[NTOS:EX]
[reactos.git] / reactos / 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 ExpCriticalWorkerThreads;
39 ULONG ExpDelayedWorkerThreads;
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.SpecialApcDisable)
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 Thread->Tcb.SpecialApcDisable = 0;
174 }
175
176 /* Make sure it returned at right IRQL */
177 if (KeGetCurrentIrql() != PASSIVE_LEVEL)
178 {
179 /* It didn't, bugcheck! */
180 KeBugCheckEx(WORKER_THREAD_RETURNED_AT_BAD_IRQL,
181 (ULONG_PTR)WorkItem->WorkerRoutine,
182 KeGetCurrentIrql(),
183 (ULONG_PTR)WorkItem->Parameter,
184 (ULONG_PTR)WorkItem);
185 }
186
187 /* Make sure it returned with Impersionation Disabled */
188 if (Thread->ActiveImpersonationInfo)
189 {
190 /* It didn't, bugcheck! */
191 KeBugCheckEx(IMPERSONATING_WORKER_THREAD,
192 (ULONG_PTR)WorkItem->WorkerRoutine,
193 (ULONG_PTR)WorkItem->Parameter,
194 (ULONG_PTR)WorkItem,
195 0);
196 }
197 }
198
199 /* This is a dynamic thread. Terminate it unless IRPs are pending */
200 if (!IsListEmpty(&Thread->IrpList)) goto ProcessLoop;
201
202 /* Don't terminate it if the queue is disabled either */
203 if (WorkQueue->Info.QueueDisabled) goto ProcessLoop;
204
205 /* Set the worker flags */
206 do
207 {
208 /* Decrease the worker count */
209 OldValue = WorkQueue->Info;
210 NewValue = OldValue;
211 NewValue.WorkerCount--;
212 }
213 while (InterlockedCompareExchange((PLONG)&WorkQueue->Info,
214 *(PLONG)&NewValue,
215 *(PLONG)&OldValue) != *(PLONG)&OldValue);
216
217 /* Decrement dynamic thread count */
218 InterlockedDecrement(&WorkQueue->DynamicThreadCount);
219
220 /* We're not a worker thread anymore */
221 Thread->ActiveExWorker = FALSE;
222
223 /* Re-enable the stack swap */
224 KeSetKernelStackSwapEnable(TRUE);
225 return;
226 }
227
228 /*++
229 * @name ExpCreateWorkerThread
230 *
231 * The ExpCreateWorkerThread routine creates a new worker thread for the
232 * specified queue.
233 **
234 * @param QueueType
235 * Type of the queue to use for this thread. Valid values are:
236 * - DelayedWorkQueue
237 * - CriticalWorkQueue
238 * - HyperCriticalWorkQueue
239 *
240 * @param Dynamic
241 * Specifies whether or not this thread is a dynamic thread.
242 *
243 * @return None.
244 *
245 * @remarks HyperCritical work threads run at priority 7; Critical work threads
246 * run at priority 5, and delayed work threads run at priority 4.
247 *
248 * This, worker threads cannot pre-empty a normal user-mode thread.
249 *
250 *--*/
251 VOID
252 NTAPI
253 ExpCreateWorkerThread(WORK_QUEUE_TYPE WorkQueueType,
254 IN BOOLEAN Dynamic)
255 {
256 PETHREAD Thread;
257 HANDLE hThread;
258 ULONG Context;
259 KPRIORITY Priority;
260
261 /* Check if this is going to be a dynamic thread */
262 Context = WorkQueueType;
263
264 /* Add the dynamic mask */
265 if (Dynamic) Context |= EX_DYNAMIC_WORK_THREAD;
266
267 /* Create the System Thread */
268 PsCreateSystemThread(&hThread,
269 THREAD_ALL_ACCESS,
270 NULL,
271 NULL,
272 NULL,
273 ExpWorkerThreadEntryPoint,
274 UlongToPtr(Context));
275
276 /* If the thread is dynamic */
277 if (Dynamic)
278 {
279 /* Increase the count */
280 InterlockedIncrement(&ExWorkerQueue[WorkQueueType].DynamicThreadCount);
281 }
282
283 /* Set the priority */
284 if (WorkQueueType == DelayedWorkQueue)
285 {
286 /* Priority == 4 */
287 Priority = EX_DELAYED_QUEUE_PRIORITY_INCREMENT;
288 }
289 else if (WorkQueueType == CriticalWorkQueue)
290 {
291 /* Priority == 5 */
292 Priority = EX_CRITICAL_QUEUE_PRIORITY_INCREMENT;
293 }
294 else
295 {
296 /* Priority == 7 */
297 Priority = EX_HYPERCRITICAL_QUEUE_PRIORITY_INCREMENT;
298 }
299
300 /* Get the Thread */
301 ObReferenceObjectByHandle(hThread,
302 THREAD_SET_INFORMATION,
303 PsThreadType,
304 KernelMode,
305 (PVOID*)&Thread,
306 NULL);
307
308 /* Set the Priority */
309 KeSetBasePriorityThread(&Thread->Tcb, Priority);
310
311 /* Dereference and close handle */
312 ObDereferenceObject(Thread);
313 ObCloseHandle(hThread, KernelMode);
314 }
315
316 /*++
317 * @name ExpCheckDynamicThreadCount
318 *
319 * The ExpCheckDynamicThreadCount routine checks every queue and creates a
320 * dynamic thread if the queue seems to be deadlocked.
321 *
322 * @param None
323 *
324 * @return None.
325 *
326 * @remarks The algorithm for deciding if a new thread must be created is
327 * based on wether the queue has processed no new items in the last
328 * second, and new items are still enqueued.
329 *
330 *--*/
331 VOID
332 NTAPI
333 ExpDetectWorkerThreadDeadlock(VOID)
334 {
335 ULONG i;
336 PEX_WORK_QUEUE Queue;
337
338 /* Loop the 3 queues */
339 for (i = 0; i < MaximumWorkQueue; i++)
340 {
341 /* Get the queue */
342 Queue = &ExWorkerQueue[i];
343 ASSERT(Queue->DynamicThreadCount <= 16);
344
345 /* Check if stuff is on the queue that still is unprocessed */
346 if ((Queue->QueueDepthLastPass) &&
347 (Queue->WorkItemsProcessed == Queue->WorkItemsProcessedLastPass) &&
348 (Queue->DynamicThreadCount < 16))
349 {
350 /* Stuff is still on the queue and nobody did anything about it */
351 DPRINT1("EX: Work Queue Deadlock detected: %lu\n", i);
352 ExpCreateWorkerThread(i, TRUE);
353 DPRINT1("Dynamic threads queued %d\n", Queue->DynamicThreadCount);
354 }
355
356 /* Update our data */
357 Queue->WorkItemsProcessedLastPass = Queue->WorkItemsProcessed;
358 Queue->QueueDepthLastPass = KeReadStateQueue(&Queue->WorkerQueue);
359 }
360 }
361
362 /*++
363 * @name ExpCheckDynamicThreadCount
364 *
365 * The ExpCheckDynamicThreadCount routine checks every queue and creates a
366 * dynamic thread if the queue requires one.
367 *
368 * @param None
369 *
370 * @return None.
371 *
372 * @remarks The algorithm for deciding if a new thread must be created is
373 * documented in the ExQueueWorkItem routine.
374 *
375 *--*/
376 VOID
377 NTAPI
378 ExpCheckDynamicThreadCount(VOID)
379 {
380 ULONG i;
381 PEX_WORK_QUEUE Queue;
382
383 /* Loop the 3 queues */
384 for (i = 0; i < MaximumWorkQueue; i++)
385 {
386 /* Get the queue */
387 Queue = &ExWorkerQueue[i];
388
389 /* Check if still need a new thread. See ExQueueWorkItem */
390 if ((Queue->Info.MakeThreadsAsNecessary) &&
391 (!IsListEmpty(&Queue->WorkerQueue.EntryListHead)) &&
392 (Queue->WorkerQueue.CurrentCount <
393 Queue->WorkerQueue.MaximumCount) &&
394 (Queue->DynamicThreadCount < 16))
395 {
396 /* Create a new thread */
397 DPRINT1("EX: Creating new dynamic thread as requested\n");
398 ExpCreateWorkerThread(i, TRUE);
399 }
400 }
401 }
402
403 /*++
404 * @name ExpWorkerThreadBalanceManager
405 *
406 * The ExpWorkerThreadBalanceManager routine is the entrypoint for the
407 * worker thread balance set manager.
408 *
409 * @param Context
410 * Unused.
411 *
412 * @return None.
413 *
414 * @remarks The worker thread balance set manager listens every second, but can
415 * also be woken up by an event when a new thread is needed, or by the
416 * special shutdown event. This thread runs at priority 7.
417 *
418 * This routine must run at IRQL == PASSIVE_LEVEL.
419 *
420 *--*/
421 VOID
422 NTAPI
423 ExpWorkerThreadBalanceManager(IN PVOID Context)
424 {
425 KTIMER Timer;
426 LARGE_INTEGER Timeout;
427 NTSTATUS Status;
428 PVOID WaitEvents[3];
429 PAGED_CODE();
430 UNREFERENCED_PARAMETER(Context);
431
432 /* Raise our priority above all other worker threads */
433 KeSetBasePriorityThread(KeGetCurrentThread(),
434 EX_CRITICAL_QUEUE_PRIORITY_INCREMENT + 1);
435
436 /* Setup the timer */
437 KeInitializeTimer(&Timer);
438 Timeout.QuadPart = Int32x32To64(-1, 10000000);
439
440 /* We'll wait on the periodic timer and also the emergency event */
441 WaitEvents[0] = &Timer;
442 WaitEvents[1] = &ExpThreadSetManagerEvent;
443 WaitEvents[2] = &ExpThreadSetManagerShutdownEvent;
444
445 /* Start wait loop */
446 for (;;)
447 {
448 /* Wait for the timer */
449 KeSetTimer(&Timer, Timeout, NULL);
450 Status = KeWaitForMultipleObjects(3,
451 WaitEvents,
452 WaitAny,
453 Executive,
454 KernelMode,
455 FALSE,
456 NULL,
457 NULL);
458 if (Status == 0)
459 {
460 /* Our timer expired. Check for deadlocks */
461 ExpDetectWorkerThreadDeadlock();
462 }
463 else if (Status == 1)
464 {
465 /* Someone notified us, verify if we should create a new thread */
466 ExpCheckDynamicThreadCount();
467 }
468 else if (Status == 2)
469 {
470 /* We are shutting down. Cancel the timer */
471 DPRINT1("System shutdown\n");
472 KeCancelTimer(&Timer);
473
474 /* Make sure we have a final thread */
475 ASSERT(ExpLastWorkerThread);
476
477 /* Wait for it */
478 KeWaitForSingleObject(ExpLastWorkerThread,
479 Executive,
480 KernelMode,
481 FALSE,
482 NULL);
483
484 /* Dereference it and kill us */
485 ObDereferenceObject(ExpLastWorkerThread);
486 PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN);
487 }
488
489 // #ifdef _WINKD_
490 /*
491 * If WinDBG wants to attach or kill a user-mode process, and/or
492 * page-in an address region, queue a debugger worker thread.
493 */
494 if (ExpDebuggerWork == WinKdWorkerActivate)
495 {
496 ExInitializeWorkItem(&ExpDebuggerWorkItem, ExpDebuggerWorker, NULL);
497 ExpDebuggerWork = WinKdWorkerInitialized;
498 ExQueueWorkItem(&ExpDebuggerWorkItem, DelayedWorkQueue);
499 }
500 // #endif /* _WINKD_ */
501 }
502 }
503
504 /*++
505 * @name ExpInitializeWorkerThreads
506 *
507 * The ExpInitializeWorkerThreads routine initializes worker thread and
508 * work queue support.
509 *
510 * @param None.
511 *
512 * @return None.
513 *
514 * @remarks This routine is only called once during system initialization.
515 *
516 *--*/
517 VOID
518 INIT_FUNCTION
519 NTAPI
520 ExpInitializeWorkerThreads(VOID)
521 {
522 ULONG WorkQueueType;
523 ULONG CriticalThreads, DelayedThreads;
524 HANDLE ThreadHandle;
525 PETHREAD Thread;
526 ULONG i;
527
528 /* Setup the stack swap support */
529 ExInitializeFastMutex(&ExpWorkerSwapinMutex);
530 InitializeListHead(&ExpWorkerListHead);
531 ExpWorkersCanSwap = TRUE;
532
533 /* Set the number of critical and delayed threads. We shouldn't hardcode */
534 DelayedThreads = EX_DELAYED_WORK_THREADS;
535 CriticalThreads = EX_CRITICAL_WORK_THREADS;
536
537 /* Protect against greedy registry modifications */
538 ExpAdditionalDelayedWorkerThreads =
539 min(ExpAdditionalDelayedWorkerThreads, 16);
540 ExpAdditionalCriticalWorkerThreads =
541 min(ExpAdditionalCriticalWorkerThreads, 16);
542
543 /* Calculate final count */
544 DelayedThreads += ExpAdditionalDelayedWorkerThreads;
545 CriticalThreads += ExpAdditionalCriticalWorkerThreads;
546
547 /* Initialize the Array */
548 for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType++)
549 {
550 /* Clear the structure and initialize the queue */
551 RtlZeroMemory(&ExWorkerQueue[WorkQueueType], sizeof(EX_WORK_QUEUE));
552 KeInitializeQueue(&ExWorkerQueue[WorkQueueType].WorkerQueue, 0);
553 }
554
555 /* Dynamic threads are only used for the critical queue */
556 ExWorkerQueue[CriticalWorkQueue].Info.MakeThreadsAsNecessary = TRUE;
557
558 /* Initialize the balance set manager events */
559 KeInitializeEvent(&ExpThreadSetManagerEvent, SynchronizationEvent, FALSE);
560 KeInitializeEvent(&ExpThreadSetManagerShutdownEvent,
561 NotificationEvent,
562 FALSE);
563
564 /* Create the built-in worker threads for the critical queue */
565 for (i = 0; i < CriticalThreads; i++)
566 {
567 /* Create the thread */
568 ExpCreateWorkerThread(CriticalWorkQueue, FALSE);
569 ExpCriticalWorkerThreads++;
570 }
571
572 /* Create the built-in worker threads for the delayed queue */
573 for (i = 0; i < DelayedThreads; i++)
574 {
575 /* Create the thread */
576 ExpCreateWorkerThread(DelayedWorkQueue, FALSE);
577 ExpDelayedWorkerThreads++;
578 }
579
580 /* Create the built-in worker thread for the hypercritical queue */
581 ExpCreateWorkerThread(HyperCriticalWorkQueue, FALSE);
582
583 /* Create the balance set manager thread */
584 PsCreateSystemThread(&ThreadHandle,
585 THREAD_ALL_ACCESS,
586 NULL,
587 0,
588 NULL,
589 ExpWorkerThreadBalanceManager,
590 NULL);
591
592 /* Get a pointer to it for the shutdown process */
593 ObReferenceObjectByHandle(ThreadHandle,
594 THREAD_ALL_ACCESS,
595 NULL,
596 KernelMode,
597 (PVOID*)&Thread,
598 NULL);
599 ExpWorkerThreadBalanceManagerPtr = Thread;
600
601 /* Close the handle and return */
602 ObCloseHandle(ThreadHandle, KernelMode);
603 }
604
605 VOID
606 NTAPI
607 ExpSetSwappingKernelApc(IN PKAPC Apc,
608 OUT PKNORMAL_ROUTINE *NormalRoutine,
609 IN OUT PVOID *NormalContext,
610 IN OUT PVOID *SystemArgument1,
611 IN OUT PVOID *SystemArgument2)
612 {
613 PBOOLEAN AllowSwap;
614 PKEVENT Event = (PKEVENT)*SystemArgument1;
615
616 /* Make sure it's an active worker */
617 if (PsGetCurrentThread()->ActiveExWorker)
618 {
619 /* Read the setting from the context flag */
620 AllowSwap = (PBOOLEAN)NormalContext;
621 KeSetKernelStackSwapEnable(*AllowSwap);
622 }
623
624 /* Let caller know that we're done */
625 KeSetEvent(Event, 0, FALSE);
626 }
627
628 VOID
629 NTAPI
630 ExSwapinWorkerThreads(IN BOOLEAN AllowSwap)
631 {
632 KEVENT Event;
633 PETHREAD CurrentThread = PsGetCurrentThread(), Thread;
634 PEPROCESS Process = PsInitialSystemProcess;
635 KAPC Apc;
636 PAGED_CODE();
637
638 /* Initialize an event so we know when we're done */
639 KeInitializeEvent(&Event, NotificationEvent, FALSE);
640
641 /* Lock this routine */
642 ExAcquireFastMutex(&ExpWorkerSwapinMutex);
643
644 /* New threads cannot swap anymore */
645 ExpWorkersCanSwap = AllowSwap;
646
647 /* Loop all threads in the system process */
648 Thread = PsGetNextProcessThread(Process, NULL);
649 while (Thread)
650 {
651 /* Skip threads with explicit permission to do this */
652 if (Thread->ExWorkerCanWaitUser) goto Next;
653
654 /* Check if we reached ourselves */
655 if (Thread == CurrentThread)
656 {
657 /* Do it inline */
658 KeSetKernelStackSwapEnable(AllowSwap);
659 }
660 else
661 {
662 /* Queue an APC */
663 KeInitializeApc(&Apc,
664 &Thread->Tcb,
665 InsertApcEnvironment,
666 ExpSetSwappingKernelApc,
667 NULL,
668 NULL,
669 KernelMode,
670 &AllowSwap);
671 if (KeInsertQueueApc(&Apc, &Event, NULL, 3))
672 {
673 /* Wait for the APC to run */
674 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
675 KeClearEvent(&Event);
676 }
677 }
678
679 /* Next thread */
680 Next:
681 Thread = PsGetNextProcessThread(Process, Thread);
682 }
683
684 /* Release the lock */
685 ExReleaseFastMutex(&ExpWorkerSwapinMutex);
686 }
687
688 /* PUBLIC FUNCTIONS **********************************************************/
689
690 /*++
691 * @name ExQueueWorkItem
692 * @implemented NT4
693 *
694 * The ExQueueWorkItem routine acquires rundown protection for
695 * the specified descriptor.
696 *
697 * @param WorkItem
698 * Pointer to an initialized Work Queue Item structure. This structure
699 * must be located in nonpaged pool memory.
700 *
701 * @param QueueType
702 * Type of the queue to use for this item. Can be one of the following:
703 * - DelayedWorkQueue
704 * - CriticalWorkQueue
705 * - HyperCriticalWorkQueue
706 *
707 * @return None.
708 *
709 * @remarks This routine is obsolete. Use IoQueueWorkItem instead.
710 *
711 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
712 *
713 *--*/
714 VOID
715 NTAPI
716 ExQueueWorkItem(IN PWORK_QUEUE_ITEM WorkItem,
717 IN WORK_QUEUE_TYPE QueueType)
718 {
719 PEX_WORK_QUEUE WorkQueue = &ExWorkerQueue[QueueType];
720 ASSERT(QueueType < MaximumWorkQueue);
721 ASSERT(WorkItem->List.Flink == NULL);
722
723 /* Don't try to trick us */
724 if ((ULONG_PTR)WorkItem->WorkerRoutine < MmUserProbeAddress)
725 {
726 /* Bugcheck the system */
727 KeBugCheckEx(WORKER_INVALID,
728 1,
729 (ULONG_PTR)WorkItem,
730 (ULONG_PTR)WorkItem->WorkerRoutine,
731 0);
732 }
733
734 /* Insert the Queue */
735 KeInsertQueue(&WorkQueue->WorkerQueue, &WorkItem->List);
736 ASSERT(!WorkQueue->Info.QueueDisabled);
737
738 /*
739 * Check if we need a new thread. Our decision is as follows:
740 * - This queue type must support Dynamic Threads (duh!)
741 * - It actually has to have unprocessed items
742 * - We have CPUs which could be handling another thread
743 * - We haven't abused our usage of dynamic threads.
744 */
745 if ((WorkQueue->Info.MakeThreadsAsNecessary) &&
746 (!IsListEmpty(&WorkQueue->WorkerQueue.EntryListHead)) &&
747 (WorkQueue->WorkerQueue.CurrentCount <
748 WorkQueue->WorkerQueue.MaximumCount) &&
749 (WorkQueue->DynamicThreadCount < 16))
750 {
751 /* Let the balance manager know about it */
752 DPRINT1("Requesting a new thread. CurrentCount: %lu. MaxCount: %lu\n",
753 WorkQueue->WorkerQueue.CurrentCount,
754 WorkQueue->WorkerQueue.MaximumCount);
755 KeSetEvent(&ExpThreadSetManagerEvent, 0, FALSE);
756 }
757 }
758
759 /* EOF */