Overview
castle::basic_mutex is Castle's non-recursive mutual-exclusion primitive. It protects a short critical section with a spinlock-style lock()/unlock() pair and exposes a pluggable WaitPolicy so blocked callers can either spin or yield through platform-specific integration code.
castle::atomic<T> from castle/sync/atomic.hpp provides embedded-friendly atomic storage and memory-order primitives built on compiler atomics, with a mutex-backed fallback for trivially copyable non-integral types. Use it when shared state must be updated without heap allocation or STL atomics.
Dependencies
core/compiler.hppcore/traits.hppsync/wait_policy.hppsync/mutex.hpp, used by the atomic fallback
Public API
Mutex
| API | Description |
|---|---|
template <typename WaitPolicy = spin_wait> class basic_mutex | Non-recursive mutex; WaitPolicy::wait() runs once per failed lock() retry. |
using wait_policy_type = WaitPolicy | Exposes the policy type used by the mutex specialization. |
basic_mutex() | Constructs the mutex in the unlocked state. |
void lock() noexcept | Blocks until the mutex is acquired; no timeout or error return. |
bool try_lock() noexcept | Single non-blocking acquire attempt; returns true on success, false on contention. |
void unlock() noexcept | Releases the mutex; no ownership checks or status reporting. |
using mutex = basic_mutex<> | Convenience alias for basic_mutex<spin_wait>. |
Atomic
| API | Description |
|---|---|
enum memory_order | Castle memory-order constants mapped directly to compiler __ATOMIC_* values. |
atomic<T> | Primary template for integral atomics; lock-free when the compiler/runtime supports the target type. |
atomic<T*> | Pointer specialization with scaled fetch_add / fetch_sub semantics. |
atomic<T, false> | Mutex-backed fallback for trivially copyable, copyable non-integral types. |
store, load, exchange | Basic atomic read/write/replace operations with explicit memory ordering. |
fetch_add, fetch_sub, fetch_and, fetch_or, fetch_xor | Read-modify-write operations for integral and pointer specializations. |
compare_exchange_weak, compare_exchange_strong | Conditional replacement; update expected on failure. |
is_lock_free, is_always_lock_free | Query runtime and compile-time lock-free properties. |
atomic_thread_fence, atomic_signal_fence | Free-function fence helpers mirroring the standard atomic fence API. |
atomic_* aliases | Convenience typedefs such as atomic_uint32_t, atomic_bool, and atomic_size_t. |
Usage example
See samples/sync/mutex.cpp and samples/sync/atomic.cpp for the full samples.
#include "castle/sync/mutex.hpp"
int main()
{
castle::mutex lock;
if (lock.try_lock())
{
lock.unlock();
}
lock.lock();
lock.unlock();
return 0;
}#include "castle/sync/atomic.hpp"
castle::atomic<uint32_t> flags{0U};
flags.fetch_or(0x01U, castle::memory_order_release);
uint32_t snapshot = flags.load(castle::memory_order_acquire);
(void)snapshot;Constraints & notes
basic_mutexis non-recursive. Callinglock()again from the current holder waits indefinitely.try_lock()is the only API that reports lock contention directly.unlock()does not validate ownership; callers must balance successful acquires correctly.- The implementation uses compiler atomic built-ins directly, with no heap allocation, exceptions, RTTI, virtual functions, or STL synchronization support.
- The default
spin_waitpolicy busy-waits. Supply a customWaitPolicywhen blocked callers should yield to an RTOS scheduler. - Atomic integral and pointer specializations use compiler
__atomicbuiltins directly. - The atomic non-integral fallback is deterministic but not lock-free; it serializes access with
castle::mutex. - Atomic pointer arithmetic is scaled by
sizeof(T). compare_exchange_*for the non-integral fallback performs bitwise comparison throughmemcmp, matching the trivially copyable requirement.