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 /* 487 * If WinDBG wants to attach or kill a user-mode process, and/or 488 * page-in an address region, queue a debugger worker thread. 489 */ 490 if (ExpDebuggerWork == WinKdWorkerStart) 491 { 492 ExInitializeWorkItem(&ExpDebuggerWorkItem, ExpDebuggerWorker, NULL); 493 ExpDebuggerWork = WinKdWorkerInitialized; 494 ExQueueWorkItem(&ExpDebuggerWorkItem, DelayedWorkQueue); 495 } 496 } 497 } 498 499 /*++ 500 * @name ExpInitializeWorkerThreads 501 * 502 * The ExpInitializeWorkerThreads routine initializes worker thread and 503 * work queue support. 504 * 505 * @param None. 506 * 507 * @return None. 508 * 509 * @remarks This routine is only called once during system initialization. 510 * 511 *--*/ 512 CODE_SEG("INIT") 513 VOID 514 NTAPI 515 ExpInitializeWorkerThreads(VOID) 516 { 517 ULONG WorkQueueType; 518 ULONG CriticalThreads, DelayedThreads; 519 HANDLE ThreadHandle; 520 PETHREAD Thread; 521 ULONG i; 522 523 /* Setup the stack swap support */ 524 ExInitializeFastMutex(&ExpWorkerSwapinMutex); 525 InitializeListHead(&ExpWorkerListHead); 526 ExpWorkersCanSwap = TRUE; 527 528 /* Set the number of critical and delayed threads. We shouldn't hardcode */ 529 DelayedThreads = EX_DELAYED_WORK_THREADS; 530 CriticalThreads = EX_CRITICAL_WORK_THREADS; 531 532 /* Protect against greedy registry modifications */ 533 ExpAdditionalDelayedWorkerThreads = 534 min(ExpAdditionalDelayedWorkerThreads, 16); 535 ExpAdditionalCriticalWorkerThreads = 536 min(ExpAdditionalCriticalWorkerThreads, 16); 537 538 /* Calculate final count */ 539 DelayedThreads += ExpAdditionalDelayedWorkerThreads; 540 CriticalThreads += ExpAdditionalCriticalWorkerThreads; 541 542 /* Initialize the Array */ 543 for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType++) 544 { 545 /* Clear the structure and initialize the queue */ 546 RtlZeroMemory(&ExWorkerQueue[WorkQueueType], sizeof(EX_WORK_QUEUE)); 547 KeInitializeQueue(&ExWorkerQueue[WorkQueueType].WorkerQueue, 0); 548 } 549 550 /* Dynamic threads are only used for the critical queue */ 551 ExWorkerQueue[CriticalWorkQueue].Info.MakeThreadsAsNecessary = TRUE; 552 553 /* Initialize the balance set manager events */ 554 KeInitializeEvent(&ExpThreadSetManagerEvent, SynchronizationEvent, FALSE); 555 KeInitializeEvent(&ExpThreadSetManagerShutdownEvent, 556 NotificationEvent, 557 FALSE); 558 559 /* Create the built-in worker threads for the critical queue */ 560 for (i = 0; i < CriticalThreads; i++) 561 { 562 /* Create the thread */ 563 ExpCreateWorkerThread(CriticalWorkQueue, FALSE); 564 ExCriticalWorkerThreads++; 565 } 566 567 /* Create the built-in worker threads for the delayed queue */ 568 for (i = 0; i < DelayedThreads; i++) 569 { 570 /* Create the thread */ 571 ExpCreateWorkerThread(DelayedWorkQueue, FALSE); 572 ExDelayedWorkerThreads++; 573 } 574 575 /* Create the built-in worker thread for the hypercritical queue */ 576 ExpCreateWorkerThread(HyperCriticalWorkQueue, FALSE); 577 578 /* Create the balance set manager thread */ 579 PsCreateSystemThread(&ThreadHandle, 580 THREAD_ALL_ACCESS, 581 NULL, 582 0, 583 NULL, 584 ExpWorkerThreadBalanceManager, 585 NULL); 586 587 /* Get a pointer to it for the shutdown process */ 588 ObReferenceObjectByHandle(ThreadHandle, 589 THREAD_ALL_ACCESS, 590 NULL, 591 KernelMode, 592 (PVOID*)&Thread, 593 NULL); 594 ExpWorkerThreadBalanceManagerPtr = Thread; 595 596 /* Close the handle and return */ 597 ObCloseHandle(ThreadHandle, KernelMode); 598 } 599 600 VOID 601 NTAPI 602 ExpSetSwappingKernelApc(IN PKAPC Apc, 603 OUT PKNORMAL_ROUTINE *NormalRoutine, 604 IN OUT PVOID *NormalContext, 605 IN OUT PVOID *SystemArgument1, 606 IN OUT PVOID *SystemArgument2) 607 { 608 PBOOLEAN AllowSwap; 609 PKEVENT Event = (PKEVENT)*SystemArgument1; 610 611 /* Make sure it's an active worker */ 612 if (PsGetCurrentThread()->ActiveExWorker) 613 { 614 /* Read the setting from the context flag */ 615 AllowSwap = (PBOOLEAN)NormalContext; 616 KeSetKernelStackSwapEnable(*AllowSwap); 617 } 618 619 /* Let caller know that we're done */ 620 KeSetEvent(Event, 0, FALSE); 621 } 622 623 VOID 624 NTAPI 625 ExSwapinWorkerThreads(IN BOOLEAN AllowSwap) 626 { 627 KEVENT Event; 628 PETHREAD CurrentThread = PsGetCurrentThread(), Thread; 629 PEPROCESS Process = PsInitialSystemProcess; 630 KAPC Apc; 631 PAGED_CODE(); 632 633 /* Initialize an event so we know when we're done */ 634 KeInitializeEvent(&Event, NotificationEvent, FALSE); 635 636 /* Lock this routine */ 637 ExAcquireFastMutex(&ExpWorkerSwapinMutex); 638 639 /* New threads cannot swap anymore */ 640 ExpWorkersCanSwap = AllowSwap; 641 642 /* Loop all threads in the system process */ 643 Thread = PsGetNextProcessThread(Process, NULL); 644 while (Thread) 645 { 646 /* Skip threads with explicit permission to do this */ 647 if (Thread->ExWorkerCanWaitUser) goto Next; 648 649 /* Check if we reached ourselves */ 650 if (Thread == CurrentThread) 651 { 652 /* Do it inline */ 653 KeSetKernelStackSwapEnable(AllowSwap); 654 } 655 else 656 { 657 /* Queue an APC */ 658 KeInitializeApc(&Apc, 659 &Thread->Tcb, 660 InsertApcEnvironment, 661 ExpSetSwappingKernelApc, 662 NULL, 663 NULL, 664 KernelMode, 665 &AllowSwap); 666 if (KeInsertQueueApc(&Apc, &Event, NULL, 3)) 667 { 668 /* Wait for the APC to run */ 669 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 670 KeClearEvent(&Event); 671 } 672 } 673 674 /* Next thread */ 675 Next: 676 Thread = PsGetNextProcessThread(Process, Thread); 677 } 678 679 /* Release the lock */ 680 ExReleaseFastMutex(&ExpWorkerSwapinMutex); 681 } 682 683 /* PUBLIC FUNCTIONS **********************************************************/ 684 685 /*++ 686 * @name ExQueueWorkItem 687 * @implemented NT4 688 * 689 * The ExQueueWorkItem routine acquires rundown protection for 690 * the specified descriptor. 691 * 692 * @param WorkItem 693 * Pointer to an initialized Work Queue Item structure. This structure 694 * must be located in nonpaged pool memory. 695 * 696 * @param QueueType 697 * Type of the queue to use for this item. Can be one of the following: 698 * - DelayedWorkQueue 699 * - CriticalWorkQueue 700 * - HyperCriticalWorkQueue 701 * 702 * @return None. 703 * 704 * @remarks This routine is obsolete. Use IoQueueWorkItem instead. 705 * 706 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 707 * 708 *--*/ 709 VOID 710 NTAPI 711 ExQueueWorkItem(IN PWORK_QUEUE_ITEM WorkItem, 712 IN WORK_QUEUE_TYPE QueueType) 713 { 714 PEX_WORK_QUEUE WorkQueue = &ExWorkerQueue[QueueType]; 715 ASSERT(QueueType < MaximumWorkQueue); 716 ASSERT(WorkItem->List.Flink == NULL); 717 718 /* Don't try to trick us */ 719 if ((ULONG_PTR)WorkItem->WorkerRoutine < MmUserProbeAddress) 720 { 721 /* Bugcheck the system */ 722 KeBugCheckEx(WORKER_INVALID, 723 1, 724 (ULONG_PTR)WorkItem, 725 (ULONG_PTR)WorkItem->WorkerRoutine, 726 0); 727 } 728 729 /* Insert the Queue */ 730 KeInsertQueue(&WorkQueue->WorkerQueue, &WorkItem->List); 731 ASSERT(!WorkQueue->Info.QueueDisabled); 732 733 /* 734 * Check if we need a new thread. Our decision is as follows: 735 * - This queue type must support Dynamic Threads (duh!) 736 * - It actually has to have unprocessed items 737 * - We have CPUs which could be handling another thread 738 * - We haven't abused our usage of dynamic threads. 739 */ 740 if ((WorkQueue->Info.MakeThreadsAsNecessary) && 741 (!IsListEmpty(&WorkQueue->WorkerQueue.EntryListHead)) && 742 (WorkQueue->WorkerQueue.CurrentCount < 743 WorkQueue->WorkerQueue.MaximumCount) && 744 (WorkQueue->DynamicThreadCount < 16)) 745 { 746 /* Let the balance manager know about it */ 747 DPRINT1("Requesting a new thread. CurrentCount: %lu. MaxCount: %lu\n", 748 WorkQueue->WorkerQueue.CurrentCount, 749 WorkQueue->WorkerQueue.MaximumCount); 750 KeSetEvent(&ExpThreadSetManagerEvent, 0, FALSE); 751 } 752 } 753 754 /* EOF */ 755