diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 126c5cf..e86864a 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -12,6 +12,7 @@ extern "C" { #include <3ds/linear.h> #include <3ds/vram.h> #include <3ds/os.h> +#include <3ds/synchronization.h> #include <3ds/gfx.h> #include <3ds/console.h> #include <3ds/util/utf.h> diff --git a/libctru/include/3ds/synchronization.h b/libctru/include/3ds/synchronization.h new file mode 100644 index 0000000..4a21734 --- /dev/null +++ b/libctru/include/3ds/synchronization.h @@ -0,0 +1,18 @@ +#pragma once + +typedef s32 LightLock; + +typedef struct +{ + LightLock lock; + u32 thread_tag; + u32 counter; +} RecursiveLock; + +void LightLock_Init(LightLock* lock); +void LightLock_Lock(LightLock* lock); +void LightLock_Unlock(LightLock* lock); + +void RecursiveLock_Init(RecursiveLock* lock); +void RecursiveLock_Lock(RecursiveLock* lock); +void RecursiveLock_Unlock(RecursiveLock* lock); diff --git a/libctru/source/synchronization.c b/libctru/source/synchronization.c new file mode 100644 index 0000000..99cdb26 --- /dev/null +++ b/libctru/source/synchronization.c @@ -0,0 +1,95 @@ +#include +#include <3ds/types.h> +#include <3ds/svc.h> +#include <3ds/synchronization.h> + +static Handle arbiter; + +Result __sync_init(void) +{ + return svcCreateAddressArbiter(&arbiter); +} + +void __sync_fini(void) +{ + if (arbiter) + svcCloseHandle(arbiter); +} + +static inline void __clrex(void) +{ + __asm__ __volatile__("clrex"); +} + +static inline s32 __ldrex(s32* addr) +{ + s32 val; + __asm__ __volatile__("ldrex %[val], [%[addr]]" : [val] "=r" (val) : [addr] "r" (addr)); + return val; +} + +static inline bool __strex(s32* addr, s32 val) +{ + bool res; + __asm__ __volatile__("strex %[res], %[val], [%[addr]]" : [res] "=&r" (res) : [val] "r" (val), [addr] "r" (addr)); + return res; +} + +void LightLock_Init(LightLock* lock) +{ + do + __ldrex(lock); + while (__strex(lock, 1)); +} + +void LightLock_Lock(LightLock* lock) +{ + s32 val; +_begin: + do + { + val = __ldrex(lock); + if (val < 0) + { + __clrex(); + svcArbitrateAddress(arbiter, (u32)lock, ARBITRATION_WAIT_IF_LESS_THAN, 0, 0); + goto _begin; // Try locking again + } + } while (__strex(lock, -val)); +} + +void LightLock_Unlock(LightLock* lock) +{ + s32 val; + do + val = -__ldrex(lock); + while (__strex(lock, val)); + svcArbitrateAddress(arbiter, (u32)lock, ARBITRATION_SIGNAL, 1, 0); +} + +void RecursiveLock_Init(RecursiveLock* lock) +{ + LightLock_Init(&lock->lock); + lock->thread_tag = 0; + lock->counter = 0; +} + +void RecursiveLock_Lock(RecursiveLock* lock) +{ + u32 tag = (u32)getThreadLocalStorage(); + if (lock->thread_tag != tag) + { + LightLock_Lock(&lock->lock); + lock->thread_tag = tag; + } + lock->counter ++; +} + +void RecursiveLock_Unlock(RecursiveLock* lock) +{ + if (!--lock->counter) + { + lock->thread_tag = 0; + LightLock_Unlock(&lock->lock); + } +}