summaryrefslogtreecommitdiff
path: root/src/Dolphin/os/OSMutex.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Dolphin/os/OSMutex.c')
-rw-r--r--src/Dolphin/os/OSMutex.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/Dolphin/os/OSMutex.c b/src/Dolphin/os/OSMutex.c
new file mode 100644
index 0000000..f853729
--- /dev/null
+++ b/src/Dolphin/os/OSMutex.c
@@ -0,0 +1,223 @@
+#include "dolphin/os.h"
+
+#define PushTail(queue, mutex, link) \
+ do { \
+ OSMutex* __prev; \
+ \
+ __prev = (queue)->tail; \
+ if (__prev == NULL) \
+ (queue)->head = (mutex); \
+ else \
+ __prev->link.next = (mutex); \
+ (mutex)->link.prev = __prev; \
+ (mutex)->link.next = NULL; \
+ (queue)->tail = (mutex); \
+ } while (0)
+
+#define PopHead(queue, mutex, link) \
+ do { \
+ OSMutex* __next; \
+ \
+ (mutex) = (queue)->head; \
+ __next = (mutex)->link.next; \
+ if (__next == NULL) \
+ (queue)->tail = NULL; \
+ else \
+ __next->link.prev = NULL; \
+ (queue)->head = __next; \
+ } while (0)
+
+#define PopItem(queue, mutex, link) \
+ do { \
+ OSMutex* __next; \
+ OSMutex* __prev; \
+ \
+ __next = (mutex)->link.next; \
+ __prev = (mutex)->link.prev; \
+ \
+ if (__next == NULL) \
+ (queue)->tail = __prev; \
+ else \
+ __next->link.prev = __prev; \
+ \
+ if (__prev == NULL) \
+ (queue)->head = __next; \
+ else \
+ __prev->link.next = __next; \
+ } while (0)
+
+void OSInitMutex(OSMutex* mutex) {
+ OSInitThreadQueue(&mutex->queue);
+ mutex->thread = 0;
+ mutex->count = 0;
+}
+
+void OSLockMutex(OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+ OSThread* ownerThread;
+
+ while (TRUE) {
+ ownerThread = ((OSMutex*)mutex)->thread;
+ if (ownerThread == 0) {
+ mutex->thread = currentThread;
+ mutex->count++;
+ PushTail(&currentThread->queueMutex, mutex, link);
+ break;
+ } else if (ownerThread == currentThread) {
+ mutex->count++;
+ break;
+ } else {
+ currentThread->mutex = mutex;
+ __OSPromoteThread(mutex->thread, currentThread->priority);
+ OSSleepThread(&mutex->queue);
+ currentThread->mutex = 0;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void OSUnlockMutex(OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+
+ if (mutex->thread == currentThread && --mutex->count == 0) {
+ PopItem(&currentThread->queueMutex, mutex, link);
+ mutex->thread = NULL;
+ if (currentThread->priority < currentThread->base) {
+ currentThread->priority = __OSGetEffectivePriority(currentThread);
+ }
+
+ OSWakeupThread(&mutex->queue);
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void __OSUnlockAllMutex(OSThread* thread) {
+ OSMutex* mutex;
+
+ while (thread->queueMutex.head) {
+ PopHead(&thread->queueMutex, mutex, link);
+ mutex->count = 0;
+ mutex->thread = NULL;
+ OSWakeupThread(&mutex->queue);
+ }
+}
+
+BOOL OSTryLockMutex(OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+ BOOL locked;
+ if (mutex->thread == 0) {
+ mutex->thread = currentThread;
+ mutex->count++;
+ PushTail(&currentThread->queueMutex, mutex, link);
+ locked = TRUE;
+ } else if (mutex->thread == currentThread) {
+ mutex->count++;
+ locked = TRUE;
+ } else {
+ locked = FALSE;
+ }
+ OSRestoreInterrupts(enabled);
+ return locked;
+}
+
+void OSInitCond(OSCond* cond) { OSInitThreadQueue(&cond->queue); }
+
+void OSWaitCond(OSCond* cond, OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+ s32 count;
+
+ if (mutex->thread == currentThread) {
+ count = mutex->count;
+ mutex->count = 0;
+ PopItem(&currentThread->queueMutex, mutex, link);
+ mutex->thread = NULL;
+
+ if (currentThread->priority < currentThread->base) {
+ currentThread->priority = __OSGetEffectivePriority(currentThread);
+ }
+
+ OSDisableScheduler();
+ OSWakeupThread(&mutex->queue);
+ OSEnableScheduler();
+ OSSleepThread(&cond->queue);
+ OSLockMutex(mutex);
+ mutex->count = count;
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+void OSSignalCond(OSCond* cond) { OSWakeupThread(&cond->queue); }
+
+static BOOL IsMember(OSMutexQueue* queue, OSMutex* mutex) {
+ OSMutex* member;
+
+ for (member = queue->head; member; member = member->link.next) {
+ if (mutex == member)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL __OSCheckMutex(OSMutex* mutex) {
+ OSThread* thread;
+ OSThreadQueue* queue;
+ OSPriority priority = 0;
+
+ queue = &mutex->queue;
+ if (!(queue->head == NULL || queue->head->link.prev == NULL))
+ return FALSE;
+ if (!(queue->tail == NULL || queue->tail->link.next == NULL))
+ return FALSE;
+ for (thread = queue->head; thread; thread = thread->link.next) {
+ if (!(thread->link.next == NULL || thread == thread->link.next->link.prev))
+ return FALSE;
+ if (!(thread->link.prev == NULL || thread == thread->link.prev->link.next))
+ return FALSE;
+
+ if (thread->state != OS_THREAD_STATE_WAITING)
+ return FALSE;
+
+ if (thread->priority < priority)
+ return FALSE;
+ priority = thread->priority;
+ }
+
+ if (mutex->thread) {
+ if (mutex->count <= 0)
+ return FALSE;
+ } else {
+ if (0 != mutex->count)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL __OSCheckDeadLock(OSThread* thread) {
+ OSMutex* mutex;
+
+ mutex = thread->mutex;
+ while (mutex && mutex->thread) {
+ if (mutex->thread == thread)
+ return TRUE;
+ mutex = mutex->thread->mutex;
+ }
+ return FALSE;
+}
+
+BOOL __OSCheckMutexes(OSThread* thread) {
+ OSMutex* mutex;
+
+ for (mutex = thread->queueMutex.head; mutex; mutex = mutex->link.next) {
+ if (mutex->thread != thread)
+ return FALSE;
+ if (!__OSCheckMutex(mutex))
+ return FALSE;
+ }
+ return TRUE;
+}