diff --git a/libctru/source/synchronization.c b/libctru/source/synchronization.c index d2a550c..0dca724 100644 --- a/libctru/source/synchronization.c +++ b/libctru/source/synchronization.c @@ -32,9 +32,26 @@ _begin: val = __ldrex(lock); if (val < 0) { - __clrex(); - svcArbitrateAddress(arbiter, (u32)lock, ARBITRATION_DECREMENT_AND_WAIT_IF_LESS_THAN, 0, 0); - goto _begin; // Try locking again + // Add ourselves to the list of threads blocked on this lock + if (__strex(lock, val-1)) + goto _begin; // strex failed, try to lock again + + _wait: + // Wait for a thread to wake us up + svcArbitrateAddress(arbiter, (u32)lock, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0); + + // Try to lock again + do + { + val = __ldrex(lock); + if (val < 0) + { + // Lock is still locked - keep waiting + __clrex(); + goto _wait; + } + } while (__strex(lock, -(val-1))); + return; } } while (__strex(lock, -val)); } @@ -60,14 +77,10 @@ void LightLock_Unlock(LightLock* lock) do val = -__ldrex(lock); while (__strex(lock, val)); + if (val > 1) - { // Wake up exactly one thread - do - val = __ldrex(lock); - while (__strex(lock, val >= 0 ? (val-1) : (val+1))); svcArbitrateAddress(arbiter, (u32)lock, ARBITRATION_SIGNAL, 1, 0); - } } void RecursiveLock_Init(RecursiveLock* lock)