/
synchronization.h
executable file
·350 lines (307 loc) · 10.4 KB
/
synchronization.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/**
* @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);