164 lines
6.8 KiB
C
164 lines
6.8 KiB
C
|
|
// tiny_atomic.h - Box 1: Atomic Operations (最下層)
|
||
|
|
// Purpose: stdatomic.h のラッパー、memory ordering の抽象化
|
||
|
|
// Invariant: すべての atomic 操作はこの箱を通して行う(直接 stdatomic を使わない)
|
||
|
|
// Design: static inline でゼロコスト、明確な命名規則
|
||
|
|
#pragma once
|
||
|
|
#include <stdatomic.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
|
||
|
|
// ========== Box 1: Atomic Operations ==========
|
||
|
|
// 箱理論の最下層。すべての上位 Box がこの箱に依存する。
|
||
|
|
// 不変条件: Memory ordering を明示的に指定し、外側に弱い順序を漏らさない。
|
||
|
|
|
||
|
|
// Memory ordering alias (可読性向上)
|
||
|
|
#define TINY_MO_RELAXED memory_order_relaxed
|
||
|
|
#define TINY_MO_ACQUIRE memory_order_acquire
|
||
|
|
#define TINY_MO_RELEASE memory_order_release
|
||
|
|
#define TINY_MO_ACQ_REL memory_order_acq_rel
|
||
|
|
#define TINY_MO_SEQ_CST memory_order_seq_cst
|
||
|
|
|
||
|
|
// ========== Load Operations ==========
|
||
|
|
|
||
|
|
// Load with explicit memory order
|
||
|
|
// Usage: uintptr_t val = tiny_atomic_load_explicit(&atomic_var, TINY_MO_ACQUIRE);
|
||
|
|
static inline uintptr_t tiny_atomic_load_explicit(_Atomic uintptr_t* ptr, memory_order mo) {
|
||
|
|
return atomic_load_explicit(ptr, mo);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load with relaxed order (fast, no synchronization)
|
||
|
|
// Usage: uintptr_t val = tiny_atomic_load_relaxed(&atomic_var);
|
||
|
|
static inline uintptr_t tiny_atomic_load_relaxed(_Atomic uintptr_t* ptr) {
|
||
|
|
return atomic_load_explicit(ptr, TINY_MO_RELAXED);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load with acquire order (synchronize with release stores)
|
||
|
|
// Usage: uintptr_t val = tiny_atomic_load_acquire(&atomic_var);
|
||
|
|
static inline uintptr_t tiny_atomic_load_acquire(_Atomic uintptr_t* ptr) {
|
||
|
|
return atomic_load_explicit(ptr, TINY_MO_ACQUIRE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load uint32_t variant
|
||
|
|
static inline uint32_t tiny_atomic_load_u32_relaxed(_Atomic uint32_t* ptr) {
|
||
|
|
return atomic_load_explicit(ptr, TINY_MO_RELAXED);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline uint32_t tiny_atomic_load_u32_acquire(_Atomic uint32_t* ptr) {
|
||
|
|
return atomic_load_explicit(ptr, TINY_MO_ACQUIRE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========== Store Operations ==========
|
||
|
|
|
||
|
|
// Store with explicit memory order
|
||
|
|
// Usage: tiny_atomic_store_explicit(&atomic_var, val, TINY_MO_RELEASE);
|
||
|
|
static inline void tiny_atomic_store_explicit(_Atomic uintptr_t* ptr, uintptr_t val, memory_order mo) {
|
||
|
|
atomic_store_explicit(ptr, val, mo);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Store with relaxed order (fast, no synchronization)
|
||
|
|
// Usage: tiny_atomic_store_relaxed(&atomic_var, val);
|
||
|
|
static inline void tiny_atomic_store_relaxed(_Atomic uintptr_t* ptr, uintptr_t val) {
|
||
|
|
atomic_store_explicit(ptr, val, TINY_MO_RELAXED);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Store with release order (synchronize with acquire loads)
|
||
|
|
// Usage: tiny_atomic_store_release(&atomic_var, val);
|
||
|
|
static inline void tiny_atomic_store_release(_Atomic uintptr_t* ptr, uintptr_t val) {
|
||
|
|
atomic_store_explicit(ptr, val, TINY_MO_RELEASE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Store uint32_t variant
|
||
|
|
static inline void tiny_atomic_store_u32_relaxed(_Atomic uint32_t* ptr, uint32_t val) {
|
||
|
|
atomic_store_explicit(ptr, val, TINY_MO_RELAXED);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void tiny_atomic_store_u32_release(_Atomic uint32_t* ptr, uint32_t val) {
|
||
|
|
atomic_store_explicit(ptr, val, TINY_MO_RELEASE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========== Compare-And-Swap (CAS) ==========
|
||
|
|
|
||
|
|
// CAS with explicit memory order (success and failure)
|
||
|
|
// Returns: 1 on success (value was expected and updated), 0 on failure
|
||
|
|
// Usage: if (tiny_atomic_cas_explicit(&atomic_var, &expected, desired, TINY_MO_ACQ_REL, TINY_MO_ACQUIRE)) { ... }
|
||
|
|
static inline int tiny_atomic_cas_explicit(_Atomic uintptr_t* ptr, uintptr_t* expected, uintptr_t desired,
|
||
|
|
memory_order success_mo, memory_order failure_mo) {
|
||
|
|
return atomic_compare_exchange_strong_explicit(ptr, expected, desired, success_mo, failure_mo);
|
||
|
|
}
|
||
|
|
|
||
|
|
// CAS with acquire-release semantics (most common case)
|
||
|
|
// Usage: if (tiny_atomic_cas_acq_rel(&atomic_var, &expected, desired)) { ... }
|
||
|
|
static inline int tiny_atomic_cas_acq_rel(_Atomic uintptr_t* ptr, uintptr_t* expected, uintptr_t desired) {
|
||
|
|
return atomic_compare_exchange_strong_explicit(ptr, expected, desired, TINY_MO_ACQ_REL, TINY_MO_ACQUIRE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// CAS uint32_t variant
|
||
|
|
static inline int tiny_atomic_cas_u32_acq_rel(_Atomic uint32_t* ptr, uint32_t* expected, uint32_t desired) {
|
||
|
|
return atomic_compare_exchange_strong_explicit(ptr, expected, desired, TINY_MO_ACQ_REL, TINY_MO_ACQUIRE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Weak CAS (may spuriously fail, faster on some architectures)
|
||
|
|
// Usage: while (!tiny_atomic_cas_weak_acq_rel(&atomic_var, &expected, desired)) { expected = old_val; }
|
||
|
|
static inline int tiny_atomic_cas_weak_acq_rel(_Atomic uintptr_t* ptr, uintptr_t* expected, uintptr_t desired) {
|
||
|
|
return atomic_compare_exchange_weak_explicit(ptr, expected, desired, TINY_MO_ACQ_REL, TINY_MO_ACQUIRE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========== Exchange ==========
|
||
|
|
|
||
|
|
// Atomic exchange with explicit memory order
|
||
|
|
// Returns: previous value
|
||
|
|
// Usage: uintptr_t old = tiny_atomic_exchange_explicit(&atomic_var, new_val, TINY_MO_ACQ_REL);
|
||
|
|
static inline uintptr_t tiny_atomic_exchange_explicit(_Atomic uintptr_t* ptr, uintptr_t val, memory_order mo) {
|
||
|
|
return atomic_exchange_explicit(ptr, val, mo);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Atomic exchange with acquire-release semantics
|
||
|
|
// Usage: uintptr_t old = tiny_atomic_exchange_acq_rel(&atomic_var, new_val);
|
||
|
|
static inline uintptr_t tiny_atomic_exchange_acq_rel(_Atomic uintptr_t* ptr, uintptr_t val) {
|
||
|
|
return atomic_exchange_explicit(ptr, val, TINY_MO_ACQ_REL);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========== Fetch-And-Add ==========
|
||
|
|
|
||
|
|
// Atomic increment (fetch and add 1)
|
||
|
|
// Returns: previous value
|
||
|
|
// Usage: uint32_t old = tiny_atomic_fetch_add_u32_relaxed(&counter, 1);
|
||
|
|
static inline uint32_t tiny_atomic_fetch_add_u32_relaxed(_Atomic uint32_t* ptr, uint32_t val) {
|
||
|
|
return atomic_fetch_add_explicit(ptr, val, TINY_MO_RELAXED);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline uint32_t tiny_atomic_fetch_add_u32_acq_rel(_Atomic uint32_t* ptr, uint32_t val) {
|
||
|
|
return atomic_fetch_add_explicit(ptr, val, TINY_MO_ACQ_REL);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========== Fetch-And-Sub ==========
|
||
|
|
|
||
|
|
// Atomic decrement (fetch and sub 1)
|
||
|
|
// Returns: previous value
|
||
|
|
// Usage: uint32_t old = tiny_atomic_fetch_sub_u32_relaxed(&counter, 1);
|
||
|
|
static inline uint32_t tiny_atomic_fetch_sub_u32_relaxed(_Atomic uint32_t* ptr, uint32_t val) {
|
||
|
|
return atomic_fetch_sub_explicit(ptr, val, TINY_MO_RELAXED);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline uint32_t tiny_atomic_fetch_sub_u32_acq_rel(_Atomic uint32_t* ptr, uint32_t val) {
|
||
|
|
return atomic_fetch_sub_explicit(ptr, val, TINY_MO_ACQ_REL);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========== Convenience Macros ==========
|
||
|
|
|
||
|
|
// Shorthand for common patterns
|
||
|
|
#define TINY_ATOMIC_INIT(val) ATOMIC_VAR_INIT(val)
|
||
|
|
|
||
|
|
// Example usage in upper boxes:
|
||
|
|
//
|
||
|
|
// Box 2 (Remote Queue):
|
||
|
|
// _Atomic uintptr_t remote_head = TINY_ATOMIC_INIT(0);
|
||
|
|
// uintptr_t head = tiny_atomic_load_acquire(&remote_head);
|
||
|
|
// tiny_atomic_store_release(&remote_head, new_head);
|
||
|
|
//
|
||
|
|
// Box 3 (Ownership):
|
||
|
|
// _Atomic uint32_t owner_tid = TINY_ATOMIC_INIT(0);
|
||
|
|
// uint32_t expected = 0;
|
||
|
|
// if (tiny_atomic_cas_u32_acq_rel(&owner_tid, &expected, my_tid)) {
|
||
|
|
// // Acquired ownership
|
||
|
|
// }
|