atomic: Spin locks now try to use the x86 PAUSE instruction for short waits. Fixes Bugzilla #4151.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
diff --git a/src/atomic/SDL_spinlock.c b/src/atomic/SDL_spinlock.c
index 1ebc718..f5e17d0 100644
--- a/src/atomic/SDL_spinlock.c
+++ b/src/atomic/SDL_spinlock.c
@@ -32,6 +32,10 @@
#include <atomic.h>
#endif
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include <xmmintrin.h>
+#endif
+
#if defined(__WATCOMC__) && defined(__386__)
SDL_COMPILE_TIME_ASSERT(locksize, 4==sizeof(SDL_SpinLock));
extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
@@ -116,12 +120,31 @@ SDL_AtomicTryLock(SDL_SpinLock *lock)
#endif
}
+/* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
+#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
+ #define PAUSE_INSTRUCTION() __asm__ __volatile__("rep nop\n")
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+ #define PAUSE_INSRUCTION() _mm_pause() /* this is actually "rep nop" and not a SIMD instruction. */
+#elif defined(__WATCOMC__) && defined(__386__)
+ extern _inline void PAUSE_INSTRUCTION(void);
+ #pragma aux PAUSE_INSTRUCTION = "rep nop"
+#else
+ #define PAUSE_INSTRUCTION()
+#endif
+
void
SDL_AtomicLock(SDL_SpinLock *lock)
{
+ int iterations = 0;
/* FIXME: Should we have an eventual timeout? */
while (!SDL_AtomicTryLock(lock)) {
- SDL_Delay(0);
+ if (iterations < 32) {
+ iterations++;
+ PAUSE_INSTRUCTION();
+ } else {
+ /* !!! FIXME: this doesn't definitely give up the current timeslice, it does different things on various platforms. */
+ SDL_Delay(0);
+ }
}
}