// Kernel event primitive. This file owns manual-reset event state and waiter wakeups. #include #include #include #include #include #include #include // Implements the event Destroy operation. static void KiEventDestroy(OBJECT* obj) { (void)obj; } // Implements the event Create operation. __declspec(dllexport) KSTATUS KeEventCreate(EVENT_TYPE type, uint32_t initialState, EVENT **out) { if (!out) return STATUS_INVALID_PARAMETER; EVENT* e = (EVENT *)ObAllocateObject(ObjectTypeEvent, sizeof(EVENT)); if (!e) return STATUS_NO_MEMORY; e->Header.Destroy = KiEventDestroy; e->Lock = (SPINLOCK)SPINLOCK_INIT; e->Type = type; e->Signaled = initialState ? 1 : 0; LIST_INIT(&e->WaitList); *out = e; return STATUS_SUCCESS; } // Signals event. __declspec(dllexport) void KeSignalEvent(EVENT* event) { if (!event) return; uintptr_t flags = KeAcquireSpinlock(&event->Lock); if (event->Type == EventManualReset) { event->Signaled = 1; while (!LIST_IS_EMPTY(&event->WaitList)) { LIST_ENTRY* node = ListPopHead(&event->WaitList); THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink); KeReleaseSpinlock(&event->Lock, flags); KeUnblockThreadWithStatus(t, STATUS_SUCCESS); flags = KeAcquireSpinlock(&event->Lock); } } else { if (!LIST_IS_EMPTY(&event->WaitList)) { // Auto-reset handoff: transfer the signal directly to exactly one // waiter. Do not leave Signaled=1, because a later waiter can steal // the disk/APC completion before the selected thread resumes. event->Signaled = 0; LIST_ENTRY* node = ListPopHead(&event->WaitList); THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink); KeReleaseSpinlock(&event->Lock, flags); KeUnblockThreadWithStatus(t, STATUS_SUCCESS); return; } event->Signaled = 1; } KeReleaseSpinlock(&event->Lock, flags); } // Clears event. __declspec(dllexport) void KeClearEvent(EVENT* event) { if (!event) return; uintptr_t flags = KeAcquireSpinlock(&event->Lock); event->Signaled = 0; KeReleaseSpinlock(&event->Lock, flags); } // Waits for for Event. __declspec(dllexport) KSTATUS KeWaitForEvent(EVENT* event, uint8_t alertable) { if (!event) return STATUS_INVALID_PARAMETER; if (!KeCanWait()) return STATUS_INVALID_IRQL; if (alertable && KeAreKernelApcsDisabled()) return STATUS_INVALID_PARAMETER; THREAD* self = KeGetCurrentThread(); uintptr_t flags = KeAcquireSpinlock(&event->Lock); if (event->Signaled) { if (event->Type == EventAutoReset) event->Signaled = 0; KeReleaseSpinlock(&event->Lock, flags); return STATUS_SUCCESS; } KePrepareBlockThread(self, &event->Header, alertable); ListInsertTail(&event->WaitList, &self->SchedulerLink); KeSetThreadWaitList(self, &event->WaitList); KeReleaseSpinlock(&event->Lock, flags); return KeCommitBlockThread(self, alertable); }