From bac12e19bb61795606c47b36c7aba096de1b161a Mon Sep 17 00:00:00 2001 From: fincs Date: Fri, 1 Jul 2016 18:28:00 +0200 Subject: [PATCH] Add light events --- libctru/include/3ds/synchronization.h | 45 ++++++++++++++ libctru/source/synchronization.c | 89 +++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/libctru/include/3ds/synchronization.h b/libctru/include/3ds/synchronization.h index 0307745..f23594b 100644 --- a/libctru/include/3ds/synchronization.h +++ b/libctru/include/3ds/synchronization.h @@ -11,6 +11,13 @@ typedef _LOCK_T LightLock; /// A recursive lock. typedef _LOCK_RECURSIVE_T RecursiveLock; +/// 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; + /// Performs a Data Synchronization Barrier operation. static inline void __dsb(void) { @@ -114,3 +121,41 @@ int RecursiveLock_TryLock(RecursiveLock* lock); * @param lock Pointer to the lock. */ void RecursiveLock_Unlock(RecursiveLock* lock); + +/** + * @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); diff --git a/libctru/source/synchronization.c b/libctru/source/synchronization.c index ecbae16..15bc35c 100644 --- a/libctru/source/synchronization.c +++ b/libctru/source/synchronization.c @@ -127,3 +127,92 @@ void RecursiveLock_Unlock(RecursiveLock* lock) LightLock_Unlock(&lock->lock); } } + +static inline void LightEvent_SetState(LightEvent* event, int state) +{ + do + __ldrex(&event->state); + while (__strex(&event->state, state)); +} + +static inline int LightEvent_TryReset(LightEvent* event) +{ + do + { + if (__ldrex(&event->state)) + { + __clrex(); + return 0; + } + } while (__strex(&event->state, -1)); + return 1; +} + +void LightEvent_Init(LightEvent* event, ResetType reset_type) +{ + LightLock_Init(&event->lock); + LightEvent_SetState(event, reset_type == RESET_STICKY ? -2 : -1); +} + +void LightEvent_Clear(LightEvent* event) +{ + if (event->state == 1) + { + LightLock_Lock(&event->lock); + LightEvent_SetState(event, -2); + LightLock_Unlock(&event->lock); + } else if (event->state == 0) + LightEvent_SetState(event, -1); +} + +void LightEvent_Pulse(LightEvent* event) +{ + if (event->state == -2) + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, -1, 0); + else if (event->state == -1) + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, 1, 0); + else + LightEvent_Clear(event); +} + +void LightEvent_Signal(LightEvent* event) +{ + if (event->state == -1) + { + LightEvent_SetState(event, 0); + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, 1, 0); + } else if (event->state == -2) + { + LightLock_Lock(&event->lock); + LightEvent_SetState(event, 1); + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_SIGNAL, -1, 0); + LightLock_Unlock(&event->lock); + } +} + +int LightEvent_TryWait(LightEvent* event) +{ + if (event->state == 1) + return 1; + return LightEvent_TryReset(event); +} + +void LightEvent_Wait(LightEvent* event) +{ + for (;;) + { + if (event->state == -2) + { + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0); + return; + } + if (event->state != -1) + { + if (event->state == 1) + return; + if (event->state == 0 && LightEvent_TryReset(event)) + return; + } + svcArbitrateAddress(arbiter, (u32)event, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0); + } +}