[kutil] Update spinlock to an MCS-style lock

Update the existing but unused spinlock class to an MCS-style queue
spinlock. This is probably still a WIP but I expect it to see more use
with SMP getting further integrated.
This commit is contained in:
Justin C. Miller
2021-02-07 23:45:34 -08:00
parent 72787c0652
commit e73064a438
3 changed files with 61 additions and 11 deletions

View File

@@ -114,6 +114,7 @@ modules:
- src/libraries/kutil/logger.cpp - src/libraries/kutil/logger.cpp
- src/libraries/kutil/memory.cpp - src/libraries/kutil/memory.cpp
- src/libraries/kutil/printf.c - src/libraries/kutil/printf.c
- src/libraries/kutil/spinlock.cpp
cpu: cpu:
kind: lib kind: lib

View File

@@ -1,19 +1,20 @@
/// \file spinlock.h
/// Spinlock types and related defintions
#pragma once #pragma once
#include <atomic>
namespace kutil { namespace kutil {
namespace spinlock {
/// An MCS based spinlock node
class spinlock struct node
{ {
public: bool locked;
spinlock() : m_lock(false) {} node *next;
inline void enter() { while (!m_lock.exchange(true)); }
inline void leave() { m_lock.store(false); }
private:
std::atomic<bool> m_lock;
}; };
void aquire(node *lock, node *waiter);
void release(node *lock, node *waiter);
} // namespace spinlock
} // namespace kutil } // namespace kutil

View File

@@ -0,0 +1,48 @@
#include "kutil/spinlock.h"
namespace kutil {
namespace spinlock {
static constexpr int memorder = __ATOMIC_SEQ_CST;
void
aquire(node * &lock, node *waiter)
{
waiter->next = nullptr;
waiter->locked = true;
// Point the lock at this waiter
node *prev = __atomic_exchange_n(&lock, waiter, memorder);
if (prev) {
// If there was a previous waiter, wait for them to
// unblock us
prev->next = waiter;
while (waiter->locked) {
asm ("pause");
}
} else {
waiter->locked = false;
}
}
void
release(node * &lock, node *waiter)
{
if (!waiter->next) {
// If we're still the last waiter, we're done
if(__atomic_compare_exchange_n(&lock, &waiter, nullptr, false, memorder, memorder))
return;
}
// Wait for the subseqent waiter to tell us who they are
while (!waiter->next) {
asm ("pause");
}
// Unblock the subseqent waiter
waiter->next->locked = false;
}
} // namespace spinlock
} // namespace kutil