### Build and Install Concurrency Kit Source: https://context7.com/concurrencykit/ck/llms.txt Standard commands to configure, build, test, and install the Concurrency Kit library. ```sh ./configure make make regressions make install make uninstall ``` -------------------------------- ### ck_barrier: Centralized Barrier Example Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates the use of a centralized barrier to synchronize multiple threads. All threads must reach the barrier before any can proceed. ```c #include #include #include #define N_THREADS 4 static ck_barrier_centralized_t barrier = CK_BARRIER_CENTRALIZED_INITIALIZER; static void * worker(void *arg) { ck_barrier_centralized_state_t state = CK_BARRIER_CENTRALIZED_STATE_INITIALIZER; int id = (int)(intptr_t)arg; printf("Thread %d before barrier\n", id); /* All N_THREADS threads must call this before any proceeds */ ck_barrier_centralized(&barrier, &state, N_THREADS); printf("Thread %d after barrier\n", id); return NULL; } int main(void) { pthread_t threads[N_THREADS]; for (int i = 0; i < N_THREADS; i++) pthread_create(&threads[i], NULL, worker, (void *)(intptr_t)i); for (int i = 0; i < N_THREADS; i++) pthread_join(threads[i], NULL); return 0; } ``` -------------------------------- ### ck_stack: Initialize and Use Lock-Free Stack Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates initializing a lock-free stack using `CK_STACK_INITIALIZER` and performing MPMC push/pop operations. Includes examples of `ck_stack_batch_pop_upmc` for atomically detaching the entire list and iterating with `CK_STACK_FOREACH`. Also shows `ck_stack_push_spnc` for single-producer, no-consumer scenarios. ```c #include #include struct node { int value; ck_stack_entry_t entry; }; CK_STACK_CONTAINER(struct node, entry, node_of) int main(void) { ck_stack_t stack = CK_STACK_INITIALIZER; struct node a = { .value = 1 }; struct node b = { .value = 2 }; ck_stack_init(&stack); /* Multi-producer, multi-consumer push */ ck_stack_push_upmc(&stack, &a.entry); ck_stack_push_upmc(&stack, &b.entry); /* Pop */ ck_stack_entry_t *e = ck_stack_pop_upmc(&stack); if (e) printf("popped: %d\n", node_of(e)->value); /* 2 */ /* Batch pop: detach entire list atomically */ ck_stack_entry_t *list = ck_stack_batch_pop_upmc(&stack); /* Iterate the detached list */ CK_STACK_FOREACH(&stack, e) { printf("item: %d\n", node_of(e)->value); } /* Single-producer no-consumer push (no atomics) */ ck_stack_push_spnc(&stack, &a.entry); printf("empty: %d\n", CK_STACK_ISEMPTY(&stack)); return 0; } ``` -------------------------------- ### Initialize and Use ck_ht Concurrent Hash Table Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates initializing and using ck_ht for key-value pairs in BYTESTSTRING mode, including hashing, putting, getting, setting, and removing entries. Also shows direct (integer) key mode. ```c #include #include #include #include #include static void *ht_malloc(size_t r) { return malloc(r); } static void ht_free(void *p, size_t b, bool r) { (void)b; (void)r; free(p); } static struct ck_malloc allocator = { .malloc = ht_malloc, .free = ht_free }; int main(void) { ck_ht_t ht; ck_ht_hash_t h; ck_ht_entry_t entry; /* Initialize: bytestring mode, NULL hash = built-in, capacity=16 */ ck_ht_init(&ht, CK_HT_MODE_BYTESTRING, NULL, &allocator, 16, 0xDEADBEEF); const char *key = "mykey"; const char *value = "myvalue"; /* Hash the key */ ck_ht_hash(&h, &ht, key, strlen(key)); /* Prepare and insert the entry */ ck_ht_entry_set(&entry, h, key, strlen(key), value); ck_ht_put_spmc(&ht, h, &entry); /* Lookup: populate entry with key info first */ ck_ht_entry_key_set(&entry, key, strlen(key)); if (ck_ht_get_spmc(&ht, h, &entry)) { printf("value: %s\n", (char *)ck_ht_entry_value(&entry)); /* myvalue */ } /* Overwrite existing key */ ck_ht_entry_set(&entry, h, key, strlen(key), "updated"); ck_ht_set_spmc(&ht, h, &entry); /* Remove */ ck_ht_entry_key_set(&entry, key, strlen(key)); ck_ht_remove_spmc(&ht, h, &entry); /* Direct (integer key) mode */ ck_ht_t direct_ht; ck_ht_init(&direct_ht, CK_HT_MODE_DIRECT, NULL, &allocator, 16, 42); ck_ht_hash_direct(&h, &direct_ht, 12345UL); ck_ht_entry_set_direct(&entry, h, 12345UL, 99999UL); ck_ht_put_spmc(&direct_ht, h, &entry); printf("count: %llu\n", (unsigned long long)ck_ht_count(&ht)); ck_ht_destroy(&ht); ck_ht_destroy(&direct_ht); return 0; } ``` -------------------------------- ### ck_spinlock: Basic and Advanced Spinlock Operations Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates basic spinlock usage with initialization, locking, unlocking, and try-locking. Includes examples for MCS and ticket lock variants. ```c #include #include static ck_spinlock_t lock = CK_SPINLOCK_INITIALIZER; static int counter = 0; void increment(void) { ck_spinlock_lock(&lock); counter++; ck_spinlock_unlock(&lock); } int main(void) { /* Dynamic initialization */ ck_spinlock_init(&lock); /* Try to acquire without blocking */ if (ck_spinlock_trylock(&lock)) { counter++; ck_spinlock_unlock(&lock); } /* Check if locked */ printf("locked: %d\n", ck_spinlock_locked(&lock)); /* MCS scalable lock (queue-based, NUMA-friendly) */ ck_spinlock_mcs_t mcs_lock = CK_SPINLOCK_MCS_INITIALIZER; ck_spinlock_mcs_context_t ctx; ck_spinlock_mcs_lock(&mcs_lock, &ctx); /* critical section */ ck_spinlock_mcs_unlock(&mcs_lock, &ctx); /* Ticket lock (fair, centralized) */ ck_spinlock_ticket_t ticket = CK_SPINLOCK_TICKET_INITIALIZER; ck_spinlock_ticket_lock(&ticket); ck_spinlock_ticket_unlock(&ticket); return 0; } ``` -------------------------------- ### Initialize and Use Epoch-Based Memory Reclamation (ck_epoch) Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates the basic usage of ck_epoch for safe memory reclamation. Ensure ck_epoch_init is called before registering records. Deferred free callbacks are scheduled with ck_epoch_call and executed when all threads pass a subsequent epoch. ```c #include #include #include static ck_epoch_t global_epoch; /* Object containing the epoch entry */ struct my_obj { int value; ck_epoch_entry_t epoch_entry; }; /* Reclamation callback invoked after grace period */ static void my_obj_free(ck_epoch_entry_t *e) { struct my_obj *obj = CK_EPOCH_CONTAINER(struct my_obj, epoch_entry, e); printf("Freeing obj with value %d\n", obj->value); free(obj); } int main(void) { ck_epoch_record_t record; ck_epoch_init(&global_epoch); ck_epoch_register(&global_epoch, &record, NULL); /* Allocate and later defer-free an object */ struct my_obj *obj = malloc(sizeof *obj); obj->value = 42; /* Begin an epoch-protected read section */ ck_epoch_begin(&record, NULL); printf("reading value: %d\n", obj->value); ck_epoch_end(&record, NULL); /* Schedule deferred reclamation */ ck_epoch_call(&record, &obj->epoch_entry, my_obj_free); /* Block until all deferred callbacks have completed */ ck_epoch_barrier(&record); /* obj is freed here */ /* Non-blocking poll: returns true if callbacks were dispatched */ ck_epoch_poll(&record); /* Synchronize without blocking (waits for epoch to advance) */ ck_epoch_synchronize(&record); /* Recycle or unregister the record */ ck_epoch_unregister(&record); return 0; } ``` -------------------------------- ### Atomic Operations with ck_pr Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates atomic load, store, compare-and-swap, fetch-and-add, fetch-and-store, bitwise operations, and memory fences using ck_pr. Requires linking with -lck. ```c #include #include int main(void) { /* Atomic load and store */ int value = 0; ck_pr_store_int(&value, 42); printf("value = %d\n", ck_pr_load_int(&value)); /* 42 */ /* Compare-and-swap: returns true on success */ int old = 42; bool swapped = ck_pr_cas_int(&value, 42, 100); printf("swapped=%d value=%d\n", swapped, ck_pr_load_int(&value)); /* 1, 100 */ /* CAS with old-value output */ ck_pr_cas_int_value(&value, 100, 200, &old); printf("old=%d value=%d\n", old, ck_pr_load_int(&value)); /* 100, 200 */ /* Fetch-and-add: returns previous value */ int prev = ck_pr_faa_int(&value, 5); printf("prev=%d value=%d\n", prev, ck_pr_load_int(&value)); /* 200, 205 */ /* Fetch-and-store: atomically exchange */ prev = ck_pr_fas_int(&value, 0); printf("prev=%d value=%d\n", prev, ck_pr_load_int(&value)); /* 205, 0 */ /* Atomic increment / decrement */ ck_pr_inc_int(&value); ck_pr_dec_int(&value); /* Atomic bitwise operations */ ck_pr_or_uint((unsigned int *)&value, 0x0F); ck_pr_and_uint((unsigned int *)&value, 0xFF); ck_pr_xor_uint((unsigned int *)&value, 0x01); /* Memory fences */ ck_pr_fence_load(); /* load-load barrier */ ck_pr_fence_store(); /* store-store barrier */ ck_pr_fence_memory(); /* full barrier */ ck_pr_fence_acquire(); /* acquire semantics */ ck_pr_fence_release(); /* release semantics */ /* Pointer operations */ void *ptr = NULL; ck_pr_store_ptr(&ptr, (void *)0xDEADBEEF); printf("ptr=%p\n", ck_pr_load_ptr(&ptr)); return 0; } /* Build: cc -o example example.c -lck */ ``` -------------------------------- ### Initialize and Use ck_hs Concurrent Hash Set Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates the initialization and basic operations of ck_hs in SPMC and OBJECT modes. Requires custom memory allocation functions. ```c #include #include #include #include #include static void *hs_malloc(size_t r) { return malloc(r); } static void hs_free(void *p, size_t b, bool r) { (void)b; (void)r; free(p); } static struct ck_malloc allocator = { .malloc = hs_malloc, .free = hs_free }; static unsigned long hash_string(const void *key, unsigned long seed) { const char *s = key; unsigned long h = seed; while (*s) h = h * 31 + (unsigned char)*s++; return h; } static bool compare_string(const void *a, const void *b) { return strcmp(a, b) == 0; } int main(void) { ck_hs_t hs; unsigned long seed = 0xDEADBEEF; /* Initialize: SPMC + OBJECT mode, initial capacity 16 */ ck_hs_init(&hs, CK_HS_MODE_SPMC | CK_HS_MODE_OBJECT, hash_string, compare_string, &allocator, 16, seed); const char *key = "hello"; unsigned long h = CK_HS_HASH(&hs, hash_string, key); /* Insert (put returns false if already present) */ ck_hs_put(&hs, h, key); /* Lookup (safe from concurrent readers) */ void *found = ck_hs_get(&hs, h, key); printf("found: %s\n", found ? (char *)found : "NULL"); /* hello */ /* Unconditional set (replaces existing) */ void *old = NULL; ck_hs_set(&hs, h, "hello", &old); /* Remove */ void *removed = ck_hs_remove(&hs, h, key); printf("removed: %s\n", removed ? (char *)removed : "NULL"); /* Iteration (writer-side only) */ ck_hs_iterator_t it = CK_HS_ITERATOR_INITIALIZER; void *item; while (ck_hs_next(&hs, &it, &item)) printf("entry: %s\n", (char *)item); printf("count: %lu\n", ck_hs_count(&hs)); /* Garbage collect tombstones */ ck_hs_gc(&hs, 0, 0); ck_hs_deinit(&hs); return 0; } ``` -------------------------------- ### Initialize and Use Hazard Pointers (ck_hp) Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates the basic usage of ck_hp for hazard pointer-based memory reclamation. The degree of hazard pointers and a threshold for reclamation are set during initialization. Pointers are protected using ck_hp_set_fence before dereferencing and retired using ck_hp_retire. ```c #include #include #include static ck_hp_t hp_global; static void my_destroy(void *ptr) { printf("destroying %p\n", ptr); free(ptr); } int main(void) { ck_hp_record_t record; ck_hp_hazard_t hazard; void *hazard_ptrs[2]; /* degree = 2 */ int *shared = malloc(sizeof(int)); *shared = 99; /* Initialize: degree=2 hazard pointers, threshold=16 */ ck_hp_init(&hp_global, 2, 16, my_destroy); ck_hp_register(&hp_global, &record, hazard_ptrs); /* Protect a pointer before dereferencing */ ck_hp_set_fence(&record, 0, shared); /* slot 0 = shared */ printf("value: %d\n", *shared); /* After use, schedule the object for deferred destruction */ ck_hp_retire(&record, &hazard, shared, shared); /* Clear all hazard pointer slots */ ck_hp_clear(&record); /* Force reclamation of all pending retired objects */ ck_hp_purge(&record); ck_hp_unregister(&record); return 0; } ``` -------------------------------- ### ck_ring: Initialize and Use Type-Safe Ring Buffer Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates initializing a type-safe ring buffer for integers using CK_RING_PROTOTYPE and performing SPSC enqueue/dequeue operations. The ring size must be a power of two. ```c #include #include #define RING_SIZE 64 /* must be power of two */ /* Type-safe ring for int values */ struct my_item { int value; }; CK_RING_PROTOTYPE(my_item, my_item) static ck_ring_t ring; static struct my_item buf[RING_SIZE]; int main(void) { ck_ring_init(&ring, RING_SIZE); /* SPSC: enqueue (returns false if full) */ struct my_item item = { .value = 42 }; bool ok = CK_RING_ENQUEUE_SPSC(my_item, &ring, buf, &item); printf("enqueue ok=%d size=%u\n", ok, ck_ring_size(&ring)); /* SPSC: dequeue */ struct my_item out; ok = CK_RING_DEQUEUE_SPSC(my_item, &ring, buf, &out); printf("dequeue ok=%d value=%d\n", ok, out.value); /* 42 */ /* MPMC: enqueue with size feedback */ unsigned int sz; CK_RING_ENQUEUE_MPMC(my_item, &ring, buf, &item); /* Reserve-then-commit pattern (zero-copy) for SPSC */ struct my_item *slot = ck_ring_enqueue_reserve_spsc(&ring, (struct ck_ring_buffer *)buf); if (slot != NULL) { slot->value = 99; ck_ring_enqueue_commit_spsc(&ring); } /* Pointer ring (generic API) */ ck_ring_t pring; ck_ring_buffer_t pbuf[32]; ck_ring_init(&pring, 32); void *p = (void *)0xABCD; ck_ring_enqueue_spsc(&pring, pbuf, p); void *got; ck_ring_dequeue_spsc(&pring, pbuf, &got); printf("ptr: %p\n", got); printf("capacity: %u\n", ck_ring_capacity(&ring)); return 0; } ``` -------------------------------- ### ck_bitmap: Concurrent Bitmap Operations Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates stack-allocated bitmap initialization, setting, testing, resetting, counting, and iterating bits. Supports union and clearing operations. ```c #include #include #define N_BITS 128 int main(void) { /* Stack-allocated bitmap instance */ CK_BITMAP_INSTANCE(N_BITS) bm; ck_bitmap_iterator_t it; unsigned int bit; /* Initialize all bits to 0 */ CK_BITMAP_INIT(&bm, N_BITS, false); /* Set individual bits (atomic OR) */ CK_BITMAP_SET(&bm, 0); CK_BITMAP_SET(&bm, 7); CK_BITMAP_SET(&bm, 63); CK_BITMAP_SET(&bm, 127); /* Test a bit */ printf("bit 7 set: %d\n", CK_BITMAP_TEST(&bm, 7)); /* 1 */ printf("bit 5 set: %d\n", CK_BITMAP_TEST(&bm, 5)); /* 0 */ /* Test-and-set: returns true if bit was already set */ bool was_set = CK_BITMAP_BTS(&bm, 7); printf("7 was already set: %d\n", was_set); /* 1 */ /* Reset a bit (atomic AND with complement) */ CK_BITMAP_RESET(&bm, 7); /* Count set bits up to limit */ printf("popcount(64): %u\n", CK_BITMAP_COUNT(&bm, 64)); /* Iterate set bits */ CK_BITMAP_ITERATOR_INIT(&it, &bm); while (CK_BITMAP_NEXT(&bm, &it, &bit)) printf("set bit: %u\n", bit); /* Union two bitmaps (atomic OR per word) */ CK_BITMAP_INSTANCE(N_BITS) bm2; CK_BITMAP_INIT(&bm2, N_BITS, false); CK_BITMAP_SET(&bm2, 3); CK_BITMAP_UNION(&bm, &bm2); /* Clear all bits */ CK_BITMAP_CLEAR(&bm); printf("empty: %d\n", CK_BITMAP_EMPTY(&bm, N_BITS)); /* 1 */ return 0; } ``` -------------------------------- ### ck_sequence: Sequence Lock for Efficient Reads Source: https://context7.com/concurrencykit/ck/llms.txt Demonstrates the use of a sequence lock (seqlock) for fast read paths. Shows writer operations to begin and end writes, and reader operations to capture and verify data. ```c #include #include static ck_sequence_t seq = CK_SEQUENCE_INITIALIZER; static int x = 0, y = 0; /* Writer (must hold external mutex or be single-writer) */ void write_point(int nx, int ny) { ck_sequence_write_begin(&seq); /* seq goes odd */ x = nx; y = ny; ck_sequence_write_end(&seq); /* seq goes even */ } /* Reader: retry if write was in progress */ void read_point(int *rx, int *ry) { unsigned int version; /* CK_SEQUENCE_READ retries until a consistent snapshot is captured */ CK_SEQUENCE_READ(&seq, &version) { *rx = x; *ry = y; } } int main(void) { int rx, ry; ck_sequence_init(&seq); write_point(3, 4); read_point(&rx, &ry); printf("point: (%d, %d)\n", rx, ry); /* (3, 4) */ /* Manual API */ unsigned int v = ck_sequence_read_begin(&seq); rx = x; ry = y; if (ck_sequence_read_retry(&seq, v)) { /* re-read needed */ } return 0; } ``` -------------------------------- ### ck_ec: Linux Futex-based Event Counters Source: https://context7.com/concurrencykit/ck/llms.txt Implements 32-bit event counters using Linux futex primitives for efficient producer-consumer synchronization. Requires platform-specific operations for time, wait, and wake. ```c #include #include #include #include #include #include #include /* Platform ops: Linux futex-based */ static int ec_gettime(const struct ck_ec_ops *ops, struct timespec *out) { (void)ops; return clock_gettime(CLOCK_MONOTONIC, out); } static void ec_wait32(const struct ck_ec_wait_state *ws, const uint32_t *addr, uint32_t expected, const struct timespec *deadline) { (void)ws; syscall(SYS_futex, addr, FUTEX_WAIT_PRIVATE, expected, deadline, NULL, 0); } static void ec_wake32(const struct ck_ec_ops *ops, const uint32_t *addr) { (void)ops; syscall(SYS_futex, (void *)addr, FUTEX_WAKE_PRIVATE, INT_MAX, NULL, NULL, 0); } static const struct ck_ec_ops my_ops = { .gettime = ec_gettime, .wait32 = ec_wait32, .wake32 = ec_wake32, }; static const struct ck_ec_mode sp_mode = { .ops = &my_ops, .single_producer = true }; static const struct ck_ec_mode mp_mode = { .ops = &my_ops, .single_producer = false }; int main(void) { struct ck_ec32 ec = CK_EC_INITIALIZER; ck_ec32_init(&ec, 0); printf("initial value: %u\n", ck_ec32_value(&ec)); /* 0 */ /* Producer increments the counter */ ck_ec32_inc(&ec, &sp_mode); printf("after inc: %u\n", ck_ec32_value(&ec)); /* 1 */ uint32_t prev = ck_ec32_add(&ec, &sp_mode, 5); printf("prev=%u now=%u\n", prev, ck_ec32_value(&ec)); /* 1, 6 */ /* Consumer: wait until counter differs from 6, with 100ms deadline */ struct timespec deadline; ck_ec_deadline(&deadline, &mp_mode, &(struct timespec){ .tv_sec = 0, .tv_nsec = 100000000 }); uint32_t snap = ck_ec32_value(&ec); int r = ck_ec32_wait(&ec, &mp_mode, snap, &deadline); printf("wait result: %d (0=changed, -1=timeout)\n", r); /* -1 (timeout) */ return 0; } ``` -------------------------------- ### ck_spinlock — Spinlock Variants Source: https://context7.com/concurrencykit/ck/llms.txt Provides a generic `ck_spinlock_t` API that bundles various spinlock implementations, exposing the fastest variant for the current platform. Supports initialization, locking, try-locking, checking lock status, and specialized variants like MCS and ticket locks. ```APIDOC ## ck_spinlock — Spinlock Variants `ck_spinlock.h` bundles all spinlock implementations and exposes the fastest variant for the current platform (FAS-based) under the generic `ck_spinlock_t` API. Variants include Anderson, CAS, CLH, DEC, FAS, HCLH, MCS, and ticket locks. ### Initialization - `CK_SPINLOCK_INITIALIZER`: Static initializer for `ck_spinlock_t`. - `ck_spinlock_init(ck_spinlock_t *lock)`: Dynamically initializes a spinlock. ### Locking Operations - `ck_spinlock_lock(ck_spinlock_t *lock)`: Acquires the spinlock, blocking if necessary. - `ck_spinlock_unlock(ck_spinlock_t *lock)`: Releases the spinlock. - `ck_spinlock_trylock(ck_spinlock_t *lock)`: Attempts to acquire the spinlock without blocking. Returns true if acquired, false otherwise. ### Status Check - `ck_spinlock_locked(ck_spinlock_t *lock)`: Returns true if the lock is currently held, false otherwise. ### Specialized Variants - **MCS Lock**: A queue-based, NUMA-friendly spinlock. - `CK_SPINLOCK_MCS_INITIALIZER`: Static initializer for `ck_spinlock_mcs_t`. - `ck_spinlock_mcs_lock(ck_spinlock_mcs_t *lock, ck_spinlock_mcs_context_t *ctx)`: Acquires the MCS lock. - `ck_spinlock_mcs_unlock(ck_spinlock_mcs_t *lock, ck_spinlock_mcs_context_t *ctx)`: Releases the MCS lock. - **Ticket Lock**: A fair, centralized spinlock. - `CK_SPINLOCK_TICKET_INITIALIZER`: Static initializer for `ck_spinlock_ticket_t`. - `ck_spinlock_ticket_lock(ck_spinlock_ticket_t *ticket)`: Acquires the ticket lock. - `ck_spinlock_ticket_unlock(ck_spinlock_ticket_t *ticket)`: Releases the ticket lock. ``` -------------------------------- ### ck_ring — Concurrent Bounded Ring Buffer (FIFO) Source: https://context7.com/concurrencykit/ck/llms.txt The ck_ring API provides functions for managing a bounded, lock-free FIFO ring buffer. It supports different producer/consumer topologies (SPSC, SPMC, MPSC, MPMC) and offers both pointer-based and type-safe interfaces. ```APIDOC ## ck_ring — Concurrent Bounded Ring Buffer (FIFO) `ck_ring` is a bounded, lock-free FIFO ring buffer with variants for SPSC, SPMC, MPSC, and MPMC producer/consumer topologies. The pointer-based API uses `ck_ring_buffer_t`; type-safe APIs for arbitrary value types are generated with the `CK_RING_PROTOTYPE` macro. The ring size must be a power of two. ### Initialization * `ck_ring_init(ck_ring_t *ring, unsigned int capacity)`: Initializes a ring buffer with a specified capacity. The capacity must be a power of two. ### Enqueue Operations * `CK_RING_ENQUEUE_SPSC(type, ring, buffer, item)`: Enqueues an item in a Single-Producer, Single-Consumer (SPSC) manner. Returns `false` if the ring is full. * `CK_RING_ENQUEUE_MPMC(type, ring, buffer, item)`: Enqueues an item in a Multi-Producer, Multi-Consumer (MPMC) manner. Provides size feedback. * `ck_ring_enqueue_reserve_spsc(ring, buffer)`: Reserves a slot for enqueuing in SPSC mode. Returns a pointer to the reserved slot or `NULL` if full. * `ck_ring_enqueue_commit_spsc(ring)`: Commits the enqueued item after filling the reserved slot in SPSC mode. * `ck_ring_enqueue_spsc(ring, buffer, item)`: Enqueues a pointer item in SPSC mode. ### Dequeue Operations * `CK_RING_DEQUEUE_SPSC(type, ring, buffer, out)`: Dequeues an item in SPSC mode. Returns `false` if the ring is empty. * `ck_ring_dequeue_spsc(ring, buffer, out)`: Dequeues a pointer item in SPSC mode. ### Utility Functions * `ck_ring_size(const ck_ring_t *ring)`: Returns the current number of elements in the ring. * `ck_ring_capacity(const ck_ring_t *ring)`: Returns the maximum capacity of the ring. ### Example Usage ```c #include #include #define RING_SIZE 64 /* must be power of two */ /* Type-safe ring for int values */ struct my_item { int value; }; CK_RING_PROTOTYPE(my_item, my_item) static ck_ring_t ring; static struct my_item buf[RING_SIZE]; int main(void) { ck_ring_init(&ring, RING_SIZE); /* SPSC: enqueue (returns false if full) */ struct my_item item = { .value = 42 }; bool ok = CK_RING_ENQUEUE_SPSC(my_item, &ring, buf, &item); printf("enqueue ok=%d size=%u\n", ok, ck_ring_size(&ring)); /* SPSC: dequeue */ struct my_item out; ok = CK_RING_DEQUEUE_SPSC(my_item, &ring, buf, &out); printf("dequeue ok=%d value=%d\n", ok, out.value); /* 42 */ /* MPMC: enqueue with size feedback */ unsigned int sz; CK_RING_ENQUEUE_MPMC(my_item, &ring, buf, &item); /* Reserve-then-commit pattern (zero-copy) for SPSC */ struct my_item *slot = ck_ring_enqueue_reserve_spsc(&ring, (struct ck_ring_buffer *)buf); if (slot != NULL) { slot->value = 99; ck_ring_enqueue_commit_spsc(&ring); } /* Pointer ring (generic API) */ ck_ring_t pring; ck_ring_buffer_t pbuf[32]; ck_ring_init(&pring, 32); void *p = (void *)0xABCD; ck_ring_enqueue_spsc(&pring, pbuf, p); void *got; ck_ring_dequeue_spsc(&pring, pbuf, &got); printf("ptr: %p\n", got); printf("capacity: %u\n", ck_ring_capacity(&ring)); return 0; } ``` ``` -------------------------------- ### ck_rwlock: Read-Write Lock Operations Source: https://context7.com/concurrencykit/ck/llms.txt Illustrates the usage of a write-biased reader-writer lock, including read/write locking, non-blocking attempts, and lock downgrading. Also shows recursive write lock usage. ```c #include #include static ck_rwlock_t rw = CK_RWLOCK_INITIALIZER; static int shared_data = 0; void reader(void) { ck_rwlock_read_lock(&rw); printf("read: %d\n", shared_data); ck_rwlock_read_unlock(&rw); } void writer(int val) { ck_rwlock_write_lock(&rw); shared_data = val; ck_rwlock_write_unlock(&rw); } int main(void) { ck_rwlock_init(&rw); writer(42); reader(); /* Non-blocking attempts */ if (ck_rwlock_write_trylock(&rw)) { shared_data = 100; /* Downgrade write lock to read lock without releasing */ ck_rwlock_write_downgrade(&rw); printf("downgraded read: %d\n", shared_data); ck_rwlock_read_unlock(&rw); } /* Recursive write lock (tid-based) */ ck_rwlock_recursive_t rec = CK_RWLOCK_RECURSIVE_INITIALIZER; ck_rwlock_recursive_write_lock(&rec, 1 /* thread id */); ck_rwlock_recursive_write_lock(&rec, 1 /* same tid: re-entrant */); ck_rwlock_recursive_write_unlock(&rec); ck_rwlock_recursive_write_unlock(&rec); return 0; } ``` -------------------------------- ### ck_array: Initialize and Use SPMC Pointer Array Source: https://context7.com/concurrencykit/ck/llms.txt Initializes a single-writer, multiple-reader (SPMC) pointer array with a custom allocator. Demonstrates staging additions with `ck_array_put`, committing changes to make them visible, and iterating with `CK_ARRAY_FOREACH`. Use `ck_array_put_unique` to insert only if not present. ```c #include #include #include #include static void *my_malloc(size_t s) { return malloc(s); } static void my_free(void *p, size_t b, bool r) { (void)b; (void)r; free(p); } static struct ck_malloc allocator = { .malloc = my_malloc, .free = my_free }; int main(void) { ck_array_t array; ck_array_iterator_t it; void *entry; /* Initialize with initial capacity 4 */ ck_array_init(&array, CK_ARRAY_MODE_SPMC, &allocator, 4); /* Writer: stage additions */ ck_array_put(&array, (void *)0x1); ck_array_put(&array, (void *)0x2); ck_array_put(&array, (void *)0x3); /* Make staged changes visible to readers atomically */ ck_array_commit(&array); /* Concurrent readers use CK_ARRAY_FOREACH */ CK_ARRAY_FOREACH(&array, &it, &entry) { printf("entry: %p\n", entry); } /* put_unique: returns 1 if inserted, 0 if already present */ int r = ck_array_put_unique(&array, (void *)0x2); printf("put_unique 0x2 = %d\n", r); /* 0 (already present) */ /* Remove and commit */ ck_array_remove(&array, (void *)0x1); ck_array_commit(&array); printf("length: %u\n", ck_array_length(&array)); ck_array_deinit(&array, false); return 0; } ``` -------------------------------- ### ck_epoch: Epoch-Protected Sections and Deferred Reclamation Source: https://context7.com/concurrencykit/ck/llms.txt Illustrates epoch-protected sections for safe reading and deferred reclamation of memory. Callbacks are executed after all threads in the current epoch have exited. ```c #include #include #include static ck_epoch_t g_epoch; struct item { int v; ck_epoch_entry_t e; }; static void reclaim(ck_epoch_entry_t *e) { struct item *it = CK_EPOCH_CONTAINER(struct item, e, e); printf("reclaimed %d\n", it->v); free(it); } int main(void) { ck_epoch_record_t rec; ck_epoch_section_t section; ck_epoch_init(&g_epoch); ck_epoch_register(&g_epoch, &rec, NULL); struct item *it = malloc(sizeof *it); it->v = 7; /* Protected read section with a section object */ ck_epoch_begin(&rec, §ion); printf("reading: %d\n", it->v); ck_epoch_end(&rec, §ion); /* Defer reclamation — safe because all readers in current epoch will have exited before the callback runs */ ck_epoch_call(&rec, &it->e, reclaim); /* Poll: dispatch any pending callbacks that are safe to run */ while (ck_epoch_poll(&rec) == false) ; /* spin until drained */ ck_epoch_unregister(&rec); return 0; } ``` -------------------------------- ### ck_ec — Event Counters Source: https://context7.com/concurrencykit/ck/llms.txt ck_ec provides efficient 32-bit and 64-bit event counters with OS-level blocking primitives for synchronization. It allows producers to increment counters and consumers to wait for changes, integrating lock-free signaling. ```APIDOC ## ck_ec — Event Counters `ck_ec` implements 32-bit and 64-bit event counters integrating OS-level blocking (futex-like primitives) with lock-free signaling. Producers call `ck_ec_inc` or `ck_ec_add` after updating shared data; consumers snapshot `ck_ec_value` then call `ck_ec_wait` to block until the counter changes. Requires user-supplied `ck_ec_ops` (gettime, wait, wake). ### Structures - **struct ck_ec32**: Represents a 32-bit event counter. - **struct ck_ec64**: Represents a 64-bit event counter. - **struct ck_ec_ops**: Structure containing platform-specific operations for event counters. - **gettime**: Function to get the current time. - **wait32**: Function to wait on a 32-bit counter. - **wake32**: Function to wake up waiters on a 32-bit counter. - **wait64**: Function to wait on a 64-bit counter (if applicable). - **wake64**: Function to wake up waiters on a 64-bit counter (if applicable). - **struct ck_ec_mode**: Specifies the mode of operation for an event counter. - **ops**: Pointer to the `ck_ec_ops` structure. - **single_producer**: Boolean indicating if there is a single producer. ### Macros - **CK_EC_INITIALIZER**: Initializes a `ck_ec32` or `ck_ec64` structure. - **CK_EC_DEADLINE(deadline, mode, ts)**: Calculates a deadline based on the current time and a timespec `ts`. ### Functions - **ck_ec32_init(ec, value)**: Initializes a 32-bit event counter `ec` with an initial `value`. - **ck_ec64_init(ec, value)**: Initializes a 64-bit event counter `ec` with an initial `value`. - **ck_ec32_value(ec)**: Returns the current value of a 32-bit event counter `ec`. - **ck_ec64_value(ec)**: Returns the current value of a 64-bit event counter `ec`. - **ck_ec32_inc(ec, mode)**: Atomically increments a 32-bit event counter `ec` in the specified `mode`. - **ck_ec64_inc(ec, mode)**: Atomically increments a 64-bit event counter `ec` in the specified `mode`. - **ck_ec32_add(ec, mode, add)**: Atomically adds `add` to a 32-bit event counter `ec` in the specified `mode`. Returns the previous value. - **ck_ec64_add(ec, mode, add)**: Atomically adds `add` to a 64-bit event counter `ec` in the specified `mode`. Returns the previous value. - **ck_ec32_wait(ec, mode, expected, deadline)**: Waits on a 32-bit event counter `ec` until its value differs from `expected`, within the given `deadline`. Returns 0 on change, -1 on timeout. - **ck_ec64_wait(ec, mode, expected, deadline)**: Waits on a 64-bit event counter `ec` until its value differs from `expected`, within the given `deadline`. Returns 0 on change, -1 on timeout. ``` -------------------------------- ### Spin Lock with Exponential Backoff Source: https://context7.com/concurrencykit/ck/llms.txt Implements a spin lock using ck_backoff for exponential backoff to reduce contention. Ensures proper memory ordering with fences. ```c #include #include #include static int locked = 0; void spin_lock(int *lock) { ck_backoff_t backoff = CK_BACKOFF_INITIALIZER; /* start: 512 iterations */ while (ck_pr_cas_int(lock, 0, 1) == false) { /* Exponentially back off to reduce contention */ ck_backoff_eb(&backoff); } } void spin_unlock(int *lock) { ck_pr_fence_release(); ck_pr_store_int(lock, 0); } int main(void) { spin_lock(&locked); /* critical section */ spin_unlock(&locked); return 0; } ``` -------------------------------- ### ck_bitmap — Concurrent Bitmap Source: https://context7.com/concurrencykit/ck/llms.txt ck_bitmap provides a thread-safe bitmap implementation using atomic operations. It supports setting, resetting, testing, and iterating over bits, suitable for managing sets of flags or states concurrently. ```APIDOC ## ck_bitmap — Concurrent Bitmap `ck_bitmap` is a multi-reader, multi-writer concurrent bitmap backed by atomic word operations. It supports set, reset, test-and-set, union, intersection, and iteration. The `CK_BITMAP_INSTANCE(n)` macro declares a fixed-size bitmap inline; heap allocation uses `ck_bitmap_size(n)`. ### Macros - **CK_BITMAP_INSTANCE(n)**: Declares a fixed-size bitmap instance. - **CK_BITMAP_INIT(bm, n, val)**: Initializes a bitmap `bm` of size `n` with initial bit values `val` (true/false). - **CK_BITMAP_SET(bm, bit)**: Atomically sets the specified `bit` in the bitmap `bm`. - **CK_BITMAP_RESET(bm, bit)**: Atomically resets the specified `bit` in the bitmap `bm`. - **CK_BITMAP_TEST(bm, bit)**: Tests if the specified `bit` is set in the bitmap `bm`. - **CK_BITMAP_BTS(bm, bit)**: Atomically tests and sets the specified `bit` in the bitmap `bm`. Returns true if the bit was already set. - **CK_BITMAP_COUNT(bm, limit)**: Counts the number of set bits up to `limit` in the bitmap `bm`. - **CK_BITMAP_CLEAR(bm)**: Clears all bits in the bitmap `bm`. - **CK_BITMAP_EMPTY(bm, n)**: Checks if the bitmap `bm` of size `n` is empty. - **CK_BITMAP_UNION(bm1, bm2)**: Performs a bitwise union of `bm2` into `bm1`. - **CK_BITMAP_ITERATOR_INIT(it, bm)**: Initializes an iterator `it` for the bitmap `bm`. - **CK_BITMAP_NEXT(bm, it, bit)**: Advances the iterator `it` and retrieves the next set bit into `bit`. ``` -------------------------------- ### ck_stack — Lock-Free Stack Source: https://context7.com/concurrencykit/ck/llms.txt The ck_stack API implements a lock-free Treiber stack, supporting multiple producer and consumer concurrency variants (SPNC, MPNC, UPMC, MPMC). Objects must embed `ck_stack_entry_t` and can be retrieved using `CK_STACK_CONTAINER`. ```APIDOC ## ck_stack — Lock-Free Stack `ck_stack` implements a lock-free Treiber stack with multiple producer/consumer concurrency variants: `spnc` (single-producer, no consumers), `mpnc` (multi-producer, no consumers), `upmc` (unique-producers, multi-consumer), and `mpmc` (full MPMC, requires double-word CAS). Objects embed `ck_stack_entry_t` and are retrieved via `CK_STACK_CONTAINER`. ### Initialization * `CK_STACK_INITIALIZER`: Macro to initialize a stack. * `ck_stack_init(ck_stack_t *stack)`: Initializes the stack. ### Push Operations * `ck_stack_push_upmc(ck_stack_t *stack, ck_stack_entry_t *entry)`: Pushes an entry onto the stack in a Multi-Producer, Multi-Consumer (MPMC) manner. * `ck_stack_push_spnc(ck_stack_t *stack, ck_stack_entry_t *entry)`: Pushes an entry onto the stack in a Single-Producer, No-Consumer (SPNC) manner (no atomics required). ### Pop Operations * `ck_stack_pop_upmc(ck_stack_t *stack)`: Pops an entry from the stack in MPMC mode. Returns `NULL` if the stack is empty. * `ck_stack_batch_pop_upmc(ck_stack_t *stack)`: Atomically detaches the entire list of entries from the stack in MPMC mode. Returns the head of the detached list or `NULL` if empty. ### Iteration * `CK_STACK_FOREACH(stack, entry)`: Macro for iterating over the entries in the stack. `entry` will point to each `ck_stack_entry_t`. ### Utility Functions * `CK_STACK_CONTAINER(type, field, name)`: Macro to define a function `name` that retrieves the container `type` from a `ck_stack_entry_t` field. * `CK_STACK_ISEMPTY(stack)`: Returns true if the stack is empty, false otherwise. ### Example Usage ```c #include #include struct node { int value; ck_stack_entry_t entry; }; CK_STACK_CONTAINER(struct node, entry, node_of) int main(void) { ck_stack_t stack = CK_STACK_INITIALIZER; struct node a = { .value = 1 }; struct node b = { .value = 2 }; ck_stack_init(&stack); /* Multi-producer, multi-consumer push */ ck_stack_push_upmc(&stack, &a.entry); ck_stack_push_upmc(&stack, &b.entry); /* Pop */ ck_stack_entry_t *e = ck_stack_pop_upmc(&stack); if (e) printf("popped: %d\n", node_of(e)->value); /* 2 */ /* Batch pop: detach entire list atomically */ ck_stack_entry_t *list = ck_stack_batch_pop_upmc(&stack); /* Iterate the detached list */ CK_STACK_FOREACH(&stack, e) { printf("item: %d\n", node_of(e)->value); } /* Single-producer no-consumer push (no atomics) */ ck_stack_push_spnc(&stack, &a.entry); printf("empty: %d\n", CK_STACK_ISEMPTY(&stack)); return 0; } ``` ```