// Copyright 2017 plutoo #include "kernel/svc.h" #include "kernel/mutex.h" #include "../internal.h" #define HAS_LISTENERS 0x40000000 static u32 _GetTag(void) { return getThreadVars()->handle; } bool mutexLock(Mutex* m) { u32 self = _GetTag(); bool first = false; while (1) { u32 cur = __sync_val_compare_and_swap((u32*)m, 0, self); if (cur == 0) { // We won the race! return true; } if ((cur &~ HAS_LISTENERS) == self) { // Kernel assigned it to us! return !first; } if (cur & HAS_LISTENERS) { // The flag is already set, we can use the syscall. svcArbitrateLock(cur &~ HAS_LISTENERS, (u32*)m, self); } else { // The flag is not set, we need to set it. u32 old = __sync_val_compare_and_swap((u32*)m, cur, cur | HAS_LISTENERS); if (old == cur) { // Flag was set successfully. svcArbitrateLock(cur, (u32*)m, self); } } first = true; } } bool mutexTryLock(Mutex* m) { u32 self = _GetTag(); u32 cur = __sync_val_compare_and_swap((u32*)m, 0, self); if (cur == 0) { // We won the race! return true; } if ((cur &~ HAS_LISTENERS) == self) { // Kernel assigned it to us! return true; } return 0; } bool mutexUnlock(Mutex* m) { u32 self = _GetTag(); u32 old = __sync_val_compare_and_swap((u32*)m, self, 0); if ((old &~ HAS_LISTENERS) == self) { if (old & HAS_LISTENERS) { svcArbitrateUnlock((u32*)m); } return true; } else { // The mutex doesn't belong to us return false; } } void rmutexLock(RMutex* m) { if (m->thread_tag != _GetTag()) { mutexLock(&m->lock); m->thread_tag = _GetTag(); } m->counter++; } bool rmutexTryLock(RMutex* m) { if (m->thread_tag != _GetTag()) { if (!mutexTryLock(&m->lock)) { return false; } m->thread_tag = _GetTag(); } m->counter++; return true; } void rmutexUnlock(RMutex* m) { if (--m->counter == 0) { m->thread_tag = 0; mutexUnlock(&m->lock); } }