libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_lwlock.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * @stability stable
4 * Use of this source code is governed by the ISC License,
5 * a copy of which is in the file LICENSE in the top-level directory
6 * of this distribution.
7 *
8 * src/inc/xtc_lwlock.h
9 * Lightweight lock -- multi-reader / single-writer lock with an
10 * atomic state word and an explicit wait queue. Ported from
11 * postgres/lrlck/src/backend/storage/lmgr/lwlock.c, retaining the
12 * state-encoding scheme verbatim:
13 *
14 * bit 31: LW_FLAG_HAS_WAITERS -- wakeups are pending
15 * bit 30: LW_FLAG_WAKE_IN_PROGRESS -- another thread is waking
16 * bit 29: LW_FLAG_QUEUE_LOCKED -- wait-queue spinlock
17 * bits 0..N-1: shared-holder count
18 * bit N: LW_VAL_EXCLUSIVE -- exclusive owner present
19 *
20 * N is configurable via XTC_LWLOCK_MAX_BACKENDS (default 4096).
21 *
22 * Trade-offs vs xtc_rwlock:
23 * + Single-CAS fast path for both acquire and release.
24 * + No fairness guarantees beyond FIFO wakeup order.
25 * + Cache-line-aligned struct (avoid false sharing).
26 * - Caller must release in reverse order of acquire.
27 * - No timeout/cancellable acquire (use xtc_amutex/xtc_rwlock for
28 * those scenarios).
29 *
30 * Trade-offs vs xtc_lrlock:
31 * + Multiple writers (serialized).
32 * + No second copy of the data.
33 * + Reads aren't wait-free \u2014 they CAS the state word.
34 * - Bigger contention surface; pick lrlock for read-mostly.
35 */
36
37#ifndef XTC_LWLOCK_H
38#define XTC_LWLOCK_H
39
40#include "xtc_export.h"
41
42#include <pthread.h>
43#include <stdatomic.h>
44#include <stdint.h>
45
46#include "xtc.h"
47
48#ifndef XTC_LWLOCK_MAX_BACKENDS
49#define XTC_LWLOCK_MAX_BACKENDS 4096 /* power of 2; bits 0..11 = shared count */
50#endif
51
52typedef enum xtc_lwlock_mode {
53 XTC_LW_EXCLUSIVE = 0,
54 XTC_LW_SHARED = 1
55} xtc_lwlock_mode_t;
56
57/* The lock itself. Caller embeds in any structure or stack-allocates. */
58typedef struct xtc_lwlock {
59 _Atomic uint32_t state;
60 pthread_mutex_t wait_mu;
61 pthread_cond_t wait_cv;
62 int n_waiters; /* protected by wait_mu */
63 int wakers_pending;/* protected by wait_mu */
64 uint16_t tranche; /* user-tag; for diagnostics only */
65 uint8_t initialised;
66 uint8_t pad_;
67 /* DST fiber-park wait queue (protected by wait_mu). Only touched
68 * when a caller acquires from inside a fiber under the sim
69 * scheduler; the production OS-thread path uses wait_cv and never
70 * reads/writes these. Opaque here -- struct fiber_waiter lives in
71 * lock_lw.c. */
72 void *wq_head;
73 void *wq_tail;
75
76/*
77 * PUBLIC: int xtc_lwlock_init __P((xtc_lwlock_t *, uint16_t));
78 * PUBLIC: void xtc_lwlock_destroy __P((xtc_lwlock_t *));
79 *
80 * PUBLIC: int xtc_lwlock_acquire __P((xtc_lwlock_t *, xtc_lwlock_mode_t));
81 * PUBLIC: int xtc_lwlock_acquire_cond __P((xtc_lwlock_t *, xtc_lwlock_mode_t));
82 * PUBLIC: void xtc_lwlock_release __P((xtc_lwlock_t *));
83 *
84 * PUBLIC: int xtc_lwlock_held_by_me __P((const xtc_lwlock_t *));
85 * PUBLIC: int xtc_lwlock_held_by_me_in_mode __P((const xtc_lwlock_t *, xtc_lwlock_mode_t));
86 *
87 * PUBLIC: void xtc_lwlock_track_enable __P((int));
88 * PUBLIC: long xtc_lwlock_track_violations __P((void));
89 * PUBLIC: void xtc_lwlock_track_reset __P((void));
90 * PUBLIC: void xtc_lwlock_track_set_handler __P((xtc_lwlock_track_fn, void *));
91 */
92
93XTC_API int xtc_lwlock_init(xtc_lwlock_t *lock, uint16_t tranche);
94XTC_API void xtc_lwlock_destroy(xtc_lwlock_t *lock);
95
96/* Acquire blocks until the lock is held in `mode`. Returns XTC_OK. */
97XTC_API int xtc_lwlock_acquire(xtc_lwlock_t *lock, xtc_lwlock_mode_t mode);
98
99/* Conditional acquire -- non-blocking. Returns XTC_OK on success,
100 * XTC_E_AGAIN if the lock would block. */
101XTC_API int xtc_lwlock_acquire_cond(xtc_lwlock_t *lock, xtc_lwlock_mode_t mode);
102
103/* Release the lock. Mode is recorded in the per-thread held-list,
104 * so the caller doesn't have to pass it back. */
105XTC_API void xtc_lwlock_release(xtc_lwlock_t *lock);
106
107/* Diagnostics. */
108XTC_API int xtc_lwlock_held_by_me(const xtc_lwlock_t *lock);
109XTC_API int xtc_lwlock_held_by_me_in_mode(const xtc_lwlock_t *lock,
110 xtc_lwlock_mode_t mode);
111
112/*
113 * Optional lock-order (WITNESS) tracker. Off by default; enable in
114 * test/staging to catch lock-order inversions among lwlocks (the
115 * deadlock precursor the lwlock primitive cannot detect on its own,
116 * unlike xtc_lockmgr). Edges are keyed by tranche (the lock class):
117 * the handler, if set, fires with (held_tranche, acquired_tranche)
118 * each time an acquisition reverses a previously-seen order.
119 * xtc_lwlock_track_violations counts those; xtc_lwlock_track_reset
120 * clears the edge set + counter.
121 */
122typedef void (*xtc_lwlock_track_fn)(uint16_t held_tranche,
123 uint16_t acquired_tranche, void *user);
124XTC_API void xtc_lwlock_track_enable(int on);
125XTC_API long xtc_lwlock_track_violations(void);
126XTC_API void xtc_lwlock_track_reset(void);
127XTC_API void xtc_lwlock_track_set_handler(xtc_lwlock_track_fn fn, void *user);
128
129#endif /* XTC_LWLOCK_H */