facil.io

#Atomics and Locks

Concurrency primitives live in ./000 core.h. The lock selector that chooses between spinlocks and OS mutexes is in ./001 header.h.

#Atomic operations

All atomic macros use sequentially consistent ordering. They work on a pointer to the object and return the previous value, unless the name ends in _fetch, in which case they return the new value.

#Load and exchange

Macro Returns Notes
fio_atomic_load(dest, p_obj) assigns value to dest Atomic read of *p_obj.
fio_atomic_exchange(p_obj, value) previous value Atomically sets *p_obj = value.
fio_atomic_compare_exchange_p(p_obj, p_expected, p_desired) 1 on success, 0 on failure System-specific compare-and-swap. p_expected may be overwritten with the current value.

#Arithmetic and logic

Macro Operation Returns
fio_atomic_add(p_obj, value) *p_obj += value previous value
fio_atomic_sub(p_obj, value) *p_obj -= value previous value
fio_atomic_and(p_obj, value) *p_obj &= value previous value
fio_atomic_or(p_obj, value) *p_obj |= value previous value
fio_atomic_xor(p_obj, value) *p_obj ^= value previous value
fio_atomic_nand(p_obj, value) *p_obj = ~(prev & value) previous value
fio_atomic_add_fetch(p_obj, value) *p_obj += value new value
fio_atomic_sub_fetch(p_obj, value) *p_obj -= value new value
fio_atomic_and_fetch(p_obj, value) *p_obj &= value new value
fio_atomic_or_fetch(p_obj, value) *p_obj |= value new value
fio_atomic_xor_fetch(p_obj, value) *p_obj ^= value new value
fio_atomic_nand_fetch(p_obj, value) *p_obj = ~(prev & value) new value

The implementation prefers GCC/Clang __atomic builtins, falls back to legacy __sync builtins, then C11 stdatomic.h, then MSVC intrinsics.

#Spinlocks

The default lock is an 8-bit spinlock with one main lock and seven sub-locks packed into the same byte.

Type / macro Meaning
fio_lock_i volatile unsigned char spinlock type.
FIO_LOCK_INIT Initializer (0).
FIO_LOCK_SUBLOCK(i) Bit mask for sub-lock i (0 to 7). Combine with |.

#Single lock API

Function Behavior
uint8_t fio_trylock(fio_lock_i *lock) Try to acquire the main lock. Returns 0 on success, 1 on failure.
void fio_lock(fio_lock_i *lock) Busy-wait until the main lock is acquired. Avoid long waits.
void fio_unlock(fio_lock_i *lock) Release the main lock.
uint8_t fio_is_locked(fio_lock_i *lock) Returns non-zero if the main lock is held.

#Group lock API

Function Behavior
uint8_t fio_trylock_group(fio_lock_i *lock, uint8_t group) Try to acquire a group of sub-locks. Returns 0 on success, 1 on failure.
void fio_lock_group(fio_lock_i *lock, uint8_t group) Busy-wait until the whole group is acquired.
void fio_unlock_group(fio_lock_i *lock, uint8_t group) Release a group of sub-locks.
uint8_t fio_is_group_locked(fio_lock_i *lock, uint8_t group) Returns non-zero if any sub-lock in group is held.
uint8_t fio_trylock_full(fio_lock_i *lock) Try to lock all sub-locks at once.
void fio_lock_full(fio_lock_i *lock) Busy-wait until every sub-lock is acquired.
void fio_unlock_full(fio_lock_i *lock) Release every sub-lock.

Example:

c
fio_lock_i lock = FIO_LOCK_INIT;
if (!fio_trylock_group(&lock,
                       FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2))) {
  /* critical section */
  fio_unlock_group(&lock, FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2));
}

#Atomic bitmap access

Operate on a single bit inside a byte array. All four helpers are atomic at the byte level.

Function Behavior
uint8_t fio_atomic_bit_get(void *map, size_t bit) Read bit bit from map.
void fio_atomic_bit_set(void *map, size_t bit) Set bit bit to 1.
void fio_atomic_bit_unset(void *map, size_t bit) Set bit bit to 0.
void fio_atomic_bit_flip(void *map, size_t bit) Toggle bit bit.

#Lock selector

./001 header.h chooses between facil.io spinlocks and OS mutexes. The default is spinlocks unless FIO_USE_THREAD_MUTEX is set to 1.

Macro Purpose
FIO_USE_THREAD_MUTEX_TMP Per-include override. Defaults to FIO_USE_THREAD_MUTEX.
FIO_THREADS Defined when OS mutexes are selected.
FIO___LOCK_NAME Human-readable name of the selected lock type.
FIO___LOCK_TYPE The selected lock type (fio_thread_mutex_t or fio_lock_i).
FIO___LOCK_INIT Initializer for the selected lock type.
FIO___LOCK_DESTROY(lock) Destroy a lock instance.
FIO___LOCK_LOCK(lock) Acquire the lock.
FIO___LOCK_TRYLOCK(lock) Try to acquire the lock.
FIO___LOCK_UNLOCK(lock) Release the lock.

These macros are used by modules that need thread safety but do not care which lock type is active.

#Thread scheduling hints

Defined in ./000 core.h and used by the spinlock back-off loops.

Macro Behavior
FIO_THREAD_WAIT(nano_sec) Sleep the current thread for about nano_sec nanoseconds. POSIX uses nanosleep; Windows uses Sleep (rounded up to at least 1 ms).
FIO_THREAD_YIELD() Hint to the CPU that the thread is spinning. Emits pause on x86, yield on ARM, calls YieldProcessor() on MSVC, or falls back to sched_yield() on POSIX.
FIO_THREAD_RESCHEDULE() Short sleep via FIO_THREAD_WAIT(4). Used by fio_lock_group to back off without fully suspending the thread.