[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:
Justin C. Miller
2021-07-11 18:11:47 -07:00
parent 746291dc67
commit 37e385e783
2 changed files with 10 additions and 13 deletions

View File

@@ -15,7 +15,7 @@ public:
/// A node in the wait queue. /// A node in the wait queue.
struct waiter struct waiter
{ {
bool locked; bool blocked;
waiter *next; waiter *next;
}; };

View File

@@ -11,7 +11,7 @@ void
spinlock::acquire(waiter *w) spinlock::acquire(waiter *w)
{ {
w->next = nullptr; w->next = nullptr;
w->locked = true; w->blocked = true;
// Point the lock at this waiter // Point the lock at this waiter
waiter *prev = __atomic_exchange_n(&m_lock, w, memorder); 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 // If there was a previous waiter, wait for them to
// unblock us // unblock us
prev->next = w; prev->next = w;
while (w->locked) { while (w->blocked)
asm ("pause"); asm ("pause");
}
} else { } else {
w->locked = false; w->blocked = false;
} }
} }
void void
spinlock::release(waiter *w) spinlock::release(waiter *w)
{ {
if (!w->next) { // If we're still the last waiter, we're done
// If we're still the last waiter, we're done waiter *expected = w;
if(__atomic_compare_exchange_n(&m_lock, &w, nullptr, false, memorder, memorder)) if(__atomic_compare_exchange_n(&m_lock, &expected, nullptr, false, memorder, memorder))
return; return;
}
// Wait for the subseqent waiter to tell us who they are // Wait for the subseqent waiter to tell us who they are
while (!w->next) { while (!w->next)
asm ("pause"); asm ("pause");
}
// Unblock the subseqent waiter // Unblock the subseqent waiter
w->next->locked = false; w->next->blocked = false;
} }