PatchworkOS  19e446b
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() \
41 { \
42 .sequence = ATOMIC_VAR_INIT(0), \
43 .writeLock = LOCK_CREATE(), \
44 }
45
46/**
47 * @brief Initializes a sequence lock.
48 *
49 * @param seqlock Pointer to the sequence lock to initialize.
50 */
51static inline void seqlock_init(seqlock_t* seqlock)
52{
53 atomic_init(&seqlock->sequence, 0);
54 lock_init(&seqlock->writeLock);
55}
56
57/**
58 * @brief Acquires the write lock of a sequence lock.
59 *
60 * This function busy-waits until the write lock is acquired.
61 *
62 * @param seqlock Pointer to the sequence lock.
63 */
64static inline void seqlock_write_acquire(seqlock_t* seqlock)
65{
66 lock_acquire(&seqlock->writeLock);
68}
69
70/**
71 * @brief Releases the write lock of a sequence lock.
72 *
73 * @param seqlock Pointer to the sequence lock.
74 */
75static inline void seqlock_write_release(seqlock_t* seqlock)
76{
78 lock_release(&seqlock->writeLock);
79}
80
81/**
82 * @brief Begins a read operation on a sequence lock.
83 *
84 * Should be called in a loop, for example:
85 * ```c
86 * uint64_t seq;
87 * do {
88 * seq = seqlock_read_begin(&seqlock);
89 * // read data here
90 * } while (seqlock_read_retry(&seqlock, seq));
91 * ```
92 *
93 * Or use the `SEQLOCK_READ_SCOPE()` macro.
94 *
95 * @param seqlock Pointer to the sequence lock.
96 * @return The current sequence number.
97 */
98static inline uint64_t seqlock_read_begin(seqlock_t* seqlock)
99{
101}
102
103/**
104 * @brief Checks if a read operation on a sequence lock needs to be retried.
105 *
106 * @param seqlock Pointer to the sequence lock.
107 * @param seq The sequence number returned by `seqlock_read_begin()`.
108 * @return `true` if the read operation needs to be retried, `false` otherwise.
109 */
110static inline bool seqlock_read_retry(seqlock_t* seqlock, uint64_t seq)
111{
113 return (atomic_load_explicit(&seqlock->sequence, memory_order_relaxed) != seq) || (seq & 1);
114}
115
116/**
117 * @brief Read scope for a sequence lock.
118 *
119 * Example usage:
120 * ```c
121 * SEQLOCK_READ_SCOPE(&seqlock)
122 * {
123 * // read data here
124 * }
125 * ```
126 *
127 * @param seqlock Pointer to the sequence lock.
128 */
129#define SEQLOCK_READ_SCOPE(seqlock) \
130 for (uint64_t __seq = seqlock_read_begin(seqlock); \
131 seqlock_read_retry(seqlock, __seq) ? (__seq = seqlock_read_begin(seqlock), true) : false;)
132
133/** @} */
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:79
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:175
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
static uint64_t seqlock_read_begin(seqlock_t *seqlock)
Begins a read operation on a sequence lock.
Definition seqlock.h:98
static void seqlock_write_release(seqlock_t *seqlock)
Releases the write lock of a sequence lock.
Definition seqlock.h:75
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:110
static void seqlock_init(seqlock_t *seqlock)
Initializes a sequence lock.
Definition seqlock.h:51
static void seqlock_write_acquire(seqlock_t *seqlock)
Acquires the write lock of a sequence lock.
Definition seqlock.h:64
@ 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