Browse Source

Use specific acquire and release variants of InterlockedExchange on ARM

_InterlockedExchange_rel() is required for correctness on ARM because
the _ReadWriteBarrier() macro is only a compiler memory barrier, not a
hardware memory barrier. Due to ARM's relaxed memory model, this means
the '*lock = 0' write may be observed before the operations inside the
lock, causing possible corruption of data protected by the lock.

_InterlockedExchange_acq() is more efficient on ARM because it avoids an
expensive full memory barrier that _InterlockedExchange() does.
Cameron Gutman 4 years ago
parent
commit
014f507c40
1 changed files with 6 additions and 1 deletions
  1. 6 1
      src/atomic/SDL_spinlock.c

+ 6 - 1
src/atomic/SDL_spinlock.c

@@ -72,6 +72,9 @@ SDL_AtomicTryLock(SDL_SpinLock *lock)
         return SDL_FALSE;
     }
 
+#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
+    return (_InterlockedExchange_acq(lock, 1) == 0);
+
 #elif defined(_MSC_VER)
     SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long));
     return (InterlockedExchange((long*)lock, 1) == 0);
@@ -173,7 +176,9 @@ SDL_AtomicLock(SDL_SpinLock *lock)
 void
 SDL_AtomicUnlock(SDL_SpinLock *lock)
 {
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
+    _InterlockedExchange_rel(lock, 0);
+#elif defined(_MSC_VER)
     _ReadWriteBarrier();
     *lock = 0;