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 */ 761