PatchworkOS  da8a090
A non-POSIX operating system.
Loading...
Searching...
No Matches
lock.h
Go to the documentation of this file.
1#pragma once
2
4
5#ifndef NDEBUG
6#include <kernel/log/panic.h>
7#endif
8
9#include <kernel/defs.h>
10#include <kernel/drivers/com.h>
11#include <stdatomic.h>
12
13/**
14 * @brief Ticket spinlock.
15 * @defgroup kernel_sync_lock Lock
16 * @ingroup kernel_sync
17 *
18 * @{
19 */
20
21/**
22 * @brief Number of iterations before we consider a deadlock to have occurred in lock_acquire.
23 * This is only used in debug builds.
24 */
25#define LOCK_DEADLOCK_ITERATIONS 10000000
26
27/**
28 * @brief Lock canary value to detect memory corruption.
29 */
30#define LOCK_CANARY 0xDEADBEEF
31
32/**
33 * @brief A simple ticket lock implementation.
34 *
35 * This lock disables interrupts when acquired, and restores the interrupt state when released.
36 * It is not recursive, and attempting to acquire a lock that is already held by the same CPU will
37 * result in a deadlock.
38 *
39 * In debug builds, the lock contains a canary value to detect memory corruption and a deadlock detection
40 * mechanism that will panic if a deadlock is detected.
41 */
42typedef struct
43{
44 atomic_uint16_t nextTicket;
45 atomic_uint16_t nowServing;
46#ifndef NDEBUG
49#endif
50} lock_t;
51
52/**
53 * @brief Acquires a lock for the reminder of the current scope.
54 *
55 * @param lock Pointer to the lock to acquire.
56 */
57#define LOCK_SCOPE(lock) \
58 __attribute__((cleanup(lock_cleanup))) lock_t* CONCAT(l, __COUNTER__) = (lock); \
59 lock_acquire((lock))
60
61/**
62 * @brief Create a lock initializer.
63 * @def LOCK_CREATE
64 *
65 * @return A `lock_t` initializer.
66 */
67#ifndef NDEBUG
68#define LOCK_CREATE() \
69 (lock_t) \
70 { \
71 .nextTicket = ATOMIC_VAR_INIT(0), .nowServing = ATOMIC_VAR_INIT(0), .canary = LOCK_CANARY \
72 }
73#else
74#define LOCK_CREATE() \
75 (lock_t) \
76 { \
77 .nextTicket = ATOMIC_VAR_INIT(0), .nowServing = ATOMIC_VAR_INIT(0) \
78 }
79#endif
80
81/**
82 * @brief Initializes a lock.
83 *
84 * @param lock Pointer to the lock to initialize.
85 */
86static inline void lock_init(lock_t* lock)
87{
90#ifndef NDEBUG
92#endif
93}
94
95/**
96 * @brief Acquires a lock, blocking until it is available.
97 *
98 * This function disables interrupts on the current CPU.
99 * It is not recursive, and attempting to acquire a lock that is already held by the same CPU will result in a deadlock.
100 *
101 * @param lock Pointer to the lock to acquire.
102 */
103static inline void lock_acquire(lock_t* lock)
104{
106
107#ifndef NDEBUG
108 if (lock->canary != LOCK_CANARY)
109 {
111 panic(NULL, "Lock canary corrupted");
112 }
113 lock->calledFrom = (uintptr_t)__builtin_return_address(0);
114 uint64_t iterations = 0;
115#endif
116
119 {
120 asm volatile("pause");
121
122#ifndef NDEBUG
123 if (lock->canary != LOCK_CANARY)
124 {
126 panic(NULL, "Lock canary corrupted after %d iterations", iterations);
127 }
128 if (++iterations >= LOCK_DEADLOCK_ITERATIONS)
129 {
131 panic(NULL, "Deadlock detected in lock last acquired from %p", (void*)lock->calledFrom);
132 }
133#endif
134 }
135
137}
138
139/**
140 * @brief Releases a lock.
141 *
142 * This function restores the interrupt state on the current CPU to what it was before the lock was acquired.
143 *
144 * @param lock Pointer to the lock to release.
145 */
146static inline void lock_release(lock_t* lock)
147{
148#ifndef NDEBUG
149 if (lock->canary != LOCK_CANARY)
150 {
151 panic(NULL, "Lock canary corrupted");
152 }
153#endif
154
157}
158
159static inline void lock_cleanup(lock_t** lock)
160{
162}
163
164/** @} */
void interrupt_disable(void)
Disable interrupts and increment the disableDepth.
Definition interrupt.c:23
void interrupt_enable(void)
Decrement the CLI depth and enable interrupts if depth reaches zero and interrupts were previously en...
Definition interrupt.c:35
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:266
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:86
static void lock_cleanup(lock_t **lock)
Definition lock.h:159
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:146
#define LOCK_DEADLOCK_ITERATIONS
Number of iterations before we consider a deadlock to have occurred in lock_acquire....
Definition lock.h:25
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:103
#define LOCK_CANARY
Lock canary value to detect memory corruption.
Definition lock.h:30
#define NULL
Pointer error value.
Definition NULL.h:23
static lock_t lock
Definition io.c:13
@ memory_order_seq_cst
Definition stdatomic.h:121
@ 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_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:43
atomic_uint16_t nextTicket
Definition lock.h:44
uintptr_t calledFrom
Definition lock.h:48
uint32_t canary
Definition lock.h:47
atomic_uint16_t nowServing
Definition lock.h:45