]> Chaos Git - corbenik/ctrulib.git/commitdiff
Add lightweight synchronization primitives
authorfincs <fincs.alt1@gmail.com>
Tue, 29 Sep 2015 22:07:42 +0000 (00:07 +0200)
committerfincs <fincs.alt1@gmail.com>
Tue, 29 Sep 2015 22:07:42 +0000 (00:07 +0200)
libctru/include/3ds.h
libctru/include/3ds/synchronization.h [new file with mode: 0644]
libctru/source/synchronization.c [new file with mode: 0644]

index 126c5cfd98629ddf467e5e5e0ea7b67a225eb4b1..e86864afe3f03eb81a30fcc2672b1194409c13ba 100644 (file)
@@ -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 (file)
index 0000000..4a21734
--- /dev/null
@@ -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 (file)
index 0000000..99cdb26
--- /dev/null
@@ -0,0 +1,95 @@
+#include <string.h>
+#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);
+       }
+}