mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kutil] Fix spinlock release during contention
Spinlock release uses __atomic_compare_exchange_n, which overwrites the `desired` parameter with the actual value when the compare fails. This was causing releases to always spin when there was lock contention.
This commit is contained in:
@@ -15,7 +15,7 @@ public:
|
||||
/// A node in the wait queue.
|
||||
struct waiter
|
||||
{
|
||||
bool locked;
|
||||
bool blocked;
|
||||
waiter *next;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ void
|
||||
spinlock::acquire(waiter *w)
|
||||
{
|
||||
w->next = nullptr;
|
||||
w->locked = true;
|
||||
w->blocked = true;
|
||||
|
||||
// Point the lock at this waiter
|
||||
waiter *prev = __atomic_exchange_n(&m_lock, w, memorder);
|
||||
@@ -19,30 +19,27 @@ spinlock::acquire(waiter *w)
|
||||
// If there was a previous waiter, wait for them to
|
||||
// unblock us
|
||||
prev->next = w;
|
||||
while (w->locked) {
|
||||
while (w->blocked)
|
||||
asm ("pause");
|
||||
}
|
||||
} else {
|
||||
w->locked = false;
|
||||
w->blocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
spinlock::release(waiter *w)
|
||||
{
|
||||
if (!w->next) {
|
||||
// If we're still the last waiter, we're done
|
||||
if(__atomic_compare_exchange_n(&m_lock, &w, nullptr, false, memorder, memorder))
|
||||
return;
|
||||
}
|
||||
// If we're still the last waiter, we're done
|
||||
waiter *expected = w;
|
||||
if(__atomic_compare_exchange_n(&m_lock, &expected, nullptr, false, memorder, memorder))
|
||||
return;
|
||||
|
||||
// Wait for the subseqent waiter to tell us who they are
|
||||
while (!w->next) {
|
||||
while (!w->next)
|
||||
asm ("pause");
|
||||
}
|
||||
|
||||
// Unblock the subseqent waiter
|
||||
w->next->locked = false;
|
||||
w->next->blocked = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user