diff --git a/nx/include/switch.h b/nx/include/switch.h index fe0acdab..37c1c97f 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -15,6 +15,7 @@ extern "C" { #include #include +#include #include #include diff --git a/nx/include/switch/kernel/mutex.h b/nx/include/switch/kernel/mutex.h new file mode 100644 index 00000000..05b61b14 --- /dev/null +++ b/nx/include/switch/kernel/mutex.h @@ -0,0 +1,16 @@ +// Copyright 2017 plutoo +typedef struct { + u32 Tag; +} Mutex; + +typedef struct { + u32 Owner; + Mutex Lock; + size_t Count; +} RMutex; + +void mutexLock(Mutex* m); +void mutexUnlock(Mutex* m); + +void rmutexLock(RMutex* m); +void rmutexUnlock(RMutex* m); diff --git a/nx/include/switch/svc.h b/nx/include/switch/svc.h index 17eeaa2e..0a0f9c53 100644 --- a/nx/include/switch/svc.h +++ b/nx/include/switch/svc.h @@ -48,6 +48,8 @@ Result svcSleepThread(u64 nano); Result svcCloseHandle(Handle handle); Result svcCreateTransferMemory(Handle* out, void* addr, size_t size, u32 perm); Result svcWaitSynchronization(s32* index, const Handle* handles, s32 handleCount, u64 timeout); +Result svcArbitrateLock(u32 wait_tag, u32* tag_location, u32 self_tag); +Result svcArbitrateUnlock(u32* tag_location); Result svcConnectToNamedPort(Handle* session, const char* name); Result svcSendSyncRequest(Handle session); Result svcBreak(u32 breakReason, u64 inval1, u64 inval2); diff --git a/nx/source/kernel/mutex.c b/nx/source/kernel/mutex.c new file mode 100644 index 00000000..9a94ccb1 --- /dev/null +++ b/nx/source/kernel/mutex.c @@ -0,0 +1,74 @@ +// Copyright 2017 plutoo +#include + +#define HAS_LISTENERS 0x40000000 + +static u32 _GetTag() { + // todo: Needs filling in at thread creation. + // todo: Must always be assigned non-zero. + return ((u32*)armGetTls()) [0x1FC/4]; +} + +void mutexLock(Mutex* m) { + u32 self = _GetTag(); + u32 cur = __sync_val_compare_and_swap(&m->Tag, 0, self); + + if (cur == 0) { + // We won the race! + return; + } + + while (1) { + if ((cur &~ HAS_LISTENERS) == self) { + // Kernel assigned it to us! + return; + } + + if (cur & HAS_LISTENERS) { + // The flag is already set, we can use the syscall. + svcArbitrateLock(cur &~ HAS_LISTENERS, &m->Tag, self); + } + else { + // The flag is not set, we need to set it. + u32 old = __sync_val_compare_and_swap(&m->Tag, cur, cur | HAS_LISTENERS); + + if (old == cur) { + // Flag was set successfully. + svcArbitrateLock(cur &~ HAS_LISTENERS, &m->Tag, self); + } + } + + cur = __sync_val_compare_and_swap(&m->Tag, 0, self); + + if (cur == 0) { + // We won the race! + return; + } + } +} + +void mutexUnlock(Mutex* m) { + u32 self = _GetTag(); + u32 old = __sync_val_compare_and_swap(&m->Tag, self, 0); + + if (old & HAS_LISTENERS) { + svcArbitrateUnlock(&m->Tag); + } +} + +void rmutexLock(RMutex* m) { + if (m->Owner == _GetTag()) { + m->Count++; + } + else { + mutexLock(&m->Lock); + m->Owner = _GetTag(); + } +} + +void rmutexUnlock(RMutex* m) { + if (--m->Count == 0) { + m->Owner = 0; + mutexUnlock(&m->Lock); + } +} diff --git a/nx/source/kernel/svc.s b/nx/source/kernel/svc.s index a58fda8a..4896f489 100644 --- a/nx/source/kernel/svc.s +++ b/nx/source/kernel/svc.s @@ -58,6 +58,14 @@ SVC_BEGIN svcWaitSynchronization ret SVC_END +SVC_BEGIN svcArbitrateLock + svc 0x1a +SVC_END + +SVC_BEGIN svcArbitrateUnlock + svc 0x1b +SVC_END + SVC_BEGIN svcConnectToNamedPort str x0, [sp, #-16]! svc 0x1F