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.
This commit is contained in:
Cameron Gutman 2021-01-03 12:13:40 -06:00
parent 393c8c1f16
commit 014f507c40
1 changed files with 6 additions and 1 deletions

View File

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