Nucleus
Barry Preemptive and lockless scheduler a1eaf26 (3 years, 1 month ago)
/*
* This file implements spinlocks. It makes heavy use of GCC's atomic built-ins
* for syncronisation. The spinlocks have some simple mechanisms for preventing
* deadlocks. Each spinlock knowns which CPU/task is holding it, and can allow
* that CPU/task to acquire it multiple times and safely release it.
*/
#include <nucleus/kernel.h>
#include <nucleus/object.h>
#include <nucleus/task.h>
/* Check if already holding */
static inline int
holding(Spinlock *lock)
{
return (lock->locked && lock->owner == current);
}
/* Initialise a lock */
void
init_lock(Spinlock *lock)
{
lock->locked = 0;
lock->usage = 0;
}
/* Acquire a lock */
void
acquire(Spinlock *lock)
{
if (!holding(lock)) {
while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE))
asm volatile("pause");
lock->owner = current;
}
__atomic_add_fetch(&lock->usage, 1, __ATOMIC_RELAXED);
}
/* Release a lock */
void
release(Spinlock *lock)
{
ASSERT(holding(lock));
if (!__atomic_sub_fetch(&lock->usage, 1, __ATOMIC_RELAXED)) {
__atomic_clear(&lock->locked, __ATOMIC_RELEASE);
lock->owner = NULL;
}
}