// ke_mutex.c - binary ownership mutex #include "ke_mutex.h" #include "ke_thread.h" #include "ke_sched.h" #include "mm_virt.h" #include "ob_object.h" static void MutexDestroy(OBJECT* obj) { (void)obj; } KSTATUS MutexCreate(MUTEX **out) { if (!out) return STATUS_INVALID_PARAMETER; MUTEX* m = (MUTEX *)ObAllocateObject(ObjectTypeMutex, sizeof(MUTEX)); if (!m) return STATUS_NO_MEMORY; m->Header.Destroy = MutexDestroy; m->Lock = (SPINLOCK)SPINLOCK_INIT; m->Owner = (struct THREAD *)0; LIST_INIT(&m->WaitList); *out = m; return STATUS_SUCCESS; } KSTATUS KeAcquireMutex(MUTEX* mutex) { if (!mutex) return STATUS_INVALID_PARAMETER; THREAD* self = KeCurrentThread(); for (;;) { KeAcquireSpinlock(&mutex->Lock); if (!mutex->Owner) { mutex->Owner = self; KeReleaseSpinlock(&mutex->Lock); return STATUS_SUCCESS; } // Already owned - add self to wait list and block. ListInsertTail(&mutex->WaitList, &self->SchedulerLink); KeReleaseSpinlock(&mutex->Lock); KeBlockThread(self, &mutex->Header); // KeBlockThread calls KeYield; when we return here we've been // unblocked by KeReleaseMutex - loop back and try to own it. } } KSTATUS KeTryAcquireMutex(MUTEX* mutex) { if (!mutex) return STATUS_INVALID_PARAMETER; THREAD* self = KeCurrentThread(); KeAcquireSpinlock(&mutex->Lock); if (!mutex->Owner) { mutex->Owner = self; KeReleaseSpinlock(&mutex->Lock); return STATUS_SUCCESS; } KeReleaseSpinlock(&mutex->Lock); return STATUS_FAILURE; } void KeReleaseMutex(MUTEX* mutex) { if (!mutex) return; KeAcquireSpinlock(&mutex->Lock); mutex->Owner = (struct THREAD *)0; // Wake the first waiter, if any. if (!LIST_IS_EMPTY(&mutex->WaitList)) { LIST_ENTRY* node = ListPopHead(&mutex->WaitList); THREAD* waiter = CONTAINING_RECORD(node, THREAD, SchedulerLink); KeReleaseSpinlock(&mutex->Lock); KeUnblockThread(waiter); return; } KeReleaseSpinlock(&mutex->Lock); }