/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
- * FILE: mkernel/kernel/work.c
+ * FILE: ntoskrnl/ex/work.c
* PURPOSE: Manage system work queues
- * PROGRAMMER: David Welch (welch@mcmail.com)
- * REVISION HISTORY:
- * 29/06/98: Created
+ *
+ * PROGRAMMERS: Alex Ionescu - Used correct work queue array and added some fixes and checks.
+ * Gunnar Dalsnes - Implemented
*/
/* INCLUDES ******************************************************************/
-#include <ddk/ntddk.h>
-
+#include <ntoskrnl.h>
+#define NDEBUG
#include <internal/debug.h>
-/* TYPES *********************************************************************/
+/* DEFINES *******************************************************************/
-typedef struct
-{
- /*
- * PURPOSE: Head of the list of waiting work items
- */
- LIST_ENTRY Head;
-
- /*
- * PURPOSE: Sychronize access to the access
- */
- KSPIN_LOCK Lock;
-
- /*
- * PURPOSE: Worker threads with nothing to do wait on this event
- */
- KEVENT Busy;
-
- /*
- * PURPOSE: Thread associated with work queue
- */
- HANDLE Thread;
-} WORK_QUEUE, *PWORK_QUEUE;
+#define NUMBER_OF_WORKER_THREADS (5)
+
+/* TYPES *********************************************************************/
/* GLOBALS *******************************************************************/
/*
* PURPOSE: Queue of items waiting to be processed at normal priority
*/
-WORK_QUEUE normal_work_queue = {{0,}};
-
-#define WAIT_INTERVAL (0)
+EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
/* FUNCTIONS ****************************************************************/
-static NTSTATUS ExWorkerThreadEntryPoint(PVOID context)
/*
* FUNCTION: Entry point for a worker thread
* ARGUMENTS:
* NOTE: To kill a worker thread you must queue an item whose callback
* calls PsTerminateSystemThread
*/
+static
+VOID
+STDCALL
+ExpWorkerThreadEntryPoint(IN PVOID Context)
{
- PWORK_QUEUE param = (PWORK_QUEUE)context;
- PWORK_QUEUE_ITEM item;
- PLIST_ENTRY current;
-
- while (1)
- {
- current = ExInterlockedRemoveHeadList(¶m->Head,¶m->Lock);
- if (current!=NULL)
- {
- item = CONTAINING_RECORD(current,WORK_QUEUE_ITEM,Entry);
- item->Routine(item->Context);
- }
- else
- {
- KeClearEvent(¶m->Busy);
- KeWaitForSingleObject((PVOID)¶m->Busy,Executive,KernelMode,
- FALSE,WAIT_INTERVAL);
- }
- };
-}
+ PWORK_QUEUE_ITEM WorkItem;
+ PLIST_ENTRY QueueEntry;
+ WORK_QUEUE_TYPE WorkQueueType;
+ PEX_WORK_QUEUE WorkQueue;
-static VOID ExKillWorkerThreadCallback(PVOID Context)
-{
- PsTerminateSystemThread(STATUS_SUCCESS);
-}
+ /* Get Queue Type and Worker Queue */
+ WorkQueueType = (WORK_QUEUE_TYPE)Context;
+ WorkQueue = &ExWorkerQueue[WorkQueueType];
-void ExKillWorkerThreads(void)
-/*
- * FUNCTION: Kill all running worker threads in preparation for a shutdown
- */
-{
- WORK_QUEUE_ITEM item1;
-
- ExInitializeWorkItem(&item1,ExKillWorkerThreadCallback,NULL);
- ExQueueWorkItem(&item1,DelayedWorkQueue);
+ /* Loop forever */
+ while (TRUE) {
+
+ /* Wait for Something to Happen on the Queue */
+ QueueEntry = KeRemoveQueue(&WorkQueue->WorkerQueue, KernelMode, NULL);
+
+ /* Can't happen since we do a KernelMode wait (and we're a system thread) */
+ ASSERT((NTSTATUS)QueueEntry != STATUS_USER_APC);
+
+ /* this should never happen either, since we wait with NULL timeout,
+ * but there's a slight possibility that STATUS_TIMEOUT is returned
+ * at queue rundown in NT (unlikely) -Gunnar
+ */
+ ASSERT((NTSTATUS)QueueEntry != STATUS_TIMEOUT);
+
+ /* Increment Processed Work Items */
+ InterlockedIncrement((PLONG)&WorkQueue->WorkItemsProcessed);
+
+ /* Get the Work Item */
+ WorkItem = CONTAINING_RECORD(QueueEntry, WORK_QUEUE_ITEM, List);
+
+ /* Call the Worker Routine */
+ WorkItem->WorkerRoutine(WorkItem->Parameter);
+
+ /* Make sure it returned at right IRQL */
+ if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
+
+ KEBUGCHECKEX(WORKER_THREAD_RETURNED_AT_BAD_IRQL,
+ (ULONG_PTR)WorkItem->WorkerRoutine,
+ KeGetCurrentIrql(),
+ (ULONG_PTR)WorkItem->Parameter,
+ (ULONG_PTR)WorkItem);
+ }
+
+ /* Make sure it returned with Impersionation Disabled */
+ if (PsGetCurrentThread()->ActiveImpersonationInfo) {
+
+ KEBUGCHECKEX(IMPERSONATING_WORKER_THREAD,
+ (ULONG_PTR)WorkItem->WorkerRoutine,
+ (ULONG_PTR)WorkItem->Parameter,
+ (ULONG_PTR)WorkItem,
+ 0);
+ }
+ }
}
-void ExInitializeWorkerThreads(void)
+static
+VOID
+STDCALL
+ExpInitializeWorkQueue(WORK_QUEUE_TYPE WorkQueueType,
+ KPRIORITY Priority)
{
- InitializeListHead(&normal_work_queue.Head);
- KeInitializeSpinLock(&normal_work_queue.Lock);
- KeInitializeEvent(&normal_work_queue.Busy,NotificationEvent,FALSE);
- PsCreateSystemThread(&normal_work_queue.Thread,THREAD_ALL_ACCESS,
- NULL,NULL,NULL,ExWorkerThreadEntryPoint,
- &normal_work_queue);
+ ULONG i;
+ PETHREAD Thread;
+ HANDLE hThread;
+
+ /* Loop through how many threads we need to create */
+ for (i = 0; i < NUMBER_OF_WORKER_THREADS; i++) {
+
+ /* Create the System Thread */
+ PsCreateSystemThread(&hThread,
+ THREAD_ALL_ACCESS,
+ NULL,
+ NULL,
+ NULL,
+ ExpWorkerThreadEntryPoint,
+ (PVOID)WorkQueueType);
+
+ /* Get the Thread */
+ ObReferenceObjectByHandle(hThread,
+ THREAD_SET_INFORMATION,
+ PsThreadType,
+ KernelMode,
+ (PVOID*)&Thread,
+ NULL);
+
+ /* Set the Priority */
+ KeSetPriorityThread(&Thread->Tcb, Priority);
+
+ /* Dereference and close handle */
+ ObDereferenceObject(Thread);
+ ZwClose(hThread);
+ }
}
-VOID ExInitializeWorkItem(PWORK_QUEUE_ITEM Item,
- PWORKER_THREAD_ROUTINE Routine,
- PVOID Context)
-/*
- * FUNCTION: Initializes a work item to be processed by one of the system
- * worker threads
- * ARGUMENTS:
- * Item = Pointer to the item to be initialized
- * Routine = Routine to be called by the worker thread
- * Context = Parameter to be passed to the callback
- */
+VOID
+INIT_FUNCTION
+STDCALL
+ExpInitializeWorkerThreads(VOID)
{
- ASSERT_IRQL(DISPATCH_LEVEL);
-
- Item->Routine=Routine;
- Item->Context=Context;
- Item->Entry.Flink=NULL;
- Item->Entry.Blink=NULL;
+ ULONG WorkQueueType;
+
+ /* Initialize the Array */
+ for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType++) {
+
+ RtlZeroMemory(&ExWorkerQueue[WorkQueueType], sizeof(EX_WORK_QUEUE));
+ KeInitializeQueue(&ExWorkerQueue[WorkQueueType].WorkerQueue, 0);
+ }
+
+ /* Create the built-in worker threads for each work queue */
+ ExpInitializeWorkQueue(CriticalWorkQueue, LOW_REALTIME_PRIORITY);
+ ExpInitializeWorkQueue(DelayedWorkQueue, LOW_PRIORITY);
+ ExpInitializeWorkQueue(HyperCriticalWorkQueue, HIGH_PRIORITY);
}
-VOID ExQueueWorkItem(PWORK_QUEUE_ITEM WorkItem,
- WORK_QUEUE_TYPE QueueType)
/*
+ * @implemented
+ *
* FUNCTION: Inserts a work item in a queue for one of the system worker
* threads to process
* ARGUMENTS:
* WorkItem = Item to insert
* QueueType = Queue to insert it in
*/
+VOID
+STDCALL
+ExQueueWorkItem(PWORK_QUEUE_ITEM WorkItem,
+ WORK_QUEUE_TYPE QueueType)
{
- assert(WorkItem!=NULL);
- ASSERT_IRQL(DISPATCH_LEVEL);
-
- /*
- * Insert the item in the appropiate queue and wake up any thread
- * waiting for something to do
- */
- switch(QueueType)
- {
- case DelayedWorkQueue:
- ExInterlockedInsertTailList(&normal_work_queue.Head,&(WorkItem->Entry),
- &normal_work_queue.Lock);
- KeSetEvent(&normal_work_queue.Busy,IO_NO_INCREMENT,FALSE);
- break;
- }
+ ASSERT(WorkItem!=NULL);
+ ASSERT_IRQL(DISPATCH_LEVEL);
+ ASSERT(WorkItem->List.Flink == NULL);
+
+ /* Insert the Queue */
+ KeInsertQueue(&ExWorkerQueue[QueueType].WorkerQueue, &WorkItem->List);
}
+
+/* EOF */