PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
lock.h
Go to the documentation of this file.
1#pragma once
2
3#include <kernel/cpu/cli.h>
4
5#ifndef NDEBUG
6#include <kernel/log/panic.h>
7#endif
8
10#include <stdatomic.h>
11#include <stdbool.h>
12#include <sys/defs.h>
13
14/**
15 * @brief Ticket spinlock.
16 * @defgroup kernel_sync_lock Lock
17 * @ingroup kernel_sync
18 *
19 * @{
20 */
21
22/**
23 * @brief Number of iterations before we consider a deadlock to have occurred in lock_acquire.
24 * This is only used in debug builds.
25 */
26#define LOCK_DEADLOCK_ITERATIONS 10000000
27
28/**
29 * @brief Lock canary value to detect memory corruption.
30 */
31#define LOCK_CANARY 0xDEADBEEF
32
33/**
34 * @brief A simple ticket lock implementation.
35 *
36 * This lock disables interrupts when acquired, and restores the interrupt state when released.
37 * It is not recursive, and attempting to acquire a lock that is already held by the same CPU will
38 * result in a deadlock.
39 *
40 * In debug builds, the lock contains a canary value to detect memory corruption and a deadlock detection
41 * mechanism that will panic if a deadlock is detected.
42 */
43typedef struct
44{
45 atomic_uint16_t nextTicket;
46 atomic_uint16_t nowServing;
47#ifndef NDEBUG
50#endif
51} lock_t;
52
53/**
54 * @brief Acquires a lock for the reminder of the current scope.
55 *
56 * @param lock Pointer to the lock to acquire.
57 */
58#define LOCK_SCOPE(lock) \
59 __attribute__((cleanup(lock_cleanup))) lock_t* CONCAT(l, __COUNTER__) = (lock); \
60 lock_acquire((lock))
61
62/**
63 * @brief Create a lock initializer.
64 * @def LOCK_CREATE
65 *
66 * @return A `lock_t` initializer.
67 */
68#ifndef NDEBUG
69#define LOCK_CREATE() {.nextTicket = ATOMIC_VAR_INIT(0), .nowServing = ATOMIC_VAR_INIT(0), .canary = LOCK_CANARY}
70#else
71#define LOCK_CREATE() {.nextTicket = ATOMIC_VAR_INIT(0), .nowServing = ATOMIC_VAR_INIT(0)}
72#endif
73
74/**
75 * @brief Initializes a lock.
76 *
77 * @param lock Pointer to the lock to initialize.
78 */
79static inline void lock_init(lock_t* lock)
80{
83#ifndef NDEBUG
85#endif
86}
87
88/**
89 * @brief Acquires a lock, blocking until it is available.
90 *
91 * This function disables interrupts on the current CPU.
92 * It is not recursive, and attempting to acquire a lock that is already held by the same CPU will result in a deadlock.
93 *
94 * @param lock Pointer to the lock to acquire.
95 */
96static inline void lock_acquire(lock_t* lock)
97{
98 cli_push();
99
100#ifndef NDEBUG
101 if (lock->canary != LOCK_CANARY)
102 {
103 cli_pop();
104 panic(NULL, "Lock canary corrupted");
105 }
106 lock->calledFrom = (uintptr_t)__builtin_return_address(0);
107 uint64_t iterations = 0;
108#endif
109
112 {
113 ASM("pause");
114
115#ifndef NDEBUG
116 if (lock->canary != LOCK_CANARY)
117 {
118 cli_pop();
119 panic(NULL, "Lock canary corrupted after %d iterations", iterations);
120 }
121 if (++iterations >= LOCK_DEADLOCK_ITERATIONS)
122 {
123 cli_pop();
124 panic(NULL, "Deadlock detected in lock last acquired from %p", (void*)lock->calledFrom);
125 }
126#endif
127 }
128
130}
131
132/**
133 * @brief Tries to acquire a lock.
134 *
135 * @param lock Pointer to the lock to acquire.
136 * @return true if the lock was acquired, false otherwise.
137 */
138static inline bool lock_try_acquire(lock_t* lock)
139{
140 cli_push();
141
144 {
145 cli_pop();
146 return false;
147 }
148
151 {
152 cli_pop();
153 return false;
154 }
155
156#ifndef NDEBUG
157 if (lock->canary != LOCK_CANARY)
158 {
159 cli_pop();
160 panic(NULL, "Lock canary corrupted");
161 }
162 lock->calledFrom = (uintptr_t)__builtin_return_address(0);
163#endif
164
165 return true;
166}
167
168/**
169 * @brief Releases a lock.
170 *
171 * This function restores the interrupt state on the current CPU to what it was before the lock was acquired.
172 *
173 * @param lock Pointer to the lock to release.
174 */
175static inline void lock_release(lock_t* lock)
176{
177#ifndef NDEBUG
178 if (lock->canary != LOCK_CANARY)
179 {
180 panic(NULL, "Lock canary corrupted");
181 }
182#endif
183
185 cli_pop();
186}
187
188static inline void lock_cleanup(lock_t** lock)
189{
191}
192
193/** @} */
static void cli_pop(void)
Decrements the CLI depth, re-enabling interrupts if depth reaches zero and interrupts were enabled pr...
Definition cli.h:42
static void cli_push(void)
Increments the CLI depth, disabling interrupts if depth was zero.
Definition cli.h:24
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:79
static bool lock_try_acquire(lock_t *lock)
Tries to acquire a lock.
Definition lock.h:138
static void lock_cleanup(lock_t **lock)
Definition lock.h:188
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:175
#define LOCK_DEADLOCK_ITERATIONS
Number of iterations before we consider a deadlock to have occurred in lock_acquire....
Definition lock.h:26
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
#define LOCK_CANARY
Lock canary value to detect memory corruption.
Definition lock.h:31
#define ASM(...)
Inline assembly macro.
Definition defs.h:160
#define NULL
Pointer error value.
Definition NULL.h:25
static lock_t lock
Definition percpu.c:31
@ memory_order_release
Definition stdatomic.h:119
@ memory_order_relaxed
Definition stdatomic.h:116
@ memory_order_acquire
Definition stdatomic.h:118
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:259
#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure)
Definition stdatomic.h:230
#define atomic_load_explicit(object, order)
Definition stdatomic.h:264
#define atomic_thread_fence(order)
Definition stdatomic.h:135
#define atomic_init(obj, value)
Definition stdatomic.h:75
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
__UINT16_TYPE__ uint16_t
Definition stdint.h:13
A simple ticket lock implementation.
Definition lock.h:44
atomic_uint16_t nextTicket
Definition lock.h:45
uintptr_t calledFrom
Definition lock.h:49
uint32_t canary
Definition lock.h:48
atomic_uint16_t nowServing
Definition lock.h:46