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 <pthread.h>
40#include <stdatomic.h>
41#include <stdint.h>
42
43#include "xtc.h"
44
45#ifndef XTC_LWLOCK_MAX_BACKENDS
46#define XTC_LWLOCK_MAX_BACKENDS 4096 /* power of 2; bits 0..11 = shared count */
47#endif
48
49typedef enum xtc_lwlock_mode {
50 XTC_LW_EXCLUSIVE = 0,
51 XTC_LW_SHARED = 1
52} xtc_lwlock_mode_t;
53
54/* The lock itself. Caller embeds in any structure or stack-allocates. */
55typedef struct xtc_lwlock {
56 _Atomic uint32_t state;
57 pthread_mutex_t wait_mu;
58 pthread_cond_t wait_cv;
59 int n_waiters; /* protected by wait_mu */
60 int wakers_pending;/* protected by wait_mu */
61 uint16_t tranche; /* user-tag; for diagnostics only */
62 uint8_t initialised;
63 uint8_t pad_;
64 /* DST fiber-park wait queue (protected by wait_mu). Only touched
65 * when a caller acquires from inside a fiber under the sim
66 * scheduler; the production OS-thread path uses wait_cv and never
67 * reads/writes these. Opaque here -- struct fiber_waiter lives in
68 * lock_lw.c. */
69 void *wq_head;
70 void *wq_tail;
72
73/*
74 * PUBLIC: int xtc_lwlock_init __P((xtc_lwlock_t *, uint16_t));
75 * PUBLIC: void xtc_lwlock_destroy __P((xtc_lwlock_t *));
76 *
77 * PUBLIC: int xtc_lwlock_acquire __P((xtc_lwlock_t *, xtc_lwlock_mode_t));
78 * PUBLIC: int xtc_lwlock_acquire_cond __P((xtc_lwlock_t *, xtc_lwlock_mode_t));
79 * PUBLIC: void xtc_lwlock_release __P((xtc_lwlock_t *));
80 *
81 * PUBLIC: int xtc_lwlock_held_by_me __P((const xtc_lwlock_t *));
82 * PUBLIC: int xtc_lwlock_held_by_me_in_mode __P((const xtc_lwlock_t *, xtc_lwlock_mode_t));
83 *
84 * PUBLIC: void xtc_lwlock_track_enable __P((int));
85 * PUBLIC: long xtc_lwlock_track_violations __P((void));
86 * PUBLIC: void xtc_lwlock_track_reset __P((void));
87 * PUBLIC: void xtc_lwlock_track_set_handler __P((xtc_lwlock_track_fn, void *));
88 */
89
90int xtc_lwlock_init(xtc_lwlock_t *lock, uint16_t tranche);
91void xtc_lwlock_destroy(xtc_lwlock_t *lock);
92
93/* Acquire blocks until the lock is held in `mode`. Returns XTC_OK. */
94int xtc_lwlock_acquire(xtc_lwlock_t *lock, xtc_lwlock_mode_t mode);
95
96/* Conditional acquire -- non-blocking. Returns XTC_OK on success,
97 * XTC_E_AGAIN if the lock would block. */
98int xtc_lwlock_acquire_cond(xtc_lwlock_t *lock, xtc_lwlock_mode_t mode);
99
100/* Release the lock. Mode is recorded in the per-thread held-list,
101 * so the caller doesn't have to pass it back. */
102void xtc_lwlock_release(xtc_lwlock_t *lock);
103
104/* Diagnostics. */
105int xtc_lwlock_held_by_me(const xtc_lwlock_t *lock);
106int xtc_lwlock_held_by_me_in_mode(const xtc_lwlock_t *lock,
107 xtc_lwlock_mode_t mode);
108
109/*
110 * Optional lock-order (WITNESS) tracker. Off by default; enable in
111 * test/staging to catch lock-order inversions among lwlocks (the
112 * deadlock precursor the lwlock primitive cannot detect on its own,
113 * unlike xtc_lockmgr). Edges are keyed by tranche (the lock class):
114 * the handler, if set, fires with (held_tranche, acquired_tranche)
115 * each time an acquisition reverses a previously-seen order.
116 * xtc_lwlock_track_violations counts those; xtc_lwlock_track_reset
117 * clears the edge set + counter.
118 */
119typedef void (*xtc_lwlock_track_fn)(uint16_t held_tranche,
120 uint16_t acquired_tranche, void *user);
121void xtc_lwlock_track_enable(int on);
122long xtc_lwlock_track_violations(void);
123void xtc_lwlock_track_reset(void);
124void xtc_lwlock_track_set_handler(xtc_lwlock_track_fn fn, void *user);
125
126#endif /* XTC_LWLOCK_H */