Skip to content

Sequence Lock

include/sync/seqlock.h View source View on GitHub

The naming here is rather… unpleasant, so it’s worth specifying upfront:

seqcount_ is for all the struct seqcount functions, seq_ and seqlock_ are for the struct seqlock functions, we keep it this way so that function signatures don’t explode in length, and to mirror the general naming conventions of the lock primitives with lock in their name (see spinlock.h, rwlock.h)

(begin_|end_)(read|write)(_raw)(_high)(_retry)

is the syntax ordering, broadly

struct seqcount (1 field) source

Sequence counters for lock free reader synchronization with serialized writers

Odd sequence indicates an in progress write, even count indicates quiescent data.

TODO

the thread API and thread.c uses what is effectively a sequence counter, just not with this API. someday we can change it over

§ atomic_uint32_t sequence
struct seqlock (2 fields) source

Sequence Lock (seqlock)

Combines a sequence counter with a spinlock to serialize writers

§ struct seqcount seqcount
§ struct spinlock lock
struct seqlock_write_guard (2 fields) source
§ struct seqlock *sl
§ enum irql irql
struct seqlock_write_raw_guard (1 field) source
§ struct seqlock *sl
typedef struct seqcount seqcount_t
source
typedef struct seqlock seqlock_t
source
void seqcount_init(struct seqcount *s)
source
uint32_t seqcount_read_raw(const struct seqcount *s)
source
uint32_t seqcount_begin_read_raw(const struct seqcount *s)
source
uint32_t seqcount_begin_read(const struct seqcount *s)
source

Wait for any active writer to complete and return the sequence with acquire barrier

bool seqcount_read_retry(const struct seqcount *s, uint32_t start)
source

Check for change since start

void seqcount_begin_write(struct seqcount *s)
source

even to odd with wmb

NOTE

the separate rlx load and store here is intentional: atomic_fetch_add is RMW, and unneeded when there is only one writer

void seqcount_end_write(struct seqcount *s)
source

odd to even with wmb

void seqlock_init_chk_full(
    struct seqlock *sl,
    const struct lock_chk_class *class,
    enum lock_chk_flags flags
)
source
uint32_t seq_begin_read(const struct seqlock *sl)
source
bool seq_read_retry(const struct seqlock *sl, uint32_t start)
source
uint32_t seq_begin_read_raw(const struct seqlock *sl)
source
uint32_t seq_read_raw(const struct seqlock *sl)
source
irql seq_write_lock(struct seqlock *sl)
source
irql seq_write_lock_high(struct seqlock *sl)
source

Writer APIs

void seq_write_unlock(struct seqlock *sl, enum irql old)
source
void seq_write_lock_raw(struct seqlock *sl)
source

Raw, no IRQL

void seq_write_unlock_raw(struct seqlock *sl)
source
cc_warn_unused_result bool seq_try_write_lock(
    struct seqlock *sl,
    enum irql *out
)
source

Trylock

cc_warn_unused_result bool seq_try_write_lock_high(
    struct seqlock *sl,
    enum irql *out
)
source
cc_warn_unused_result bool seq_try_write_lock_raw(struct seqlock *sl)
source
bool seqlock_is_writing(const struct seqlock *sl)
source

Query

bool seqlock_held(const struct seqlock *sl)
source
void seqcount_write_guard_exit(struct seqcount **s)
source
struct seqcount * seqcount_write_guard_enter(struct seqcount *s)
source
void seqlock_write_guard_assume(struct seqlock *sl)
source
void seqlock_write_guard_exit(struct seqlock_write_guard *g)
source
struct seqlock_write_guard seqlock_write_guard_enter(struct seqlock *sl)
source
struct seqlock_write_guard seqlock_write_high_guard_enter(struct seqlock *sl)
source
void seqlock_write_raw_guard_exit(struct seqlock_write_raw_guard *g)
source
struct seqlock_write_raw_guard seqlock_write_raw_guard_enter(struct seqlock *sl)
source
#define SEQCOUNT_INIT
source
Expansion
#define SEQCOUNT_INIT \
    (struct seqcount) { \
        .sequence = ATOMIC_VAR_INIT(0) \
    }
#define SEQLOCK_INIT_CHK(class_, flags_)
source
Expansion
#define SEQLOCK_INIT_CHK(class_, flags_) \
    (struct seqlock) { \
        .seqcount = SEQCOUNT_INIT, \
        .lock = SPINLOCK_INIT_CHK((class_), (flags_)) \
    }
#define SEQLOCK_INIT SEQLOCK_INIT_CHK(NULL, LOCK_CHKD_FULL)
source
#define SEQLOCK_DEFINE(id)
source
Expansion
#define SEQLOCK_DEFINE(id) struct seqlock id = SEQLOCK_INIT
#define SEQLOCK_DEFINE_CHK(id, class_, flags_)
source
Expansion
#define SEQLOCK_DEFINE_CHK(id, class_, flags_) \
    struct seqlock id = SEQLOCK_INIT_CHK((class_), (flags_))
#define seqlock_init_chk(sl_, class_, flags_)
source
Expansion
#define seqlock_init_chk(sl_, class_, flags_) \
    seqlock_init_chk_full((sl_), (class_), (flags_))
#define seqlock_init_auto_internal(sl_, flags_)
source
Expansion
#define seqlock_init_auto_internal(sl_, flags_) \
    do { \
        static const struct lock_chk_class __auto_class = { \
            .name = #sl_, \
            .file = __RELFILE__, \
            .line = __LINE__, \
        }; \
        seqlock_init_chk_full((sl_), &__auto_class, (flags_)); \
    } while (0)
#define seqlock_init_chk(sl_, class_, flags_)
source
Expansion
#define seqlock_init_chk(sl_, class_, flags_) \
    seqlock_init_chk_full((sl_), NULL, LOCK_UNCHKD)
#define seqlock_init_auto_internal(sl_, flags_)
source
Expansion
#define seqlock_init_auto_internal(sl_, flags_) \
    seqlock_init_chk_full((sl_), NULL, LOCK_UNCHKD)
#define seqlock_init_1(sl_)
source
Expansion
#define seqlock_init_1(sl_) seqlock_init_2(sl_, LOCK_CHKD_FULL)
#define seqlock_init_2(sl_, flags_)
source
Expansion
#define seqlock_init_2(sl_, flags_) seqlock_init_auto_internal((sl_), (flags_))
#define seqlock_init(...)
source
Expansion
#define seqlock_init(...) PP_CALL(seqlock_init, __VA_ARGS__)
#define SEQLOCK_ASSERT_HELD(sl)
source
Expansion
#define SEQLOCK_ASSERT_HELD(sl) enforce(seqlock_held(sl))
#define seqcount_write_guard(s_)
source
Expansion
#define seqcount_write_guard(s_) \
    cc_cleanup(seqcount_write_guard_exit) struct seqcount *PP_CONCAT( \
        seqc_guard_, __COUNTER__) = seqcount_write_guard_enter(s_)
#define seq_write_guard(sl_)
source
Expansion
#define seq_write_guard(sl_) \
    cc_cleanup(seqlock_write_guard_exit) struct seqlock_write_guard PP_CONCAT( \
        seq_wguard_, __COUNTER__) = \
        (seqlock_write_guard_assume(sl_), seqlock_write_guard_enter(sl_))
#define seq_write_guard_high(sl_)
source
#define seq_write_guard_raw(sl_)
source