diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index b46668a409..202af8f01b 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -20,6 +20,7 @@ target_sources(common PRIVATE
FastJmp.cpp
IniInterface.cpp
Mutex.cpp
+ Misc.cpp
PathUtils.cpp
PrecompiledHeader.cpp
Perf.cpp
diff --git a/common/General.h b/common/General.h
index 3329c0e320..afc9849781 100644
--- a/common/General.h
+++ b/common/General.h
@@ -217,6 +217,11 @@ extern void InitCPUTicks();
extern u64 GetTickFrequency();
extern u64 GetCPUTicks();
extern u64 GetPhysicalMemory();
+/// Spin for a short period of time (call while spinning waiting for a lock)
+/// Returns the approximate number of ns that passed
+extern u32 ShortSpin();
+/// Number of ns to spin for before sleeping a thread
+extern const u32 SPIN_TIME_NS;
extern wxString GetOSVersionString();
diff --git a/common/Misc.cpp b/common/Misc.cpp
new file mode 100644
index 0000000000..b2d9af41d1
--- /dev/null
+++ b/common/Misc.cpp
@@ -0,0 +1,73 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2021 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "General.h"
+
+static u32 PAUSE_TIME = 0;
+
+static void MultiPause()
+{
+ _mm_pause();
+ _mm_pause();
+ _mm_pause();
+ _mm_pause();
+ _mm_pause();
+ _mm_pause();
+ _mm_pause();
+ _mm_pause();
+}
+
+__noinline static void UpdatePauseTime()
+{
+ u64 start = GetCPUTicks();
+ for (int i = 0; i < 64; i++)
+ {
+ MultiPause();
+ }
+ u64 time = GetCPUTicks() - start;
+ u64 nanos = (time * 1000000000) / GetTickFrequency();
+ PAUSE_TIME = (nanos / 64) + 1;
+}
+
+u32 ShortSpin()
+{
+ u32 inc = PAUSE_TIME;
+ if (unlikely(inc == 0))
+ {
+ UpdatePauseTime();
+ inc = PAUSE_TIME;
+ }
+
+ u32 time = 0;
+ // Sleep for approximately 500ns
+ for (; time < 500; time += inc)
+ MultiPause();
+
+ return time;
+}
+
+static u32 GetSpinTime()
+{
+ if (char* req = getenv("WAIT_SPIN_MICROSECONDS"))
+ {
+ return 1000 * atoi(req);
+ }
+ else
+ {
+ return 50 * 1000; // 50µs
+ }
+}
+
+const u32 SPIN_TIME_NS = GetSpinTime();
diff --git a/common/common.vcxproj b/common/common.vcxproj
index d1afc0d505..a9eb69c152 100644
--- a/common/common.vcxproj
+++ b/common/common.vcxproj
@@ -67,6 +67,7 @@
+
diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters
index 79099f3206..81fc51b00a 100644
--- a/common/common.vcxproj.filters
+++ b/common/common.vcxproj.filters
@@ -55,6 +55,9 @@
Source Files
+
+ Source Files
+
Source Files