synchronization.h
Go to the documentation of this file.
/**
* @file synchronization.h
* @brief Provides synchronization locks.
*/
#pragma once
#include <sys/lock.h>
#include <3ds/svc.h>
/// A light lock.
typedef _LOCK_T LightLock;
/// A recursive lock.
typedef _LOCK_RECURSIVE_T RecursiveLock;
/// A condition variable.
typedef s32 CondVar;
/// A light event.
typedef struct
{
s32 state; ///< State of the event: -2=cleared sticky, -1=cleared oneshot, 0=signaled oneshot, 1=signaled sticky
LightLock lock; ///< Lock used for sticky timer operation
} LightEvent;
/// A light semaphore.
typedef struct
{
s32 current_count; ///< The current release count of the semaphore
s16 num_threads_acq; ///< Number of threads concurrently acquiring the semaphore
s16 max_count; ///< The maximum release count of the semaphore
} LightSemaphore;
/// Performs a Data Synchronization Barrier operation.
static inline void __dsb(void)
{
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (0) : "memory");
}
/// Performs a Data Memory Barrier operation.
static inline void __dmb(void)
{
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
}
/// Performs an Instruction Synchronization Barrier (officially "flush prefetch buffer") operation.
static inline void __isb(void)
{
__asm__ __volatile__("mcr p15, 0, %[val], c7, c5, 4" :: [val] "r" (0) : "memory");
}
/// Performs a clrex operation.
static inline void __clrex(void)
{
__asm__ __volatile__("clrex" ::: "memory");
}
/**
* @brief Performs a ldrex operation.
* @param addr Address to perform the operation on.
* @return The resulting value.
*/
static inline s32 __ldrex(s32* addr)
{
s32 val;
__asm__ __volatile__("ldrex %[val], %[addr]" : [val] "=r" (val) : [addr] "Q" (*addr));
return val;
}
/**
* @brief Performs a strex operation.
* @param addr Address to perform the operation on.
* @param val Value to store.
* @return Whether the operation was successful.
*/
static inline bool __strex(s32* addr, s32 val)
{
bool res;
__asm__ __volatile__("strex %[res], %[val], %[addr]" : [res] "=&r" (res) : [val] "r" (val), [addr] "Q" (*addr));
return res;
}
/**
* @brief Performs a ldrexh operation.
* @param addr Address to perform the operation on.
* @return The resulting value.
*/
static inline u16 __ldrexh(u16* addr)
{
u16 val;
__asm__ __volatile__("ldrexh %[val], %[addr]" : [val] "=r" (val) : [addr] "Q" (*addr));
return val;
}
/**
* @brief Performs a strexh operation.
* @param addr Address to perform the operation on.
* @param val Value to store.
* @return Whether the operation was successful.
*/
static inline bool __strexh(u16* addr, u16 val)
{
bool res;
__asm__ __volatile__("strexh %[res], %[val], %[addr]" : [res] "=&r" (res) : [val] "r" (val), [addr] "Q" (*addr));
return res;
}
/**
* @brief Performs a ldrexb operation.
* @param addr Address to perform the operation on.
* @return The resulting value.
*/
static inline u8 __ldrexb(u8* addr)
{
u8 val;
__asm__ __volatile__("ldrexb %[val], %[addr]" : [val] "=r" (val) : [addr] "Q" (*addr));
return val;
}
/**
* @brief Performs a strexb operation.
* @param addr Address to perform the operation on.
* @param val Value to store.
* @return Whether the operation was successful.
*/
static inline bool __strexb(u8* addr, u8 val)
{
bool res;
__asm__ __volatile__("strexb %[res], %[val], %[addr]" : [res] "=&r" (res) : [val] "r" (val), [addr] "Q" (*addr));
return res;
}
/// Performs an atomic pre-increment operation.
#define AtomicIncrement(ptr) __atomic_add_fetch((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic pre-decrement operation.
#define AtomicDecrement(ptr) __atomic_sub_fetch((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic post-increment operation.
#define AtomicPostIncrement(ptr) __atomic_fetch_add((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic post-decrement operation.
#define AtomicPostDecrement(ptr) __atomic_fetch_sub((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
/// Performs an atomic swap operation.
#define AtomicSwap(ptr, value) __atomic_exchange_n((u32*)(ptr), (value), __ATOMIC_SEQ_CST)
/**
* @brief Function used to implement user-mode synchronization primitives.
* @param addr Pointer to a signed 32-bit value whose address will be used to identify waiting threads.
* @param type Type of action to be performed by the arbiter
* @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
*
* This will perform an arbitration based on #type. The comparisons are done between #value and the value at the address #addr.
*
* @code
* s32 val=0;
* // Does *nothing* since val >= 0
* syncArbitrateAddress(&val,ARBITRATION_WAIT_IF_LESS_THAN,0);
* @endcode
*
* @note Usage of this function entails an implicit Data Memory Barrier (dmb).
*/
Result syncArbitrateAddress(s32* addr, ArbitrationType type, s32 value);
/**
* @brief Function used to implement user-mode synchronization primitives (with timeout).
* @param addr Pointer to a signed 32-bit value whose address will be used to identify waiting threads.
* @param type Type of action to be performed by the arbiter (must use \ref ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT or \ref ARBITRATION_DECREMENT_AND_WAIT_IF_LESS_THAN_TIMEOUT)
* @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
*
* This will perform an arbitration based on #type. The comparisons are done between #value and the value at the address #addr.
*
* @code
* s32 val=0;
* // Thread will wait for a signal or wake up after 10000000 nanoseconds because val < 1.
* syncArbitrateAddressWithTimeout(&val,ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT,1,10000000LL);
* @endcode
*
* @note Usage of this function entails an implicit Data Memory Barrier (dmb).
*/
Result syncArbitrateAddressWithTimeout(s32* addr, ArbitrationType type, s32 value, s64 timeout_ns);
/**
* @brief Initializes a light lock.
* @param lock Pointer to the lock.
*/
void LightLock_Init(LightLock* lock);
/**
* @brief Locks a light lock.
* @param lock Pointer to the lock.
*/
void LightLock_Lock(LightLock* lock);
/**
* @brief Attempts to lock a light lock.
* @param lock Pointer to the lock.
* @return Zero on success, non-zero on failure.
*/
int LightLock_TryLock(LightLock* lock);
/**
* @brief Unlocks a light lock.
* @param lock Pointer to the lock.
*/
void LightLock_Unlock(LightLock* lock);
/**
* @brief Initializes a recursive lock.
* @param lock Pointer to the lock.
*/
void RecursiveLock_Init(RecursiveLock* lock);
/**
* @brief Locks a recursive lock.
* @param lock Pointer to the lock.
*/
void RecursiveLock_Lock(RecursiveLock* lock);
/**
* @brief Attempts to lock a recursive lock.
* @param lock Pointer to the lock.
* @return Zero on success, non-zero on failure.
*/
int RecursiveLock_TryLock(RecursiveLock* lock);
/**
* @brief Unlocks a recursive lock.
* @param lock Pointer to the lock.
*/
void RecursiveLock_Unlock(RecursiveLock* lock);
/**
* @brief Initializes a condition variable.
* @param cv Pointer to the condition variable.
*/
void CondVar_Init(CondVar* cv);
/**
* @brief Waits on a condition variable.
* @param cv Pointer to the condition variable.
* @param lock Pointer to the lock to atomically unlock/relock during the wait.
*/
void CondVar_Wait(CondVar* cv, LightLock* lock);
/**
* @brief Waits on a condition variable with a timeout.
* @param cv Pointer to the condition variable.
* @param lock Pointer to the lock to atomically unlock/relock during the wait.
* @param timeout_ns Timeout in nanoseconds.
* @return Zero on success, non-zero on failure.
*/
int CondVar_WaitTimeout(CondVar* cv, LightLock* lock, s64 timeout_ns);
/**
* @brief Wakes up threads waiting on a condition variable.
* @param cv Pointer to the condition variable.
* @param num_threads Maximum number of threads to wake up (or \ref ARBITRATION_SIGNAL_ALL to wake them all).
*/
void CondVar_WakeUp(CondVar* cv, s32 num_threads);
/**
* @brief Wakes up a single thread waiting on a condition variable.
* @param cv Pointer to the condition variable.
*/
static inline void CondVar_Signal(CondVar* cv)
{
CondVar_WakeUp(cv, 1);
}
/**
* @brief Wakes up all threads waiting on a condition variable.
* @param cv Pointer to the condition variable.
*/
static inline void CondVar_Broadcast(CondVar* cv)
{
CondVar_WakeUp(cv, ARBITRATION_SIGNAL_ALL);
}
/**
* @brief Initializes a light event.
* @param event Pointer to the event.
* @param reset_type Type of reset the event uses (RESET_ONESHOT/RESET_STICKY).
*/
void LightEvent_Init(LightEvent* event, ResetType reset_type);
/**
* @brief Clears a light event.
* @param event Pointer to the event.
*/
void LightEvent_Clear(LightEvent* event);
/**
* @brief Wakes up threads waiting on a sticky light event without signaling it. If the event had been signaled before, it is cleared instead.
* @param event Pointer to the event.
*/
void LightEvent_Pulse(LightEvent* event);
/**
* @brief Signals a light event, waking up threads waiting on it.
* @param event Pointer to the event.
*/
void LightEvent_Signal(LightEvent* event);
/**
* @brief Attempts to wait on a light event.
* @param event Pointer to the event.
* @return Non-zero if the event was signaled, zero otherwise.
*/
int LightEvent_TryWait(LightEvent* event);
/**
* @brief Waits on a light event.
* @param event Pointer to the event.
*/
void LightEvent_Wait(LightEvent* event);
/**
* @brief Waits on a light event until either the event is signaled or the timeout is reached.
* @param event Pointer to the event.
* @param timeout_ns Timeout in nanoseconds.
* @return Non-zero on timeout, zero otherwise.
*/
int LightEvent_WaitTimeout(LightEvent* event, s64 timeout_ns);
/**
* @brief Initializes a light semaphore.
* @param event Pointer to the semaphore.
* @param max_count Initial count of the semaphore.
* @param max_count Maximum count of the semaphore.
*/
void LightSemaphore_Init(LightSemaphore* semaphore, s16 initial_count, s16 max_count);
/**
* @brief Acquires a light semaphore.
* @param semaphore Pointer to the semaphore.
* @param count Acquire count
*/
void LightSemaphore_Acquire(LightSemaphore* semaphore, s32 count);
/**
* @brief Attempts to acquire a light semaphore.
* @param semaphore Pointer to the semaphore.
* @param count Acquire count
* @return Zero on success, non-zero on failure
*/
int LightSemaphore_TryAcquire(LightSemaphore* semaphore, s32 count);
/**
* @brief Releases a light semaphore.
* @param semaphore Pointer to the semaphore.
* @param count Release count
*/
void LightSemaphore_Release(LightSemaphore* semaphore, s32 count);
LightEvent
A light event.
Definition: synchronization.h:20
LightEvent::lock
LightLock lock
Lock used for sticky timer operation.
Definition: synchronization.h:22
LightEvent::state
s32 state
State of the event: -2=cleared sticky, -1=cleared oneshot, 0=signaled oneshot, 1=signaled sticky.
Definition: synchronization.h:21
LightSemaphore
A light semaphore.
Definition: synchronization.h:27
LightSemaphore::current_count
s32 current_count
The current release count of the semaphore.
Definition: synchronization.h:28
LightSemaphore::num_threads_acq
s16 num_threads_acq
Number of threads concurrently acquiring the semaphore.
Definition: synchronization.h:29
LightSemaphore::max_count
s16 max_count
The maximum release count of the semaphore.
Definition: synchronization.h:30
svc.h
Syscall wrappers.
ArbitrationType
ArbitrationType
Arbitration modes.
Definition: svc.h:88
ARBITRATION_SIGNAL_ALL
#define ARBITRATION_SIGNAL_ALL
Special value to signal all the threads.
Definition: svc.h:97
ResetType
ResetType
Reset types (for use with events and timers)
Definition: svc.h:105
LightEvent_Wait
void LightEvent_Wait(LightEvent *event)
Waits on a light event.
LightLock
_LOCK_T LightLock
A light lock.
Definition: synchronization.h:10
syncArbitrateAddress
Result syncArbitrateAddress(s32 *addr, ArbitrationType type, s32 value)
Function used to implement user-mode synchronization primitives.
LightLock_TryLock
int LightLock_TryLock(LightLock *lock)
Attempts to lock a light lock.
RecursiveLock_Init
void RecursiveLock_Init(RecursiveLock *lock)
Initializes a recursive lock.
__strexh
static bool __strexh(u16 *addr, u16 val)
Performs a strexh operation.
Definition: synchronization.h:100
LightEvent_Init
void LightEvent_Init(LightEvent *event, ResetType reset_type)
Initializes a light event.
LightEvent_TryWait
int LightEvent_TryWait(LightEvent *event)
Attempts to wait on a light event.
LightEvent_Pulse
void LightEvent_Pulse(LightEvent *event)
Wakes up threads waiting on a sticky light event without signaling it.
syncArbitrateAddressWithTimeout
Result syncArbitrateAddressWithTimeout(s32 *addr, ArbitrationType type, s32 value, s64 timeout_ns)
Function used to implement user-mode synchronization primitives (with timeout).
CondVar_Signal
static void CondVar_Signal(CondVar *cv)
Wakes up a single thread waiting on a condition variable.
Definition: synchronization.h:262
__ldrexb
static u8 __ldrexb(u8 *addr)
Performs a ldrexb operation.
Definition: synchronization.h:112
LightSemaphore_Release
void LightSemaphore_Release(LightSemaphore *semaphore, s32 count)
Releases a light semaphore.
CondVar_WakeUp
void CondVar_WakeUp(CondVar *cv, s32 num_threads)
Wakes up threads waiting on a condition variable.
LightSemaphore_TryAcquire
int LightSemaphore_TryAcquire(LightSemaphore *semaphore, s32 count)
Attempts to acquire a light semaphore.
__dsb
static void __dsb(void)
Performs a Data Synchronization Barrier operation.
Definition: synchronization.h:34
RecursiveLock_Unlock
void RecursiveLock_Unlock(RecursiveLock *lock)
Unlocks a recursive lock.
LightEvent_Clear
void LightEvent_Clear(LightEvent *event)
Clears a light event.
CondVar
s32 CondVar
A condition variable.
Definition: synchronization.h:16
LightLock_Init
void LightLock_Init(LightLock *lock)
Initializes a light lock.
LightLock_Unlock
void LightLock_Unlock(LightLock *lock)
Unlocks a light lock.
RecursiveLock_TryLock
int RecursiveLock_TryLock(RecursiveLock *lock)
Attempts to lock a recursive lock.
__isb
static void __isb(void)
Performs an Instruction Synchronization Barrier (officially "flush prefetch buffer") operation.
Definition: synchronization.h:46
CondVar_Wait
void CondVar_Wait(CondVar *cv, LightLock *lock)
Waits on a condition variable.
__ldrex
static s32 __ldrex(s32 *addr)
Performs a ldrex operation.
Definition: synchronization.h:62
CondVar_WaitTimeout
int CondVar_WaitTimeout(CondVar *cv, LightLock *lock, s64 timeout_ns)
Waits on a condition variable with a timeout.
LightEvent_WaitTimeout
int LightEvent_WaitTimeout(LightEvent *event, s64 timeout_ns)
Waits on a light event until either the event is signaled or the timeout is reached.
__dmb
static void __dmb(void)
Performs a Data Memory Barrier operation.
Definition: synchronization.h:40
CondVar_Init
void CondVar_Init(CondVar *cv)
Initializes a condition variable.
CondVar_Broadcast
static void CondVar_Broadcast(CondVar *cv)
Wakes up all threads waiting on a condition variable.
Definition: synchronization.h:271
__ldrexh
static u16 __ldrexh(u16 *addr)
Performs a ldrexh operation.
Definition: synchronization.h:87
LightEvent_Signal
void LightEvent_Signal(LightEvent *event)
Signals a light event, waking up threads waiting on it.
__strexb
static bool __strexb(u8 *addr, u8 val)
Performs a strexb operation.
Definition: synchronization.h:125
LightSemaphore_Init
void LightSemaphore_Init(LightSemaphore *semaphore, s16 initial_count, s16 max_count)
Initializes a light semaphore.
LightSemaphore_Acquire
void LightSemaphore_Acquire(LightSemaphore *semaphore, s32 count)
Acquires a light semaphore.
RecursiveLock
_LOCK_RECURSIVE_T RecursiveLock
A recursive lock.
Definition: synchronization.h:13
__strex
static bool __strex(s32 *addr, s32 val)
Performs a strex operation.
Definition: synchronization.h:75
LightLock_Lock
void LightLock_Lock(LightLock *lock)
Locks a light lock.
__clrex
static void __clrex(void)
Performs a clrex operation.
Definition: synchronization.h:52
RecursiveLock_Lock
void RecursiveLock_Lock(RecursiveLock *lock)
Locks a recursive lock.
s64
int64_t s64
64-bit signed integer
Definition: types.h:29
u8
uint8_t u8
would be nice if newlib had this already
Definition: types.h:21
s16
int16_t s16
16-bit signed integer
Definition: types.h:27
Result
s32 Result
Function result.
Definition: types.h:42
u16
uint16_t u16
16-bit unsigned integer
Definition: types.h:22
s32
int32_t s32
32-bit signed integer
Definition: types.h:28