Change KEBUGCHECK -> KEBUGCHECKEX
[reactos.git] / reactos / ntoskrnl / ex / work.c
index cf58a71..5001689 100644 (file)
@@ -1,56 +1,34 @@
 /*
  * 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:
@@ -59,97 +37,145 @@ static NTSTATUS ExWorkerThreadEntryPoint(PVOID context)
  * 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(&param->Head,&param->Lock);
-       if (current!=NULL)
-         {
-            item = CONTAINING_RECORD(current,WORK_QUEUE_ITEM,Entry);
-            item->Routine(item->Context);
-         }
-       else
-         {
-            KeClearEvent(&param->Busy);
-            KeWaitForSingleObject((PVOID)&param->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 */