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