// Kernel message queue primitive. This file owns bounded queue storage and blocking send/receive behavior. #include #include #include #include #include #include // Implements the queue Destroy operation. static void KiQueueDestroy(OBJECT* obj) { (void)obj; } // Implements the queue Create operation. KSTATUS KeQueueCreate(QUEUE **out) { if (!out) return STATUS_INVALID_PARAMETER; QUEUE* q = (QUEUE *)ObAllocateObject(ObjectTypeQueue, sizeof(QUEUE)); if (!q) return STATUS_NO_MEMORY; q->Header.Destroy = KiQueueDestroy; q->Lock = (SPINLOCK)SPINLOCK_INIT; q->Head = q->Tail = q->Count = 0; LIST_INIT(&q->SendWaiters); LIST_INIT(&q->RecvWaiters); *out = q; return STATUS_SUCCESS; } // Implements the send Message operation. KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, bool blocking) { if (!queue || !msg) return STATUS_INVALID_PARAMETER; if (blocking && !KeCanWait()) return STATUS_INVALID_IRQL; THREAD* self = KeGetCurrentThread(); while (true) { uintptr_t flags = KeAcquireSpinlock(&queue->Lock); if (queue->Count < QUEUE_DEPTH) { queue->Ring[queue->Tail] = *msg; queue->Tail = (queue->Tail + 1) % QUEUE_DEPTH; queue->Count++; // Wake a receiver if one is waiting. if (!LIST_IS_EMPTY(&queue->RecvWaiters)) { LIST_ENTRY* node = ListPopHead(&queue->RecvWaiters); THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink); KeReleaseSpinlock(&queue->Lock, flags); KeUnblockThread(t); } else { KeReleaseSpinlock(&queue->Lock, flags); } return STATUS_SUCCESS; } if (!blocking) { KeReleaseSpinlock(&queue->Lock, flags); return STATUS_QUEUE_FULL; } KePrepareBlockThread(self, &queue->Header, 0); ListInsertTail(&queue->SendWaiters, &self->SchedulerLink); KeSetThreadWaitList(self, &queue->SendWaiters); KeReleaseSpinlock(&queue->Lock, flags); KeCommitBlockThread(self, 0); } } // Implements the receive Message operation. KSTATUS KeReceiveMessage(QUEUE* queue, QUEUE_MESSAGE* msg, bool blocking) { if (!queue || !msg) return STATUS_INVALID_PARAMETER; if (blocking && !KeCanWait()) return STATUS_INVALID_IRQL; THREAD* self = KeGetCurrentThread(); while (true) { uintptr_t flags = KeAcquireSpinlock(&queue->Lock); if (queue->Count > 0) { *msg = queue->Ring[queue->Head]; queue->Head = (queue->Head + 1) % QUEUE_DEPTH; queue->Count--; // Wake a sender if one is waiting. if (!LIST_IS_EMPTY(&queue->SendWaiters)) { LIST_ENTRY* node = ListPopHead(&queue->SendWaiters); THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink); KeReleaseSpinlock(&queue->Lock, flags); KeUnblockThread(t); } else { KeReleaseSpinlock(&queue->Lock, flags); } return STATUS_SUCCESS; } if (!blocking) { KeReleaseSpinlock(&queue->Lock, flags); return STATUS_QUEUE_EMPTY; } KePrepareBlockThread(self, &queue->Header, 0); ListInsertTail(&queue->RecvWaiters, &self->SchedulerLink); KeSetThreadWaitList(self, &queue->RecvWaiters); KeReleaseSpinlock(&queue->Lock, flags); KeCommitBlockThread(self, 0); } }