44 lines
1.4 KiB
C
44 lines
1.4 KiB
C
#ifndef SPINLOCK_H
|
|
#define SPINLOCK_H
|
|
|
|
#include <stdint.h>
|
|
|
|
typedef struct {
|
|
volatile uint32_t ticket;
|
|
volatile uint32_t serving;
|
|
} spinlock_t;
|
|
|
|
#define SPINLOCK_INIT { .ticket = 0, .serving = 0 }
|
|
|
|
static inline void spinlock_acquire(spinlock_t* lock) {
|
|
uint32_t my_ticket = __atomic_fetch_add(&lock->ticket, 1, __ATOMIC_RELAXED);
|
|
while (__atomic_load_n(&lock->serving, __ATOMIC_ACQUIRE) != my_ticket)
|
|
asm volatile("pause");
|
|
}
|
|
|
|
static inline void spinlock_release(spinlock_t* lock) {
|
|
__atomic_fetch_add(&lock->serving, 1, __ATOMIC_RELEASE);
|
|
}
|
|
|
|
static inline int spinlock_try_acquire(spinlock_t* lock) {
|
|
uint32_t serving = __atomic_load_n(&lock->serving, __ATOMIC_RELAXED);
|
|
uint32_t ticket = __atomic_load_n(&lock->ticket, __ATOMIC_RELAXED);
|
|
if (serving != ticket) return 0;
|
|
uint32_t expected = serving;
|
|
return __atomic_compare_exchange_n(&lock->ticket, &expected, serving + 1,
|
|
0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
|
|
}
|
|
|
|
static inline uint64_t spinlock_acquire_irqsave(spinlock_t* lock) {
|
|
uint64_t flags;
|
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
|
spinlock_acquire(lock);
|
|
return flags;
|
|
}
|
|
|
|
static inline void spinlock_release_irqrestore(spinlock_t* lock, uint64_t flags) {
|
|
spinlock_release(lock);
|
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
|
|
}
|
|
|
|
#endif |