CK_SEQUENCE(3)

✨ ck_sequence β€” sequence locks (yes bestie, the seqlock page) ✨

okay so this is ck_sequence(3), from the Concurrency Kit manual, and it covers the whole girl squad of functions: ck_sequence_init, ck_sequence_read_begin, ck_sequence_read_retry, ck_sequence_write_begin, and ck_sequence_write_end. sequence locks, periodt. πŸ’…


πŸ’– LIBRARY πŸ’–

Concurrency Kit β€” link it with libck, aka -lck. 🫢


🌸 SYNOPSIS 🌸

#include <ck_sequence.h>

ck_sequence_t seqlock = CK_SEQUENCE_INITIALIZER;

void
ck_sequence_init(ck_sequence_t *sq);

unsigned int
ck_sequence_read_begin(const ck_sequence_t *sq);

bool
ck_sequence_read_retry(const ck_sequence_t *sq, unsigned int version);

void
ck_sequence_write_begin(ck_sequence_t *sq);

void
ck_sequence_write_end(ck_sequence_t *sq);

πŸ¦‹ DESCRIPTION πŸ¦‹

So here's the vibe: you reach for ck_sequence when you've got a small amount of data, that data cannot be accessed atomically, and you need to sync it up with readers in a way that never blocks a single writer. That's the whole fantasy. ✨

And the readers? They get to run their read-side critical sections with zero atomic operations. literally nothing. no atomics. living rent free. 😌

Now, the rules of the game, and you do have to follow them:

  • A ck_sequence_t must be initialized before use. No skipping this.
  • You can initialize it two ways: the static initializer CK_SEQUENCE_INITIALIZER, or the function ck_sequence_init(). Pick your fighter. πŸŽ€
  • Before a reader tries to read data that might be getting concurrently modified, it must first save the return value of ck_sequence_read_begin(). do not lose that value bestie, it's your receipt.
  • While (or after) a reader has finished copying the data associated with a ck_sequence_t, it must pass that earlier return value into ck_sequence_read_retry().
  • If ck_sequence_read_retry() returns true, then your copy of the data may be inconsistent, and the whole read process must be retried. 😭 it's giving "try again".
  • Writers, listen: you must rely on your own synchronization primitives. ck_sequence is not doing that part for you, that's on you. πŸ’€
  • Once a writer has entered its respective critical section, it must call ck_sequence_write_begin() to signal its intent to update the data protected by the ck_sequence_t.
  • Before the writer leaves that critical section, it must execute ck_sequence_write_end() to indicate that the updates have left the respective objects in a consistent state. 🫢
no cap, if you skip the begin/end pair your readers are gonna have a bad time and it's gonna be your fault.

πŸŽ€ EXAMPLE πŸŽ€

okay here's the whole thing in action, main character energy:

#include <ck_sequence.h>
#include <stdlib.h>

static struct example {
        int a;
        int b;
        int c;
} global;

static ck_sequence_t seqlock = CK_SEQUENCE_INITIALIZER;

void
reader(void)
{
        struct example copy;
        unsigned int version;

        /*
         * Attempt a read of the data structure. If the structure
         * has been modified between ck_sequence_read_begin and
         * ck_sequence_read_retry then attempt another read since
         * the data may be in an inconsistent state.
         */
        do {
                version = ck_sequence_read_begin(&seqlock);
                copy = global;
        } while (ck_sequence_read_retry(&seqlock, version));

        /*
         * The previous may also be expressed using CK_SEQUENCE_READ.
         * Generally recommend to only use ck_sequence_read_retry
         * if you would like to detect a conflicting write at some
         * higher granularity.
         */
        CK_SEQUENCE_READ(&seqlock, &version) {
                copy = global;
        }

        return;
}

void
writer(void)
{

        for (;;) {
                ck_sequence_write_begin(&seqlock);
                global.a = rand();
                global.b = global.a + global.b;
                global.c = global.b + global.c;
                ck_sequence_write_end(&seqlock);
        }

        return;
}

see how the reader just loops on ck_sequence_read_retry(&seqlock, version) until it stops being true? obsessed. and note the comment about CK_SEQUENCE_READ β€” generally you're recommended to only reach for ck_sequence_read_retry if you want to detect a conflicting write at some higher granularity. iykyk. 🌸


πŸ‘‘ SEE ALSO πŸ‘‘

and if you want even more, additional information is available at http://concurrencykit.org/ πŸ’œ


k that's the whole page, bestie. this one was dated July 26, 2013, it lives in section 3 as ck_sequence(3), and it's part of the Concurrency Kit docs. go forth and seqlock responsibly. πŸ’…βœ¨πŸ¦‹

πŸ“¦ where this page comes from