PatchworkOS  3984a1d
A non-POSIX operating system.
Loading...
Searching...
No Matches
seqlock.h
Go to the documentation of this file.
1#pragma once
2
3#include <kernel/sync/lock.h>
4
5#include <stdatomic.h>
6
7/**
8 * @brief Sequence Lock
9 * @defgroup kernel_sync_seqlock Seqlock
10 * @ingroup kernel_sync
11 *
12 * A sequence lock is similar to a read-write lock, but optimized for scenarios where there are many more readers than
13 * writers, or where reads are frequent and writes are rare.
14 *
15 * Readers can read the data without acquiring a lock, instead they check a "sequence number" before and after their
16 * read to verify that no write occured during their read, if one did, they must retry their read.
17 *
18 * Writers acquire the lock exclusively and increment the "sequence number" before and after their write, this means
19 * that readers can detect if a write has occured, and if a write is currently in progress by checking if the sequence
20 * number is odd.
21 *
22 * @{
23 */
24
25/**
26 * @brief Sequence lock structure.
27 * @struct seqlock_t
28 */
29typedef struct
30{
31 atomic_uint64_t sequence;
33} seqlock_t;
34
35/**
36 * @brief Create a sequence lock initializer.
37 *
38 * @return A `seqlock_t` initializer.
39 */
40#define SEQLOCK_CREATE() {.sequence = ATOMIC_VAR_INIT(0), .writeLock = LOCK_CREATE()}
41
42/**
43 * @brief Initializes a sequence lock.
44 *
45 * @param seqlock Pointer to the sequence lock to initialize.
46 */
47static inline void seqlock_init(seqlock_t* seqlock)
48{
49 atomic_init(&seqlock->sequence, 0);
50 lock_init(&seqlock->writeLock);
51}
52
53/**
54 * @brief Acquires the write lock of a sequence lock.
55 *
56 * This function busy-waits until the write lock is acquired.
57 *
58 * @param seqlock Pointer to the sequence lock.
59 */
60static inline void seqlock_write_acquire(seqlock_t* seqlock)
61{
62 lock_acquire(&seqlock->writeLock);
64}
65
66/**
67 * @brief Releases the write lock of a sequence lock.
68 *
69 * @param seqlock Pointer to the sequence lock.
70 */
71static inline void seqlock_write_release(seqlock_t* seqlock)
72{
74 lock_release(&seqlock->writeLock);
75}
76
77/**
78 * @brief Begins a read operation on a sequence lock.
79 *
80 * Should be called in a loop, for example:
81 * ```c
82 * uint64_t seq;
83 * do {
84 * seq = seqlock_read_begin(&seqlock);
85 * // read data here
86 * } while (seqlock_read_retry(&seqlock, seq));
87 * ```
88 *
89 * Or use the `SEQLOCK_READ_SCOPE()` macro.
90 *
91 * @param seqlock Pointer to the sequence lock.
92 * @return The current sequence number.
93 */
94static inline uint64_t seqlock_read_begin(seqlock_t* seqlock)
95{
97}
98
99/**
100 * @brief Checks if a read operation on a sequence lock needs to be retried.
101 *
102 * @param seqlock Pointer to the sequence lock.
103 * @param seq The sequence number returned by `seqlock_read_begin()`.
104 * @return `true` if the read operation needs to be retried, `false` otherwise.
105 */
106static inline bool seqlock_read_retry(seqlock_t* seqlock, uint64_t seq)
107{
109 return (atomic_load_explicit(&seqlock->sequence, memory_order_relaxed) != seq) || (seq & 1);
110}
111
112/**
113 * @brief Read scope for a sequence lock.
114 *
115 * Example usage:
116 * ```c
117 * SEQLOCK_READ_SCOPE(&seqlock)
118 * {
119 * // read data here
120 * }
121 * ```
122 *
123 * @param seqlock Pointer to the sequence lock.
124 */
125#define SEQLOCK_READ_SCOPE(seqlock) \
126 for (uint64_t __seq = seqlock_read_begin(seqlock); \
127 seqlock_read_retry(seqlock, __seq) ? (__seq = seqlock_read_begin(seqlock), true) : false;)
128
129/** @} */
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:87
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:184
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:104
static uint64_t seqlock_read_begin(seqlock_t *seqlock)
Begins a read operation on a sequence lock.
Definition seqlock.h:94
static void seqlock_write_release(seqlock_t *seqlock)
Releases the write lock of a sequence lock.
Definition seqlock.h:71
static bool seqlock_read_retry(seqlock_t *seqlock, uint64_t seq)
Checks if a read operation on a sequence lock needs to be retried.
Definition seqlock.h:106
static void seqlock_init(seqlock_t *seqlock)
Initializes a sequence lock.
Definition seqlock.h:47
static void seqlock_write_acquire(seqlock_t *seqlock)
Acquires the write lock of a sequence lock.
Definition seqlock.h:60
@ 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
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
A simple ticket lock implementation.
Definition lock.h:44
Sequence lock structure.
Definition seqlock.h:30
lock_t writeLock
Definition seqlock.h:32
atomic_uint64_t sequence
Definition seqlock.h:31