C CASTLE / MODULE NOTES
CASTLE/Modules/Sync

MODULE 13 / 15

Sync

Non-recursive mutual exclusion with a pluggable wait policy, and lock-free or mutex-backed atomic storage for shared state.

#include "castle/sync/mutex.hpp"
#include "castle/sync/atomic.hpp"

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.hpp
  • core/traits.hpp
  • sync/wait_policy.hpp
  • sync/mutex.hpp, used by the atomic fallback

Public API

Mutex

APIDescription
template <typename WaitPolicy = spin_wait> class basic_mutexNon-recursive mutex; WaitPolicy::wait() runs once per failed lock() retry.
using wait_policy_type = WaitPolicyExposes the policy type used by the mutex specialization.
basic_mutex()Constructs the mutex in the unlocked state.
void lock() noexceptBlocks until the mutex is acquired; no timeout or error return.
bool try_lock() noexceptSingle non-blocking acquire attempt; returns true on success, false on contention.
void unlock() noexceptReleases the mutex; no ownership checks or status reporting.
using mutex = basic_mutex<>Convenience alias for basic_mutex<spin_wait>.

Atomic

APIDescription
enum memory_orderCastle 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, exchangeBasic atomic read/write/replace operations with explicit memory ordering.
fetch_add, fetch_sub, fetch_and, fetch_or, fetch_xorRead-modify-write operations for integral and pointer specializations.
compare_exchange_weak, compare_exchange_strongConditional replacement; update expected on failure.
is_lock_free, is_always_lock_freeQuery runtime and compile-time lock-free properties.
atomic_thread_fence, atomic_signal_fenceFree-function fence helpers mirroring the standard atomic fence API.
atomic_* aliasesConvenience 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_mutex is non-recursive. Calling lock() 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_wait policy busy-waits. Supply a custom WaitPolicy when blocked callers should yield to an RTOS scheduler.
  • Atomic integral and pointer specializations use compiler __atomic builtins 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 through memcmp, matching the trivially copyable requirement.