libctru  v2.3.1
synchronization.h
Go to the documentation of this file.
1 /**
2  * @file synchronization.h
3  * @brief Provides synchronization locks.
4  */
5 #pragma once
6 #include <sys/lock.h>
7 #include <3ds/svc.h>
8 
9 /// A light lock.
10 typedef _LOCK_T LightLock;
11 
12 /// A recursive lock.
13 typedef _LOCK_RECURSIVE_T RecursiveLock;
14 
15 /// A condition variable.
16 typedef s32 CondVar;
17 
18 /// A light event.
19 typedef struct
20 {
21  s32 state; ///< State of the event: -2=cleared sticky, -1=cleared oneshot, 0=signaled oneshot, 1=signaled sticky
22  LightLock lock; ///< Lock used for sticky timer operation
23 } LightEvent;
24 
25 /// A light semaphore.
26 typedef struct
27 {
28  s32 current_count; ///< The current release count of the semaphore
29  s16 num_threads_acq; ///< Number of threads concurrently acquiring the semaphore
30  s16 max_count; ///< The maximum release count of the semaphore
32 
33 /// Performs a Data Synchronization Barrier operation.
34 static inline void __dsb(void)
35 {
36  __asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (0) : "memory");
37 }
38 
39 /// Performs a Data Memory Barrier operation.
40 static inline void __dmb(void)
41 {
42  __asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
43 }
44 
45 /// Performs an Instruction Synchronization Barrier (officially "flush prefetch buffer") operation.
46 static inline void __isb(void)
47 {
48  __asm__ __volatile__("mcr p15, 0, %[val], c7, c5, 4" :: [val] "r" (0) : "memory");
49 }
50 
51 /// Performs a clrex operation.
52 static inline void __clrex(void)
53 {
54  __asm__ __volatile__("clrex" ::: "memory");
55 }
56 
57 /**
58  * @brief Performs a ldrex operation.
59  * @param addr Address to perform the operation on.
60  * @return The resulting value.
61  */
62 static inline s32 __ldrex(s32* addr)
63 {
64  s32 val;
65  __asm__ __volatile__("ldrex %[val], %[addr]" : [val] "=r" (val) : [addr] "Q" (*addr));
66  return val;
67 }
68 
69 /**
70  * @brief Performs a strex operation.
71  * @param addr Address to perform the operation on.
72  * @param val Value to store.
73  * @return Whether the operation was successful.
74  */
75 static inline bool __strex(s32* addr, s32 val)
76 {
77  bool res;
78  __asm__ __volatile__("strex %[res], %[val], %[addr]" : [res] "=&r" (res) : [val] "r" (val), [addr] "Q" (*addr));
79  return res;
80 }
81 
82 /**
83  * @brief Performs a ldrexh operation.
84  * @param addr Address to perform the operation on.
85  * @return The resulting value.
86  */
87 static inline u16 __ldrexh(u16* addr)
88 {
89  u16 val;
90  __asm__ __volatile__("ldrexh %[val], %[addr]" : [val] "=r" (val) : [addr] "Q" (*addr));
91  return val;
92 }
93 
94 /**
95  * @brief Performs a strexh operation.
96  * @param addr Address to perform the operation on.
97  * @param val Value to store.
98  * @return Whether the operation was successful.
99  */
100 static inline bool __strexh(u16* addr, u16 val)
101 {
102  bool res;
103  __asm__ __volatile__("strexh %[res], %[val], %[addr]" : [res] "=&r" (res) : [val] "r" (val), [addr] "Q" (*addr));
104  return res;
105 }
106 
107 /**
108  * @brief Performs a ldrexb operation.
109  * @param addr Address to perform the operation on.
110  * @return The resulting value.
111  */
112 static inline u8 __ldrexb(u8* addr)
113 {
114  u8 val;
115  __asm__ __volatile__("ldrexb %[val], %[addr]" : [val] "=r" (val) : [addr] "Q" (*addr));
116  return val;
117 }
118 
119 /**
120  * @brief Performs a strexb operation.
121  * @param addr Address to perform the operation on.
122  * @param val Value to store.
123  * @return Whether the operation was successful.
124  */
125 static inline bool __strexb(u8* addr, u8 val)
126 {
127  bool res;
128  __asm__ __volatile__("strexb %[res], %[val], %[addr]" : [res] "=&r" (res) : [val] "r" (val), [addr] "Q" (*addr));
129  return res;
130 }
131 
132 /// Performs an atomic pre-increment operation.
133 #define AtomicIncrement(ptr) __atomic_add_fetch((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
134 /// Performs an atomic pre-decrement operation.
135 #define AtomicDecrement(ptr) __atomic_sub_fetch((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
136 /// Performs an atomic post-increment operation.
137 #define AtomicPostIncrement(ptr) __atomic_fetch_add((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
138 /// Performs an atomic post-decrement operation.
139 #define AtomicPostDecrement(ptr) __atomic_fetch_sub((u32*)(ptr), 1, __ATOMIC_SEQ_CST)
140 /// Performs an atomic swap operation.
141 #define AtomicSwap(ptr, value) __atomic_exchange_n((u32*)(ptr), (value), __ATOMIC_SEQ_CST)
142 
143 /**
144  * @brief Function used to implement user-mode synchronization primitives.
145  * @param addr Pointer to a signed 32-bit value whose address will be used to identify waiting threads.
146  * @param type Type of action to be performed by the arbiter
147  * @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
148  *
149  * This will perform an arbitration based on #type. The comparisons are done between #value and the value at the address #addr.
150  *
151  * @code
152  * s32 val=0;
153  * // Does *nothing* since val >= 0
154  * syncArbitrateAddress(&val,ARBITRATION_WAIT_IF_LESS_THAN,0);
155  * @endcode
156  *
157  * @note Usage of this function entails an implicit Data Memory Barrier (dmb).
158  */
160 
161 /**
162  * @brief Function used to implement user-mode synchronization primitives (with timeout).
163  * @param addr Pointer to a signed 32-bit value whose address will be used to identify waiting threads.
164  * @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)
165  * @param value Number of threads to signal if using @ref ARBITRATION_SIGNAL, or the value used for comparison.
166  *
167  * This will perform an arbitration based on #type. The comparisons are done between #value and the value at the address #addr.
168  *
169  * @code
170  * s32 val=0;
171  * // Thread will wait for a signal or wake up after 10000000 nanoseconds because val < 1.
172  * syncArbitrateAddressWithTimeout(&val,ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT,1,10000000LL);
173  * @endcode
174  *
175  * @note Usage of this function entails an implicit Data Memory Barrier (dmb).
176  */
178 
179 /**
180  * @brief Initializes a light lock.
181  * @param lock Pointer to the lock.
182  */
184 
185 /**
186  * @brief Locks a light lock.
187  * @param lock Pointer to the lock.
188  */
190 
191 /**
192  * @brief Attempts to lock a light lock.
193  * @param lock Pointer to the lock.
194  * @return Zero on success, non-zero on failure.
195  */
197 
198 /**
199  * @brief Unlocks a light lock.
200  * @param lock Pointer to the lock.
201  */
203 
204 /**
205  * @brief Initializes a recursive lock.
206  * @param lock Pointer to the lock.
207  */
209 
210 /**
211  * @brief Locks a recursive lock.
212  * @param lock Pointer to the lock.
213  */
215 
216 /**
217  * @brief Attempts to lock a recursive lock.
218  * @param lock Pointer to the lock.
219  * @return Zero on success, non-zero on failure.
220  */
222 
223 /**
224  * @brief Unlocks a recursive lock.
225  * @param lock Pointer to the lock.
226  */
228 
229 /**
230  * @brief Initializes a condition variable.
231  * @param cv Pointer to the condition variable.
232  */
234 
235 /**
236  * @brief Waits on a condition variable.
237  * @param cv Pointer to the condition variable.
238  * @param lock Pointer to the lock to atomically unlock/relock during the wait.
239  */
240 void CondVar_Wait(CondVar* cv, LightLock* lock);
241 
242 /**
243  * @brief Waits on a condition variable with a timeout.
244  * @param cv Pointer to the condition variable.
245  * @param lock Pointer to the lock to atomically unlock/relock during the wait.
246  * @param timeout_ns Timeout in nanoseconds.
247  * @return Zero on success, non-zero on failure.
248  */
249 int CondVar_WaitTimeout(CondVar* cv, LightLock* lock, s64 timeout_ns);
250 
251 /**
252  * @brief Wakes up threads waiting on a condition variable.
253  * @param cv Pointer to the condition variable.
254  * @param num_threads Maximum number of threads to wake up (or \ref ARBITRATION_SIGNAL_ALL to wake them all).
255  */
256 void CondVar_WakeUp(CondVar* cv, s32 num_threads);
257 
258 /**
259  * @brief Wakes up a single thread waiting on a condition variable.
260  * @param cv Pointer to the condition variable.
261  */
262 static inline void CondVar_Signal(CondVar* cv)
263 {
264  CondVar_WakeUp(cv, 1);
265 }
266 
267 /**
268  * @brief Wakes up all threads waiting on a condition variable.
269  * @param cv Pointer to the condition variable.
270  */
271 static inline void CondVar_Broadcast(CondVar* cv)
272 {
274 }
275 
276 /**
277  * @brief Initializes a light event.
278  * @param event Pointer to the event.
279  * @param reset_type Type of reset the event uses (RESET_ONESHOT/RESET_STICKY).
280  */
281 void LightEvent_Init(LightEvent* event, ResetType reset_type);
282 
283 /**
284  * @brief Clears a light event.
285  * @param event Pointer to the event.
286  */
288 
289 /**
290  * @brief Wakes up threads waiting on a sticky light event without signaling it. If the event had been signaled before, it is cleared instead.
291  * @param event Pointer to the event.
292  */
294 
295 /**
296  * @brief Signals a light event, waking up threads waiting on it.
297  * @param event Pointer to the event.
298  */
300 
301 /**
302  * @brief Attempts to wait on a light event.
303  * @param event Pointer to the event.
304  * @return Non-zero if the event was signaled, zero otherwise.
305  */
307 
308 /**
309  * @brief Waits on a light event.
310  * @param event Pointer to the event.
311  */
313 
314 /**
315  * @brief Waits on a light event until either the event is signaled or the timeout is reached.
316  * @param event Pointer to the event.
317  * @param timeout_ns Timeout in nanoseconds.
318  * @return Non-zero on timeout, zero otherwise.
319  */
320 int LightEvent_WaitTimeout(LightEvent* event, s64 timeout_ns);
321 
322 /**
323  * @brief Initializes a light semaphore.
324  * @param event Pointer to the semaphore.
325  * @param max_count Initial count of the semaphore.
326  * @param max_count Maximum count of the semaphore.
327  */
328 void LightSemaphore_Init(LightSemaphore* semaphore, s16 initial_count, s16 max_count);
329 
330 /**
331  * @brief Acquires a light semaphore.
332  * @param semaphore Pointer to the semaphore.
333  * @param count Acquire count
334  */
335 void LightSemaphore_Acquire(LightSemaphore* semaphore, s32 count);
336 
337 /**
338  * @brief Attempts to acquire a light semaphore.
339  * @param semaphore Pointer to the semaphore.
340  * @param count Acquire count
341  * @return Zero on success, non-zero on failure
342  */
344 
345 /**
346  * @brief Releases a light semaphore.
347  * @param semaphore Pointer to the semaphore.
348  * @param count Release count
349  */
350 void LightSemaphore_Release(LightSemaphore* semaphore, s32 count);
A light event.
Definition: synchronization.h:20
LightLock lock
Lock used for sticky timer operation.
Definition: synchronization.h:22
s32 state
State of the event: -2=cleared sticky, -1=cleared oneshot, 0=signaled oneshot, 1=signaled sticky.
Definition: synchronization.h:21
A light semaphore.
Definition: synchronization.h:27
s32 current_count
The current release count of the semaphore.
Definition: synchronization.h:28
s16 num_threads_acq
Number of threads concurrently acquiring the semaphore.
Definition: synchronization.h:29
s16 max_count
The maximum release count of the semaphore.
Definition: synchronization.h:30
Syscall wrappers.
ArbitrationType
Arbitration modes.
Definition: svc.h:88
#define ARBITRATION_SIGNAL_ALL
Special value to signal all the threads.
Definition: svc.h:97
ResetType
Reset types (for use with events and timers)
Definition: svc.h:105
void LightEvent_Wait(LightEvent *event)
Waits on a light event.
_LOCK_T LightLock
A light lock.
Definition: synchronization.h:10
Result syncArbitrateAddress(s32 *addr, ArbitrationType type, s32 value)
Function used to implement user-mode synchronization primitives.
int LightLock_TryLock(LightLock *lock)
Attempts to lock a light lock.
void RecursiveLock_Init(RecursiveLock *lock)
Initializes a recursive lock.
static bool __strexh(u16 *addr, u16 val)
Performs a strexh operation.
Definition: synchronization.h:100
void LightEvent_Init(LightEvent *event, ResetType reset_type)
Initializes a light event.
int LightEvent_TryWait(LightEvent *event)
Attempts to wait on a light event.
void LightEvent_Pulse(LightEvent *event)
Wakes up threads waiting on a sticky light event without signaling it.
Result syncArbitrateAddressWithTimeout(s32 *addr, ArbitrationType type, s32 value, s64 timeout_ns)
Function used to implement user-mode synchronization primitives (with timeout).
static void CondVar_Signal(CondVar *cv)
Wakes up a single thread waiting on a condition variable.
Definition: synchronization.h:262
static u8 __ldrexb(u8 *addr)
Performs a ldrexb operation.
Definition: synchronization.h:112
void LightSemaphore_Release(LightSemaphore *semaphore, s32 count)
Releases a light semaphore.
void CondVar_WakeUp(CondVar *cv, s32 num_threads)
Wakes up threads waiting on a condition variable.
int LightSemaphore_TryAcquire(LightSemaphore *semaphore, s32 count)
Attempts to acquire a light semaphore.
static void __dsb(void)
Performs a Data Synchronization Barrier operation.
Definition: synchronization.h:34
void RecursiveLock_Unlock(RecursiveLock *lock)
Unlocks a recursive lock.
void LightEvent_Clear(LightEvent *event)
Clears a light event.
s32 CondVar
A condition variable.
Definition: synchronization.h:16
void LightLock_Init(LightLock *lock)
Initializes a light lock.
void LightLock_Unlock(LightLock *lock)
Unlocks a light lock.
int RecursiveLock_TryLock(RecursiveLock *lock)
Attempts to lock a recursive lock.
static void __isb(void)
Performs an Instruction Synchronization Barrier (officially "flush prefetch buffer") operation.
Definition: synchronization.h:46
void CondVar_Wait(CondVar *cv, LightLock *lock)
Waits on a condition variable.
static s32 __ldrex(s32 *addr)
Performs a ldrex operation.
Definition: synchronization.h:62
int CondVar_WaitTimeout(CondVar *cv, LightLock *lock, s64 timeout_ns)
Waits on a condition variable with a timeout.
int LightEvent_WaitTimeout(LightEvent *event, s64 timeout_ns)
Waits on a light event until either the event is signaled or the timeout is reached.
static void __dmb(void)
Performs a Data Memory Barrier operation.
Definition: synchronization.h:40
void CondVar_Init(CondVar *cv)
Initializes a condition variable.
static void CondVar_Broadcast(CondVar *cv)
Wakes up all threads waiting on a condition variable.
Definition: synchronization.h:271
static u16 __ldrexh(u16 *addr)
Performs a ldrexh operation.
Definition: synchronization.h:87
void LightEvent_Signal(LightEvent *event)
Signals a light event, waking up threads waiting on it.
static bool __strexb(u8 *addr, u8 val)
Performs a strexb operation.
Definition: synchronization.h:125
void LightSemaphore_Init(LightSemaphore *semaphore, s16 initial_count, s16 max_count)
Initializes a light semaphore.
void LightSemaphore_Acquire(LightSemaphore *semaphore, s32 count)
Acquires a light semaphore.
_LOCK_RECURSIVE_T RecursiveLock
A recursive lock.
Definition: synchronization.h:13
static bool __strex(s32 *addr, s32 val)
Performs a strex operation.
Definition: synchronization.h:75
void LightLock_Lock(LightLock *lock)
Locks a light lock.
static void __clrex(void)
Performs a clrex operation.
Definition: synchronization.h:52
void RecursiveLock_Lock(RecursiveLock *lock)
Locks a recursive lock.
int64_t s64
64-bit signed integer
Definition: types.h:29
uint8_t u8
would be nice if newlib had this already
Definition: types.h:21
int16_t s16
16-bit signed integer
Definition: types.h:27
s32 Result
Function result.
Definition: types.h:42
uint16_t u16
16-bit unsigned integer
Definition: types.h:22
int32_t s32
32-bit signed integer
Definition: types.h:28