Final Platform Layer 1.0.0
Loading...
Searching...
No Matches
Synchronization methods

Why synchronization is needed

When multiple Threads access the same data, reads and writes can interleave in any order.
Without synchronization the result is a race condition: lost updates, torn reads, or inconsistent state.

Even a single statement like counter++ is not atomic - it is a load, an add and a store.
Two threads running it in parallel can both read the same value, add one, and write back the same result - losing one increment.

Synchronization primitives serialize access to shared data or signal state changes between Threads, so the program behaves as intended.

Solving race conditions

Mutex - protect a critical section

Use a fplMutexHandle to ensure only one Thread executes a code section at a time.

static fplMutexHandle counterMutex;
static uint32_t sharedCounter;
void IncrementCounter() {
fplMutexLock(&counterMutex);
++sharedCounter; // safe: only one Thread at a time
fplMutexUnlock(&counterMutex);
}
fpl_platform_api bool fplMutexUnlock(fplMutexHandle *mutex)
Unlocks the given mutex.
fpl_platform_api bool fplMutexLock(fplMutexHandle *mutex)
Locks the given mutex and blocks any other threads.
Stores the mutex handle structure.

Signal - notify waiting Threads

Use a fplSignalHandle when one Thread must tell others that something happened (e.g. work is ready).

static fplSignalHandle workReady;
void ProducerThread() {
// ... produce work ...
fplSignalSet(&workReady); // wake all waiters
}
void ConsumerThread() {
// ... consume work ...
}
fpl_platform_api bool fplSignalSet(fplSignalHandle *signal)
Sets the signal and wakes up the given signal.
fpl_platform_api bool fplSignalWaitForOne(fplSignalHandle *signal, const fplTimeoutValue timeout)
Waits until the given signal is woken up.
#define FPL_TIMEOUT_INFINITE
Infinite timeout constant.
Stores the signal handle structure.

Condition-Variable - wait for a predicate

Use a fplConditionVariable together with a fplMutexHandle when waiters must check a condition under a lock and re-check after wakeup.

static fplMutexHandle queueMutex;
static fplConditionVariable queueCond;
static int itemCount;
void Push() {
fplMutexLock(&queueMutex);
++itemCount;
fplConditionSignal(&queueCond); // wake one waiter
fplMutexUnlock(&queueMutex);
}
void Pop() {
fplMutexLock(&queueMutex);
while (itemCount == 0) { // re-check after wakeup
fplConditionWait(&queueCond, &queueMutex, FPL_TIMEOUT_INFINITE);
}
--itemCount;
fplMutexUnlock(&queueMutex);
}
fpl_platform_api bool fplConditionWait(fplConditionVariable *condition, fplMutexHandle *mutex, const fplTimeoutValue timeout)
Sleeps on the given condition and releases the mutex when done.
fpl_platform_api bool fplConditionSignal(fplConditionVariable *condition)
Wakes up one thread that waits on the given condition.
Stores the condition variable structure.

Semaphore - limit concurrent access

Use a fplSemaphoreHandle to allow up to N Threads into a section at the same time.

static fplSemaphoreHandle pool; // init with N permits
void UseResource() {
fplSemaphoreWait(&pool, FPL_TIMEOUT_INFINITE); // take a permit
// ... use one of N shared resources ...
fplSemaphoreRelease(&pool); // give it back
}
fpl_platform_api bool fplSemaphoreRelease(fplSemaphoreHandle *semaphore)
Increments the semaphore value by one.
fpl_platform_api bool fplSemaphoreWait(fplSemaphoreHandle *semaphore, const fplTimeoutValue timeout)
Waits for the semaphore until it gets signaled or the timeout has been reached.
Stores the semaphore handle structure.

Atomics - lock-free single-value updates

Use atomics when only a single integer or pointer needs to be updated safely, without a Mutex.

static volatile uint32_t counter;
void IncrementCounter() {
fplAtomicAddAndFetchU32(&counter, 1); // safe without a lock
}
fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *dest, const uint32_t addend)
Adds the addend to destination 32-bit unsigned integer atomically and returns the result after the ad...

Comparison

Primitive Purpose Scope Cost Notes
fplMutexHandle Mutual exclusion of a code section Same process Low Only the owner Thread can unlock
fplSignalHandle Notify one or many waiters Cross-process Medium Set/Reset is not Thread-Safe by itself
fplConditionVariable Wait for a predicate under a Mutex Same process Medium Requires a Mutex; supports signal and broadcast
fplSemaphoreHandle Limit N concurrent accessors Same process Medium Any Thread can release; counted permits
Atomics Lock-free op on a single value Same process Lowest One variable at a time; includes a memory barrier
Note
Pick the lightest primitive that fits the problem. Atomics for a single value, a Mutex for a small critical section, a Condition-Variable when waiting on a predicate, a Semaphore for bounded resources, a Signal for cross-process or fan-out notifications.