24d328abc07f9cd78286c31dcac7f057f69eacf3
[reactos.git] / ntoskrnl / ex / work.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ex/work.c
5 * PURPOSE: Manage system work queues and worker threads
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #if defined (ALLOC_PRAGMA)
16 #pragma alloc_text(INIT, ExpInitializeWorkerThreads)
17 #endif
18
19 /* DATA **********************************************************************/
20
21 /* Number of worker threads for each Queue */
22 #define EX_HYPERCRITICAL_WORK_THREADS 1
23 #define EX_DELAYED_WORK_THREADS 3
24 #define EX_CRITICAL_WORK_THREADS 5
25
26 /* Magic flag for dynamic worker threads */
27 #define EX_DYNAMIC_WORK_THREAD 0x80000000
28
29 /* Worker thread priority increments (added to base priority) */
30 #define EX_HYPERCRITICAL_QUEUE_PRIORITY_INCREMENT 7
31 #define EX_CRITICAL_QUEUE_PRIORITY_INCREMENT 5
32 #define EX_DELAYED_QUEUE_PRIORITY_INCREMENT 4
33
34 /* The actual worker queue array */
35 EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
36
37 /* Accounting of the total threads and registry hacked threads */
38 ULONG ExCriticalWorkerThreads;
39 ULONG ExDelayedWorkerThreads;
40 ULONG ExpAdditionalCriticalWorkerThreads;
41 ULONG ExpAdditionalDelayedWorkerThreads;
42
43 /* Future support for stack swapping worker threads */
44 BOOLEAN ExpWorkersCanSwap;
45 LIST_ENTRY ExpWorkerListHead;
46 FAST_MUTEX ExpWorkerSwapinMutex;
47
48 /* The worker balance set manager events */
49 KEVENT ExpThreadSetManagerEvent;
50 KEVENT ExpThreadSetManagerShutdownEvent;
51
52 /* Thread pointers for future worker thread shutdown support */
53 PETHREAD ExpWorkerThreadBalanceManagerPtr;
54 PETHREAD ExpLastWorkerThread;
55
56 /* PRIVATE FUNCTIONS *********************************************************/
57
58 /*++
59 * @name ExpWorkerThreadEntryPoint
60 *
61 * The ExpWorkerThreadEntryPoint routine is the entrypoint for any new
62 * worker thread created by teh system.
63 *
64 * @param Context
65 * Contains the work queue type masked with a flag specifing whether the
66 * thread is dynamic or not.
67 *
68 * @return None.
69 *
70 * @remarks A dynamic thread can timeout after 10 minutes of waiting on a queue
71 * while a static thread will never timeout.
72 *
73 * Worker threads must return at IRQL == PASSIVE_LEVEL, must not have
74 * active impersonation info, and must not have disabled APCs.
75 *
76 * NB: We will re-enable APCs for broken threads but all other cases
77 * will generate a bugcheck.
78 *
79 *--*/
80 VOID
81 NTAPI
82 ExpWorkerThreadEntryPoint(IN PVOID Context)
83 {
84 PWORK_QUEUE_ITEM WorkItem;
85 PLIST_ENTRY QueueEntry;
86 WORK_QUEUE_TYPE WorkQueueType;
87 PEX_WORK_QUEUE WorkQueue;
88 LARGE_INTEGER Timeout;
89 PLARGE_INTEGER TimeoutPointer = NULL;
90 PETHREAD Thread = PsGetCurrentThread();
91 KPROCESSOR_MODE WaitMode;
92 EX_QUEUE_WORKER_INFO OldValue, NewValue;
93
94 /* Check if this is a dyamic thread */
95 if ((ULONG_PTR)Context & EX_DYNAMIC_WORK_THREAD)
96 {
97 /* It is, which means we will eventually time out after 10 minutes */
98 Timeout.QuadPart = Int32x32To64(10, -10000000 * 60);
99 TimeoutPointer = &Timeout;
100 }
101
102 /* Get Queue Type and Worker Queue */
103 WorkQueueType = (WORK_QUEUE_TYPE)((ULONG_PTR)Context &
104 ~EX_DYNAMIC_WORK_THREAD);
105 WorkQueue = &ExWorkerQueue[WorkQueueType];
106
107 /* Select the wait mode */
108 WaitMode = (UCHAR)WorkQueue->Info.WaitMode;
109
110 /* Nobody should have initialized this yet, do it now */
111 ASSERT(Thread->ExWorkerCanWaitUser == 0);
112 if (WaitMode == UserMode) Thread->ExWorkerCanWaitUser = TRUE;
113
114 /* If we shouldn't swap, disable that feature */
115 if (!ExpWorkersCanSwap) KeSetKernelStackSwapEnable(FALSE);
116
117 /* Set the worker flags */
118 do
119 {
120 /* Check if the queue is being disabled */
121 if (WorkQueue->Info.QueueDisabled)
122 {
123 /* Re-enable stack swapping and kill us */
124 KeSetKernelStackSwapEnable(TRUE);
125 PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN);
126 }
127
128 /* Increase the worker count */
129 OldValue = WorkQueue->Info;
130 NewValue = OldValue;
131 NewValue.WorkerCount++;
132 }
133 while (InterlockedCompareExchange((PLONG)&WorkQueue->Info,
134 *(PLONG)&NewValue,
135 *(PLONG)&OldValue) != *(PLONG)&OldValue);
136
137 /* Success, you are now officially a worker thread! */
138 Thread->ActiveExWorker = TRUE;
139
140 /* Loop forever */
141 ProcessLoop:
142 for (;;)
143 {
144 /* Wait for something to happen on the queue */
145 QueueEntry = KeRemoveQueue(&WorkQueue->WorkerQueue,
146 WaitMode,
147 TimeoutPointer);
148
149 /* Check if we timed out and quit this loop in that case */
150 if ((NTSTATUS)(ULONG_PTR)QueueEntry == STATUS_TIMEOUT) break;
151
152 /* Increment Processed Work Items */
153 InterlockedIncrement((PLONG)&WorkQueue->WorkItemsProcessed);
154
155 /* Get the Work Item */
156 WorkItem = CONTAINING_RECORD(QueueEntry, WORK_QUEUE_ITEM, List);
157
158 /* Make sure nobody is trying to play smart with us */
159 ASSERT((ULONG_PTR)WorkItem->WorkerRoutine > MmUserProbeAddress);
160
161 /* Call the Worker Routine */
162 WorkItem->WorkerRoutine(WorkItem->Parameter);
163
164 /* Make sure APCs are not disabled */
165 if (Thread->Tcb.CombinedApcDisable != 0)
166 {
167 /* We're nice and do it behind your back */
168 DPRINT1("Warning: Broken Worker Thread: %p %p %p came back "
169 "with APCs disabled!\n",
170 WorkItem->WorkerRoutine,
171 WorkItem->Parameter,
172 WorkItem);
173 ASSERT(Thread->Tcb.CombinedApcDisable == 0);
174 Thread->Tcb.CombinedApcDisable = 0;
175 }
176
177 /* Make sure it returned at right IRQL */
178 if (KeGetCurrentIrql() != PASSIVE_LEVEL)
179 {
180 /* It didn't, bugcheck! */
181 KeBugCheckEx(WORKER_THREAD_RETURNED_AT_BAD_IRQL,
182 (ULONG_PTR)WorkItem->WorkerRoutine,
183 KeGetCurrentIrql(),
184 (ULONG_PTR)WorkItem->Parameter,
185 (ULONG_PTR)WorkItem);
186 }
187
188 /* Make sure it returned with Impersionation Disabled */
189 if (Thread->ActiveImpersonationInfo)
190 {
191 /* It didn't, bugcheck! */
192 KeBugCheckEx(IMPERSONATING_WORKER_THREAD,
193 (ULONG_PTR)WorkItem->WorkerRoutine,
194 (ULONG_PTR)WorkItem->Parameter,
195 (ULONG_PTR)WorkItem,
196 0);
197 }
198 }
199
200 /* This is a dynamic thread. Terminate it unless IRPs are pending */
201 if (!IsListEmpty(&Thread->IrpList)) goto ProcessLoop;
202
203 /* Don't terminate it if the queue is disabled either */
204 if (WorkQueue->Info.QueueDisabled) goto ProcessLoop;
205
206 /* Set the worker flags */
207 do
208 {
209 /* Decrease the worker count */
210 OldValue = WorkQueue->Info;
211 NewValue = OldValue;
212 NewValue.WorkerCount--;
213 }
214 while (InterlockedCompareExchange((PLONG)&WorkQueue->Info,
215 *(PLONG)&NewValue,
216 *(PLONG)&OldValue) != *(PLONG)&OldValue);
217
218 /* Decrement dynamic thread count */
219 InterlockedDecrement(&WorkQueue->DynamicThreadCount);
220
221 /* We're not a worker thread anymore */
222 Thread->ActiveExWorker = FALSE;
223
224 /* Re-enable the stack swap */
225 KeSetKernelStackSwapEnable(TRUE);
226 return;
227 }
228
229 /*++
230 * @name ExpCreateWorkerThread
231 *
232 * The ExpCreateWorkerThread routine creates a new worker thread for the
233 * specified queue.
234 *
235 * @param QueueType
236 * Type of the queue to use for this thread. Valid values are:
237 * - DelayedWorkQueue
238 * - CriticalWorkQueue
239 * - HyperCriticalWorkQueue
240 *
241 * @param Dynamic
242 * Specifies whether or not this thread is a dynamic thread.
243 *
244 * @return None.
245 *
246 * @remarks HyperCritical work threads run at priority 7; Critical work threads
247 * run at priority 5, and delayed work threads run at priority 4.
248 *
249 * This, worker threads cannot pre-empty a normal user-mode thread.
250 *
251 *--*/
252 VOID
253 NTAPI
254 ExpCreateWorkerThread(WORK_QUEUE_TYPE WorkQueueType,
255 IN BOOLEAN Dynamic)
256 {
257 PETHREAD Thread;
258 HANDLE hThread;
259 ULONG Context;
260 KPRIORITY Priority;
261
262 /* Check if this is going to be a dynamic thread */
263 Context = WorkQueueType;
264
265 /* Add the dynamic mask */
266 if (Dynamic) Context |= EX_DYNAMIC_WORK_THREAD;
267
268 /* Create the System Thread */
269 PsCreateSystemThread(&hThread,
270 THREAD_ALL_ACCESS,
271 NULL,
272 NULL,
273 NULL,
274 ExpWorkerThreadEntryPoint,
275 UlongToPtr(Context));
276
277 /* If the thread is dynamic */
278 if (Dynamic)
279 {
280 /* Increase the count */
281 InterlockedIncrement(&ExWorkerQueue[WorkQueueType].DynamicThreadCount);
282 }
283
284 /* Set the priority */
285 if (WorkQueueType == DelayedWorkQueue)
286 {
287 /* Priority == 4 */
288 Priority = EX_DELAYED_QUEUE_PRIORITY_INCREMENT;
289 }
290 else if (WorkQueueType == CriticalWorkQueue)
291 {
292 /* Priority == 5 */
293 Priority = EX_CRITICAL_QUEUE_PRIORITY_INCREMENT;
294 }
295 else
296 {
297 /* Priority == 7 */
298 Priority = EX_HYPERCRITICAL_QUEUE_PRIORITY_INCREMENT;
299 }
300
301 /* Get the Thread */
302 ObReferenceObjectByHandle(hThread,
303 THREAD_SET_INFORMATION,
304 PsThreadType,
305 KernelMode,
306 (PVOID*)&Thread,
307 NULL);
308
309 /* Set the Priority */
310 KeSetBasePriorityThread(&Thread->Tcb, Priority);
311
312 /* Dereference and close handle */
313 ObDereferenceObject(Thread);
314 ObCloseHandle(hThread, KernelMode);
315 }
316
317 /*++
318 * @name ExpDetectWorkerThreadDeadlock
319 *
320 * The ExpDetectWorkerThreadDeadlock routine checks every queue and creates
321 * a dynamic thread if the queue seems to be deadlocked.
322 *
323 * @param None
324 *
325 * @return None.
326 *
327 * @remarks The algorithm for deciding if a new thread must be created is based
328 * on whether the queue has processed no new items in the last second,
329 * and new items are still enqueued.
330 *
331 *--*/
332 VOID
333 NTAPI
334 ExpDetectWorkerThreadDeadlock(VOID)
335 {
336 ULONG i;
337 PEX_WORK_QUEUE Queue;
338
339 /* Loop the 3 queues */
340 for (i = 0; i < MaximumWorkQueue; i++)
341 {
342 /* Get the queue */
343 Queue = &ExWorkerQueue[i];
344 ASSERT(Queue->DynamicThreadCount <= 16);
345
346 /* Check if stuff is on the queue that still is unprocessed */
347 if ((Queue->QueueDepthLastPass) &&
348 (Queue->WorkItemsProcessed == Queue->WorkItemsProcessedLastPass) &&
349 (Queue->DynamicThreadCount < 16))
350 {
351 /* Stuff is still on the queue and nobody did anything about it */
352 DPRINT1("EX: Work Queue Deadlock detected: %lu\n", i);
353 ExpCreateWorkerThread(i, TRUE);
354 DPRINT1("Dynamic threads queued %d\n", Queue->DynamicThreadCount);
355 }
356
357 /* Update our data */
358 Queue->WorkItemsProcessedLastPass = Queue->WorkItemsProcessed;
359 Queue->QueueDepthLastPass = KeReadStateQueue(&Queue->WorkerQueue);
360 }
361 }
362
363 /*++
364 * @name ExpCheckDynamicThreadCount
365 *
366 * The ExpCheckDynamicThreadCount routine checks every queue and creates
367 * a dynamic thread if the queue requires one.
368 *
369 * @param None
370 *
371 * @return None.
372 *
373 * @remarks The algorithm for deciding if a new thread must be created is
374 * documented in the ExQueueWorkItem routine.
375 *
376 *--*/
377 VOID
378 NTAPI
379 ExpCheckDynamicThreadCount(VOID)
380 {
381 ULONG i;
382 PEX_WORK_QUEUE Queue;
383
384 /* Loop the 3 queues */
385 for (i = 0; i < MaximumWorkQueue; i++)
386 {
387 /* Get the queue */
388 Queue = &ExWorkerQueue[i];
389
390 /* Check if still need a new thread. See ExQueueWorkItem */
391 if ((Queue->Info.MakeThreadsAsNecessary) &&
392 (!IsListEmpty(&Queue->WorkerQueue.EntryListHead)) &&
393 (Queue->WorkerQueue.CurrentCount <
394 Queue->WorkerQueue.MaximumCount) &&
395 (Queue->DynamicThreadCount < 16))
396 {
397 /* Create a new thread */
398 DPRINT1("EX: Creating new dynamic thread as requested\n");
399 ExpCreateWorkerThread(i, TRUE);
400 }
401 }
402 }
403
404 /*++
405 * @name ExpWorkerThreadBalanceManager
406 *
407 * The ExpWorkerThreadBalanceManager routine is the entrypoint for the
408 * worker thread balance set manager.
409 *
410 * @param Context
411 * Unused.
412 *
413 * @return None.
414 *
415 * @remarks The worker thread balance set manager listens every second, but can
416 * also be woken up by an event when a new thread is needed, or by the
417 * special shutdown event. This thread runs at priority 7.
418 *
419 * This routine must run at IRQL == PASSIVE_LEVEL.
420 *
421 *--*/
422 VOID
423 NTAPI
424 ExpWorkerThreadBalanceManager(IN PVOID Context)
425 {
426 KTIMER Timer;
427 LARGE_INTEGER Timeout;
428 NTSTATUS Status;
429 PVOID WaitEvents[3];
430 PAGED_CODE();
431 UNREFERENCED_PARAMETER(Context);
432
433 /* Raise our priority above all other worker threads */
434 KeSetBasePriorityThread(KeGetCurrentThread(),
435 EX_CRITICAL_QUEUE_PRIORITY_INCREMENT + 1);
436
437 /* Setup the timer */
438 KeInitializeTimer(&Timer);
439 Timeout.QuadPart = Int32x32To64(-1, 10000000);
440
441 /* We'll wait on the periodic timer and also the emergency event */
442 WaitEvents[0] = &Timer;
443 WaitEvents[1] = &ExpThreadSetManagerEvent;
444 WaitEvents[2] = &ExpThreadSetManagerShutdownEvent;
445
446 /* Start wait loop */
447 for (;;)
448 {
449 /* Wait for the timer */
450 KeSetTimer(&Timer, Timeout, NULL);
451 Status = KeWaitForMultipleObjects(3,
452 WaitEvents,
453 WaitAny,
454 Executive,
455 KernelMode,
456 FALSE,
457 NULL,
458 NULL);
459 if (Status == 0)
460 {
461 /* Our timer expired. Check for deadlocks */
462 ExpDetectWorkerThreadDeadlock();
463 }
464 else if (Status == 1)
465 {
466 /* Someone notified us, verify if we should create a new thread */
467 ExpCheckDynamicThreadCount();
468 }
469 else if (Status == 2)
470 {
471 /* We are shutting down. Cancel the timer */
472 DPRINT1("System shutdown\n");
473 KeCancelTimer(&Timer);
474
475 /* Make sure we have a final thread */
476 ASSERT(ExpLastWorkerThread);
477
478 /* Wait for it */
479 KeWaitForSingleObject(ExpLastWorkerThread,
480 Executive,
481 KernelMode,
482 FALSE,
483 NULL);
484
485 /* Dereference it and kill us */
486 ObDereferenceObject(ExpLastWorkerThread);
487 PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN);
488 }
489
490 // #ifdef _WINKD_
491 /*
492 * If WinDBG wants to attach or kill a user-mode process, and/or
493 * page-in an address region, queue a debugger worker thread.
494 */
495 if (ExpDebuggerWork == WinKdWorkerStart)
496 {
497 ExInitializeWorkItem(&ExpDebuggerWorkItem, ExpDebuggerWorker, NULL);
498 ExpDebuggerWork = WinKdWorkerInitialized;
499 ExQueueWorkItem(&ExpDebuggerWorkItem, DelayedWorkQueue);
500 }
501 // #endif /* _WINKD_ */
502 }
503 }
504
505 /*++
506 * @name ExpInitializeWorkerThreads
507 *
508 * The ExpInitializeWorkerThreads routine initializes worker thread and
509 * work queue support.
510 *
511 * @param None.
512 *
513 * @return None.
514 *
515 * @remarks This routine is only called once during system initialization.
516 *
517 *--*/
518 VOID
519 INIT_FUNCTION
520 NTAPI
521 ExpInitializeWorkerThreads(VOID)
522 {
523 ULONG WorkQueueType;
524 ULONG CriticalThreads, DelayedThreads;
525 HANDLE ThreadHandle;
526 PETHREAD Thread;
527 ULONG i;
528
529 /* Setup the stack swap support */
530 ExInitializeFastMutex(&ExpWorkerSwapinMutex);
531 InitializeListHead(&ExpWorkerListHead);
532 ExpWorkersCanSwap = TRUE;
533
534 /* Set the number of critical and delayed threads. We shouldn't hardcode */
535 DelayedThreads = EX_DELAYED_WORK_THREADS;
536 CriticalThreads = EX_CRITICAL_WORK_THREADS;
537
538 /* Protect against greedy registry modifications */
539 ExpAdditionalDelayedWorkerThreads =
540 min(ExpAdditionalDelayedWorkerThreads, 16);
541 ExpAdditionalCriticalWorkerThreads =
542 min(ExpAdditionalCriticalWorkerThreads, 16);
543
544 /* Calculate final count */
545 DelayedThreads += ExpAdditionalDelayedWorkerThreads;
546 CriticalThreads += ExpAdditionalCriticalWorkerThreads;
547
548 /* Initialize the Array */
549 for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType++)
550 {
551 /* Clear the structure and initialize the queue */
552 RtlZeroMemory(&ExWorkerQueue[WorkQueueType], sizeof(EX_WORK_QUEUE));
553 KeInitializeQueue(&ExWorkerQueue[WorkQueueType].WorkerQueue, 0);
554 }
555
556 /* Dynamic threads are only used for the critical queue */
557 ExWorkerQueue[CriticalWorkQueue].Info.MakeThreadsAsNecessary = TRUE;
558
559 /* Initialize the balance set manager events */
560 KeInitializeEvent(&ExpThreadSetManagerEvent, SynchronizationEvent, FALSE);
561 KeInitializeEvent(&ExpThreadSetManagerShutdownEvent,
562 NotificationEvent,
563 FALSE);
564
565 /* Create the built-in worker threads for the critical queue */
566 for (i = 0; i < CriticalThreads; i++)
567 {
568 /* Create the thread */
569 ExpCreateWorkerThread(CriticalWorkQueue, FALSE);
570 ExCriticalWorkerThreads++;
571 }
572
573 /* Create the built-in worker threads for the delayed queue */
574 for (i = 0; i < DelayedThreads; i++)
575 {
576 /* Create the thread */
577 ExpCreateWorkerThread(DelayedWorkQueue, FALSE);
578 ExDelayedWorkerThreads++;
579 }
580
581 /* Create the built-in worker thread for the hypercritical queue */
582 ExpCreateWorkerThread(HyperCriticalWorkQueue, FALSE);
583
584 /* Create the balance set manager thread */
585 PsCreateSystemThread(&ThreadHandle,
586 THREAD_ALL_ACCESS,
587 NULL,
588 0,
589 NULL,
590 ExpWorkerThreadBalanceManager,
591 NULL);
592
593 /* Get a pointer to it for the shutdown process */
594 ObReferenceObjectByHandle(ThreadHandle,
595 THREAD_ALL_ACCESS,
596 NULL,
597 KernelMode,
598 (PVOID*)&Thread,
599 NULL);
600 ExpWorkerThreadBalanceManagerPtr = Thread;
601
602 /* Close the handle and return */
603 ObCloseHandle(ThreadHandle, KernelMode);
604 }
605
606 VOID
607 NTAPI
608 ExpSetSwappingKernelApc(IN PKAPC Apc,
609 OUT PKNORMAL_ROUTINE *NormalRoutine,
610 IN OUT PVOID *NormalContext,
611 IN OUT PVOID *SystemArgument1,
612 IN OUT PVOID *SystemArgument2)
613 {
614 PBOOLEAN AllowSwap;
615 PKEVENT Event = (PKEVENT)*SystemArgument1;
616
617 /* Make sure it's an active worker */
618 if (PsGetCurrentThread()->ActiveExWorker)
619 {
620 /* Read the setting from the context flag */
621 AllowSwap = (PBOOLEAN)NormalContext;
622 KeSetKernelStackSwapEnable(*AllowSwap);
623 }
624
625 /* Let caller know that we're done */
626 KeSetEvent(Event, 0, FALSE);
627 }
628
629 VOID
630 NTAPI
631 ExSwapinWorkerThreads(IN BOOLEAN AllowSwap)
632 {
633 KEVENT Event;
634 PETHREAD CurrentThread = PsGetCurrentThread(), Thread;
635 PEPROCESS Process = PsInitialSystemProcess;
636 KAPC Apc;
637 PAGED_CODE();
638
639 /* Initialize an event so we know when we're done */
640 KeInitializeEvent(&Event, NotificationEvent, FALSE);
641
642 /* Lock this routine */
643 ExAcquireFastMutex(&ExpWorkerSwapinMutex);
644
645 /* New threads cannot swap anymore */
646 ExpWorkersCanSwap = AllowSwap;
647
648 /* Loop all threads in the system process */
649 Thread = PsGetNextProcessThread(Process, NULL);
650 while (Thread)
651 {
652 /* Skip threads with explicit permission to do this */
653 if (Thread->ExWorkerCanWaitUser) goto Next;
654
655 /* Check if we reached ourselves */
656 if (Thread == CurrentThread)
657 {
658 /* Do it inline */
659 KeSetKernelStackSwapEnable(AllowSwap);
660 }
661 else
662 {
663 /* Queue an APC */
664 KeInitializeApc(&Apc,
665 &Thread->Tcb,
666 InsertApcEnvironment,
667 ExpSetSwappingKernelApc,
668 NULL,
669 NULL,
670 KernelMode,
671 &AllowSwap);
672 if (KeInsertQueueApc(&Apc, &Event, NULL, 3))
673 {
674 /* Wait for the APC to run */
675 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
676 KeClearEvent(&Event);
677 }
678 }
679
680 /* Next thread */
681 Next:
682 Thread = PsGetNextProcessThread(Process, Thread);
683 }
684
685 /* Release the lock */
686 ExReleaseFastMutex(&ExpWorkerSwapinMutex);
687 }
688
689 /* PUBLIC FUNCTIONS **********************************************************/
690
691 /*++
692 * @name ExQueueWorkItem
693 * @implemented NT4
694 *
695 * The ExQueueWorkItem routine acquires rundown protection for
696 * the specified descriptor.
697 *
698 * @param WorkItem
699 * Pointer to an initialized Work Queue Item structure. This structure
700 * must be located in nonpaged pool memory.
701 *
702 * @param QueueType
703 * Type of the queue to use for this item. Can be one of the following:
704 * - DelayedWorkQueue
705 * - CriticalWorkQueue
706 * - HyperCriticalWorkQueue
707 *
708 * @return None.
709 *
710 * @remarks This routine is obsolete. Use IoQueueWorkItem instead.
711 *
712 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
713 *
714 *--*/
715 VOID
716 NTAPI
717 ExQueueWorkItem(IN PWORK_QUEUE_ITEM WorkItem,
718 IN WORK_QUEUE_TYPE QueueType)
719 {
720 PEX_WORK_QUEUE WorkQueue = &ExWorkerQueue[QueueType];
721 ASSERT(QueueType < MaximumWorkQueue);
722 ASSERT(WorkItem->List.Flink == NULL);
723
724 /* Don't try to trick us */
725 if ((ULONG_PTR)WorkItem->WorkerRoutine < MmUserProbeAddress)
726 {
727 /* Bugcheck the system */
728 KeBugCheckEx(WORKER_INVALID,
729 1,
730 (ULONG_PTR)WorkItem,
731 (ULONG_PTR)WorkItem->WorkerRoutine,
732 0);
733 }
734
735 /* Insert the Queue */
736 KeInsertQueue(&WorkQueue->WorkerQueue, &WorkItem->List);
737 ASSERT(!WorkQueue->Info.QueueDisabled);
738
739 /*
740 * Check if we need a new thread. Our decision is as follows:
741 * - This queue type must support Dynamic Threads (duh!)
742 * - It actually has to have unprocessed items
743 * - We have CPUs which could be handling another thread
744 * - We haven't abused our usage of dynamic threads.
745 */
746 if ((WorkQueue->Info.MakeThreadsAsNecessary) &&
747 (!IsListEmpty(&WorkQueue->WorkerQueue.EntryListHead)) &&
748 (WorkQueue->WorkerQueue.CurrentCount <
749 WorkQueue->WorkerQueue.MaximumCount) &&
750 (WorkQueue->DynamicThreadCount < 16))
751 {
752 /* Let the balance manager know about it */
753 DPRINT1("Requesting a new thread. CurrentCount: %lu. MaxCount: %lu\n",
754 WorkQueue->WorkerQueue.CurrentCount,
755 WorkQueue->WorkerQueue.MaximumCount);
756 KeSetEvent(&ExpThreadSetManagerEvent, 0, FALSE);
757 }
758 }
759
760 /* EOF */