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