Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1890 lines
50 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. worker.c
  5. Abstract:
  6. This module defines functions for the worker thread pool.
  7. Author:
  8. Gurdeep Singh Pall (gurdeep) Nov 13, 1997
  9. Revision History:
  10. lokeshs - extended/modified threadpool.
  11. Rob Earhart (earhart) September 29, 2000
  12. Split off from threads.c
  13. Environment:
  14. These routines are statically linked in the caller's executable
  15. and are callable only from user mode. They make use of Nt system
  16. services.
  17. --*/
  18. #include <ntos.h>
  19. #include <ntrtl.h>
  20. #include <wow64t.h>
  21. #include "ntrtlp.h"
  22. #include "threads.h"
  23. // Worker Thread Pool
  24. // ------------------
  25. // Clients can submit functions to be executed by a worker thread. Threads are
  26. // created if the work queue exceeds a threshold. Clients can request that the
  27. // function be invoked in the context of a I/O thread. I/O worker threads
  28. // can be used for initiating asynchronous I/O requests. They are not terminated if
  29. // there are pending IO requests. Worker threads terminate if inactivity exceeds a
  30. // threshold.
  31. // Clients can also associate IO completion requests with the IO completion port
  32. // waited upon by the non I/O worker threads. One should not post overlapped IO requests
  33. // in worker threads.
  34. ULONG StartedWorkerInitialization ; // Used for Worker thread startup synchronization
  35. ULONG CompletedWorkerInitialization ; // Used to check if Worker thread pool is initialized
  36. ULONG NumFutureWorkItems = 0 ; // Future work items (timers, waits, &c to exec in workers)
  37. ULONG NumFutureIOWorkItems = 0 ; // Future IO work items (timers, waits, &c to exec in IO workers)
  38. ULONG NumIOWorkerThreads ; // Count of IO Worker Threads alive
  39. ULONG NumWorkerThreads ; // Count of Worker Threads alive
  40. ULONG NumMinWorkerThreads ; // Min worker threads should be alive: 1 if ioCompletion used, else 0
  41. ULONG NumIOWorkRequests ; // Count of IO Work Requests pending
  42. ULONG NumLongIOWorkRequests ; // IO Worker threads executing long worker functions
  43. ULONG NumWorkRequests ; // Count of Work Requests pending.
  44. ULONG NumQueuedWorkRequests; // Count of work requests pending on IO completion
  45. ULONG NumLongWorkRequests ; // Worker threads executing long worker functions
  46. ULONG NumExecutingWorkerThreads ; // Worker threads currently executing worker functions
  47. ULONG TotalExecutedWorkRequests ; // Total worker requests that were picked up
  48. ULONG OldTotalExecutedWorkRequests ; // Total worker requests since last timeout.
  49. HANDLE WorkerThreadTimerQueue = NULL ; // Timer queue used by worker threads
  50. HANDLE WorkerThreadTimer = NULL ; // Timer used by worker threads
  51. RTL_CRITICAL_SECTION WorkerTimerCriticalSection; // Synchronizes access to the worker timer
  52. ULONG LastThreadCreationTickCount ; // Tick count at which the last thread was created
  53. LIST_ENTRY IOWorkerThreads ; // List of IOWorkerThreads
  54. PRTLP_IOWORKER_TCB PersistentIOTCB ; // ptr to TCB of persistest IO worker thread
  55. HANDLE WorkerCompletionPort ; // Completion port used for queuing tasks to Worker threads
  56. RTL_CRITICAL_SECTION WorkerCriticalSection ; // Exclusion used by worker threads
  57. NTSTATUS
  58. RtlpStartWorkerThread (
  59. VOID
  60. );
  61. VOID
  62. RtlpWorkerThreadCancelTimer(
  63. VOID
  64. )
  65. {
  66. if (! RtlTryEnterCriticalSection(&WorkerTimerCriticalSection)) {
  67. //
  68. // Either another thread is setting a timer, or clearing it.
  69. // Either way, there's no reason for us to clear the timer --
  70. // return immediately.
  71. //
  72. return;
  73. }
  74. __try {
  75. if (WorkerThreadTimer) {
  76. ASSERT(WorkerThreadTimerQueue);
  77. RtlDeleteTimer(WorkerThreadTimerQueue,
  78. WorkerThreadTimer,
  79. NULL);
  80. }
  81. } __finally {
  82. WorkerThreadTimer = NULL;
  83. RtlLeaveCriticalSection(&WorkerTimerCriticalSection);
  84. }
  85. }
  86. VOID
  87. RtlpWorkerThreadTimerCallback(
  88. PVOID Context,
  89. BOOLEAN NotUsed
  90. )
  91. /*++
  92. Routine Description:
  93. This routine checks if new worker thread has to be created
  94. Arguments:
  95. None
  96. Return Value:
  97. None
  98. --*/
  99. {
  100. IO_COMPLETION_BASIC_INFORMATION Info ;
  101. BOOLEAN bCreateThread = FALSE ;
  102. NTSTATUS Status ;
  103. ULONG QueueLength, Threshold ;
  104. Status = NtQueryIoCompletion(
  105. WorkerCompletionPort,
  106. IoCompletionBasicInformation,
  107. &Info,
  108. sizeof(Info),
  109. NULL
  110. ) ;
  111. if (!NT_SUCCESS(Status))
  112. return ;
  113. QueueLength = Info.Depth ;
  114. if (!QueueLength) {
  115. OldTotalExecutedWorkRequests = TotalExecutedWorkRequests ;
  116. return ;
  117. }
  118. RtlEnterCriticalSection (&WorkerCriticalSection) ;
  119. // if there are queued work items and no new work items have been scheduled
  120. // in the last 30 seconds then create a new thread.
  121. // this will take care of deadlocks.
  122. // this will create a problem only if some thread is running for a long time
  123. if (TotalExecutedWorkRequests == OldTotalExecutedWorkRequests) {
  124. bCreateThread = TRUE ;
  125. }
  126. // if there are a lot of queued work items, then create a new thread
  127. {
  128. ULONG NumEffWorkerThreads = NumWorkerThreads > NumLongWorkRequests
  129. ? NumWorkerThreads - NumLongWorkRequests
  130. : 0;
  131. ULONG ShortWorkRequests ;
  132. ULONG CapturedNumExecutingWorkerThreads;
  133. ULONG ThreadCreationDampingTime = NumWorkerThreads < NEW_THREAD_THRESHOLD
  134. ? THREAD_CREATION_DAMPING_TIME1
  135. : (NumWorkerThreads < 30
  136. ? THREAD_CREATION_DAMPING_TIME2
  137. : (NumWorkerThreads << 13)); // *100ms
  138. Threshold = (NumWorkerThreads < MAX_WORKER_THREADS
  139. ? (NumEffWorkerThreads < 7
  140. ? NumEffWorkerThreads*NumEffWorkerThreads
  141. : ((NumEffWorkerThreads<40)
  142. ? NEW_THREAD_THRESHOLD * NumEffWorkerThreads
  143. : NEW_THREAD_THRESHOLD2 * NumEffWorkerThreads))
  144. : 0xffffffff) ;
  145. CapturedNumExecutingWorkerThreads = NumExecutingWorkerThreads;
  146. ShortWorkRequests = QueueLength + CapturedNumExecutingWorkerThreads > NumLongWorkRequests
  147. ? QueueLength + CapturedNumExecutingWorkerThreads - NumLongWorkRequests
  148. : 0;
  149. if (LastThreadCreationTickCount > NtGetTickCount())
  150. LastThreadCreationTickCount = NtGetTickCount() ;
  151. if (ShortWorkRequests > Threshold
  152. && (LastThreadCreationTickCount + ThreadCreationDampingTime
  153. < NtGetTickCount()))
  154. {
  155. bCreateThread = TRUE ;
  156. }
  157. }
  158. if (bCreateThread && NumWorkerThreads<MaxThreads) {
  159. RtlpStartWorkerThread();
  160. RtlpWorkerThreadCancelTimer();
  161. }
  162. OldTotalExecutedWorkRequests = TotalExecutedWorkRequests ;
  163. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  164. }
  165. NTSTATUS
  166. RtlpWorkerThreadSetTimer(
  167. VOID
  168. )
  169. {
  170. NTSTATUS Status;
  171. HANDLE NewTimerQueue;
  172. HANDLE NewTimer;
  173. Status = STATUS_SUCCESS;
  174. if (! RtlTryEnterCriticalSection(&WorkerTimerCriticalSection)) {
  175. //
  176. // Either another thread is setting a timer, or clearing it.
  177. // Either way, there's no reason for us to set the timer --
  178. // return immediately.
  179. //
  180. return STATUS_SUCCESS;
  181. }
  182. __try {
  183. if (! WorkerThreadTimerQueue) {
  184. Status = RtlCreateTimerQueue(&NewTimerQueue);
  185. if (! NT_SUCCESS(Status)) {
  186. __leave;
  187. }
  188. WorkerThreadTimerQueue = NewTimerQueue;
  189. }
  190. ASSERT(WorkerThreadTimerQueue != NULL);
  191. if (! WorkerThreadTimer) {
  192. Status = RtlCreateTimer(
  193. WorkerThreadTimerQueue,
  194. &NewTimer,
  195. RtlpWorkerThreadTimerCallback,
  196. NULL,
  197. 60000,
  198. 60000,
  199. WT_EXECUTEINTIMERTHREAD
  200. ) ;
  201. if (! NT_SUCCESS(Status)) {
  202. __leave;
  203. }
  204. WorkerThreadTimer = NewTimer;
  205. }
  206. } __finally {
  207. RtlLeaveCriticalSection(&WorkerTimerCriticalSection);
  208. }
  209. return Status;
  210. }
  211. #if _MSC_FULL_VER >= 13008827
  212. #pragma warning(push)
  213. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  214. #endif
  215. LONG
  216. RtlpWorkerThread (
  217. PVOID Parameter
  218. )
  219. /*++
  220. Routine Description:
  221. All non I/O worker threads execute in this routine. Worker thread will try to
  222. terminate when it has not serviced a request for
  223. STARTING_WORKER_SLEEP_TIME +
  224. STARTING_WORKER_SLEEP_TIME << 1 +
  225. ...
  226. STARTING_WORKER_SLEEP_TIME << MAX_WORKER_SLEEP_TIME_EXPONENT
  227. Arguments:
  228. HandlePtr - Pointer to our handle.
  229. N.B. This is closed by RtlpStartWorkerThread, but
  230. we're still responsible for the memory.
  231. Return Value:
  232. --*/
  233. {
  234. NTSTATUS Status ;
  235. PVOID WorkerProc ;
  236. PVOID Context ;
  237. IO_STATUS_BLOCK IoSb ;
  238. ULONG SleepTime ;
  239. LARGE_INTEGER TimeOut ;
  240. ULONG Terminate ;
  241. PVOID Overlapped ;
  242. UNREFERENCED_PARAMETER(Parameter);
  243. #if DBG
  244. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  245. RTLP_THREADPOOL_TRACE_MASK,
  246. "Starting worker thread\n");
  247. #endif
  248. // Set default sleep time for 40 seconds.
  249. #define WORKER_IDLE_TIMEOUT 40000 // In Milliseconds
  250. SleepTime = WORKER_IDLE_TIMEOUT ;
  251. // Loop servicing I/O completion requests
  252. for ( ; ; ) {
  253. TimeOut.QuadPart = Int32x32To64( SleepTime, -10000 ) ;
  254. Status = NtRemoveIoCompletion(
  255. WorkerCompletionPort,
  256. (PVOID) &WorkerProc,
  257. &Overlapped,
  258. &IoSb,
  259. &TimeOut
  260. ) ;
  261. if (Status == STATUS_SUCCESS) {
  262. TotalExecutedWorkRequests ++ ;//interlocked op not req
  263. InterlockedIncrement(&NumExecutingWorkerThreads) ;
  264. // Call the work item.
  265. // If IO APC, context1 contains number of IO bytes transferred, and context2
  266. // contains the overlapped structure.
  267. // If (IO)WorkerFunction, context1 contains the actual WorkerFunction to be
  268. // executed and context2 contains the actual context
  269. Context = (PVOID) IoSb.Information ;
  270. RtlpApcCallout(WorkerProc,
  271. IoSb.Status,
  272. Context,
  273. Overlapped);
  274. SleepTime = WORKER_IDLE_TIMEOUT ;
  275. InterlockedDecrement(&NumExecutingWorkerThreads) ;
  276. RtlpWorkerThreadCancelTimer();
  277. } else if (Status == STATUS_TIMEOUT) {
  278. // NtRemoveIoCompletion timed out. Check to see if have hit our limit
  279. // on waiting. If so terminate.
  280. Terminate = FALSE ;
  281. RtlEnterCriticalSection (&WorkerCriticalSection) ;
  282. // The thread terminates if there are > 1 threads and the queue is small
  283. // OR if there is only 1 thread and there is no request pending
  284. if (NumWorkerThreads > 1) {
  285. ULONG NumEffWorkerThreads = NumWorkerThreads > NumLongWorkRequests
  286. ? NumWorkerThreads - NumLongWorkRequests
  287. : 0;
  288. if (NumEffWorkerThreads<=NumMinWorkerThreads) {
  289. Terminate = FALSE ;
  290. } else {
  291. //
  292. // have been idle for very long time. terminate irrespective of number of
  293. // work items. (This is useful when the set of runnable threads is taking
  294. // care of all the work items being queued). dont terminate if
  295. // (NumEffWorkerThreads == 1)
  296. //
  297. if (NumEffWorkerThreads > 1) {
  298. Terminate = TRUE ;
  299. } else {
  300. Terminate = FALSE ;
  301. }
  302. }
  303. } else {
  304. if ( NumMinWorkerThreads == 0
  305. && NumWorkRequests == 0
  306. && NumFutureWorkItems == 0) {
  307. Terminate = TRUE ;
  308. } else {
  309. Terminate = FALSE ;
  310. }
  311. }
  312. if (Terminate) {
  313. THREAD_BASIC_INFORMATION ThreadInfo;
  314. ULONG IsIoPending ;
  315. Terminate = FALSE ;
  316. Status = NtQueryInformationThread( NtCurrentThread(),
  317. ThreadIsIoPending,
  318. &IsIoPending,
  319. sizeof( IsIoPending ),
  320. NULL
  321. );
  322. if (NT_SUCCESS( Status )) {
  323. if (! IsIoPending )
  324. Terminate = TRUE ;
  325. }
  326. }
  327. if (Terminate) {
  328. ASSERT(NumWorkerThreads > 0);
  329. NumWorkerThreads--;
  330. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  331. RtlpExitThreadFunc( 0 );
  332. } else {
  333. // This is the condition where a request was queued *after* the
  334. // thread woke up - ready to terminate because of inactivity. In
  335. // this case dont terminate - service the completion port.
  336. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  337. }
  338. } else {
  339. ASSERTMSG ("NtRemoveIoCompletion failed",
  340. (Status != STATUS_SUCCESS) && (Status != STATUS_TIMEOUT)) ;
  341. }
  342. }
  343. return 1 ;
  344. }
  345. #if _MSC_FULL_VER >= 13008827
  346. #pragma warning(pop)
  347. #endif
  348. NTSTATUS
  349. RtlpStartWorkerThread (
  350. VOID
  351. )
  352. /*++
  353. Routine Description:
  354. This routine starts a regular worker thread
  355. Arguments:
  356. Return Value:
  357. NTSTATUS error codes resulting from attempts to create a thread
  358. STATUS_SUCCESS
  359. --*/
  360. {
  361. HANDLE ThreadHandle;
  362. ULONG CurrentTickCount;
  363. NTSTATUS Status;
  364. // Create worker thread
  365. Status = RtlpStartThreadpoolThread (RtlpWorkerThread,
  366. NULL,
  367. &ThreadHandle);
  368. if (NT_SUCCESS(Status)) {
  369. #if DBG
  370. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  371. RTLP_THREADPOOL_TRACE_MASK,
  372. "Created worker thread; handle %d (closing)\n",
  373. ThreadHandle);
  374. #endif
  375. // We don't care about worker threads' handles.
  376. NtClose(ThreadHandle);
  377. // Update the time at which the current thread was created
  378. LastThreadCreationTickCount = NtGetTickCount() ;
  379. // Increment the count of the thread type created
  380. NumWorkerThreads++;
  381. } else {
  382. #if DBG
  383. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  384. RTLP_THREADPOOL_TRACE_MASK,
  385. "Failed to create worker thread; status %p\n",
  386. Status);
  387. #endif
  388. // Thread creation failed. If there is even one thread present do not return
  389. // failure - else queue the request anyway.
  390. if (NumWorkerThreads <= NumLongWorkRequests) {
  391. return Status ;
  392. }
  393. }
  394. return STATUS_SUCCESS ;
  395. }
  396. NTSTATUS
  397. RtlpInitializeWorkerThreadPool (
  398. )
  399. /*++
  400. Routine Description:
  401. This routine initializes all aspects of the thread pool.
  402. Arguments:
  403. None
  404. Return Value:
  405. None
  406. --*/
  407. {
  408. NTSTATUS Status = STATUS_SUCCESS ;
  409. LARGE_INTEGER TimeOut ;
  410. SYSTEM_BASIC_INFORMATION BasicInfo;
  411. ASSERT(! RtlIsImpersonating());
  412. // Initialize the timer component if it hasnt been done already
  413. if (CompletedTimerInitialization != 1) {
  414. Status = RtlpInitializeTimerThreadPool () ;
  415. if ( !NT_SUCCESS(Status) )
  416. return Status ;
  417. }
  418. // In order to avoid an explicit RtlInitialize() function to initialize the thread pool
  419. // we use StartedInitialization and CompletedInitialization to provide us the necessary
  420. // synchronization to avoid multiple threads from initializing the thread pool.
  421. // This scheme does not work if RtlInitializeCriticalSection() fails - but in this case the
  422. // caller has no choices left.
  423. if (!InterlockedExchange(&StartedWorkerInitialization, 1L)) {
  424. if (CompletedWorkerInitialization)
  425. InterlockedExchange( &CompletedWorkerInitialization, 0 ) ;
  426. do {
  427. // Initialize Critical Sections
  428. Status = RtlInitializeCriticalSection( &WorkerCriticalSection );
  429. if (!NT_SUCCESS(Status))
  430. break ;
  431. Status = RtlInitializeCriticalSection( &WorkerTimerCriticalSection );
  432. if (! NT_SUCCESS(Status)) {
  433. RtlDeleteCriticalSection( &WorkerCriticalSection );
  434. break;
  435. }
  436. InitializeListHead (&IOWorkerThreads) ;
  437. // get number of processors
  438. Status = NtQuerySystemInformation (
  439. SystemBasicInformation,
  440. &BasicInfo,
  441. sizeof(BasicInfo),
  442. NULL
  443. ) ;
  444. if ( !NT_SUCCESS(Status) ) {
  445. BasicInfo.NumberOfProcessors = 1 ;
  446. }
  447. // Create completion port used by worker threads
  448. Status = NtCreateIoCompletion (
  449. &WorkerCompletionPort,
  450. IO_COMPLETION_ALL_ACCESS,
  451. NULL,
  452. BasicInfo.NumberOfProcessors
  453. );
  454. if (!NT_SUCCESS(Status)) {
  455. RtlDeleteCriticalSection( &WorkerCriticalSection );
  456. RtlDeleteCriticalSection( &WorkerTimerCriticalSection );
  457. break;
  458. }
  459. } while ( FALSE ) ;
  460. if (!NT_SUCCESS(Status) ) {
  461. StartedWorkerInitialization = 0 ;
  462. InterlockedExchange( &CompletedWorkerInitialization, ~0 ) ;
  463. return Status ;
  464. }
  465. // Signal that initialization has completed
  466. InterlockedExchange (&CompletedWorkerInitialization, 1L) ;
  467. } else {
  468. LARGE_INTEGER Timeout ;
  469. // Sleep 1 ms and see if the other thread has completed initialization
  470. ONE_MILLISECOND_TIMEOUT(TimeOut) ;
  471. while (!*((ULONG volatile *)&CompletedWorkerInitialization)) {
  472. NtDelayExecution (FALSE, &TimeOut) ;
  473. }
  474. if (CompletedWorkerInitialization != 1)
  475. return STATUS_NO_MEMORY ;
  476. }
  477. return NT_SUCCESS(Status) ? STATUS_SUCCESS : Status ;
  478. }
  479. LONG
  480. RtlpIOWorkerThread (
  481. PVOID Parameter
  482. )
  483. /*++
  484. Routine Description:
  485. All I/O worker threads execute in this routine. All the work requests execute as APCs
  486. in this thread.
  487. Arguments:
  488. HandlePtr - Pointer to our handle.
  489. Return Value:
  490. --*/
  491. {
  492. #define IOWORKER_IDLE_TIMEOUT 40000 // In Milliseconds
  493. LARGE_INTEGER TimeOut ;
  494. ULONG SleepTime = IOWORKER_IDLE_TIMEOUT ;
  495. PRTLP_IOWORKER_TCB ThreadCB ; // Control Block allocated on the
  496. // heap by the parent thread
  497. NTSTATUS Status ;
  498. BOOLEAN Terminate ;
  499. ASSERT(Parameter != NULL);
  500. ThreadCB = (PRTLP_IOWORKER_TCB) Parameter;
  501. #if DBG
  502. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  503. RTLP_THREADPOOL_TRACE_MASK,
  504. "Starting IO worker thread\n");
  505. #endif
  506. // Sleep alertably so that all the activity can take place
  507. // in APCs
  508. for ( ; ; ) {
  509. // Set timeout for IdleTimeout
  510. TimeOut.QuadPart = Int32x32To64( SleepTime, -10000 ) ;
  511. Status = NtDelayExecution (TRUE, &TimeOut) ;
  512. // Status is STATUS_SUCCESS only when it has timed out
  513. if (Status != STATUS_SUCCESS) {
  514. continue ;
  515. }
  516. //
  517. // idle timeout. check if you can terminate the thread
  518. //
  519. Terminate = FALSE ;
  520. RtlEnterCriticalSection (&WorkerCriticalSection) ;
  521. // dont terminate if it is a persistent thread
  522. if (ThreadCB->Flags & WT_EXECUTEINPERSISTENTIOTHREAD) {
  523. TimeOut.LowPart = 0x0;
  524. TimeOut.HighPart = 0x80000000;
  525. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  526. continue ;
  527. }
  528. // The thread terminates if there are > 1 threads and the queue is small
  529. // OR if there is only 1 thread and there is no request pending
  530. if (NumIOWorkerThreads > 1) {
  531. ULONG NumEffIOWorkerThreads = NumIOWorkerThreads > NumLongIOWorkRequests
  532. ? NumIOWorkerThreads - NumLongIOWorkRequests
  533. : 0;
  534. ULONG Threshold;
  535. if (NumEffIOWorkerThreads == 0) {
  536. Terminate = FALSE ;
  537. } else {
  538. // Check if we need to shrink worker thread pool
  539. Threshold = NEW_THREAD_THRESHOLD * (NumEffIOWorkerThreads-1);
  540. if (NumIOWorkRequests-NumLongIOWorkRequests < Threshold) {
  541. Terminate = TRUE ;
  542. } else {
  543. Terminate = FALSE ;
  544. SleepTime <<= 1 ;
  545. }
  546. }
  547. } else {
  548. if (NumIOWorkRequests == 0
  549. && NumFutureIOWorkItems == 0) {
  550. // delay termination of last thread
  551. if (SleepTime < 4*IOWORKER_IDLE_TIMEOUT) {
  552. SleepTime <<= 1 ;
  553. Terminate = FALSE ;
  554. } else {
  555. Terminate = TRUE ;
  556. }
  557. } else {
  558. Terminate = FALSE ;
  559. }
  560. }
  561. //
  562. // terminate only if no io is pending
  563. //
  564. if (Terminate) {
  565. NTSTATUS xStatus;
  566. THREAD_BASIC_INFORMATION ThreadInfo;
  567. ULONG IsIoPending ;
  568. Terminate = FALSE ;
  569. xStatus = NtQueryInformationThread( ThreadCB->ThreadHandle,
  570. ThreadIsIoPending,
  571. &IsIoPending,
  572. sizeof( IsIoPending ),
  573. NULL
  574. );
  575. if (NT_SUCCESS( xStatus )) {
  576. if (! IsIoPending )
  577. Terminate = TRUE ;
  578. }
  579. }
  580. if (Terminate) {
  581. ASSERT(NumIOWorkerThreads > 0);
  582. NumIOWorkerThreads--;
  583. RemoveEntryList (&ThreadCB->List) ;
  584. NtClose( ThreadCB->ThreadHandle ) ;
  585. RtlpFreeTPHeap( ThreadCB );
  586. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  587. RtlpExitThreadFunc( 0 );
  588. } else {
  589. // This is the condition where a request was queued *after* the
  590. // thread woke up - ready to terminate because of inactivity. In
  591. // this case dont terminate - service the completion port.
  592. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  593. }
  594. }
  595. return 0 ; // Keep compiler happy
  596. }
  597. NTSTATUS
  598. RtlpStartIOWorkerThread (
  599. )
  600. /*++
  601. Routine Description:
  602. This routine starts an I/O worker thread
  603. N.B. Callers MUST hold the WorkerCriticalSection.
  604. Arguments:
  605. Return Value:
  606. NTSTATUS error codes resulting from attempts to create a thread
  607. STATUS_SUCCESS
  608. --*/
  609. {
  610. ULONG CurrentTickCount ;
  611. NTSTATUS Status ;
  612. PRTLP_IOWORKER_TCB ThreadCB;
  613. // Create the worker's control block
  614. ThreadCB = (PRTLP_IOWORKER_TCB) RtlpAllocateTPHeap(sizeof(RTLP_IOWORKER_TCB), 0);
  615. if (! ThreadCB) {
  616. return STATUS_NO_MEMORY;
  617. }
  618. // Fill in the control block
  619. ThreadCB->Flags = 0;
  620. ThreadCB->LongFunctionFlag = FALSE;
  621. // Create worker thread
  622. Status = RtlpStartThreadpoolThread (RtlpIOWorkerThread,
  623. ThreadCB,
  624. &ThreadCB->ThreadHandle);
  625. if (NT_SUCCESS(Status)) {
  626. // Update the time at which the current thread was created,
  627. // and insert the ThreadCB into the IO worker thread list.
  628. LastThreadCreationTickCount = NtGetTickCount() ;
  629. NumIOWorkerThreads++;
  630. InsertHeadList(&IOWorkerThreads, &ThreadCB->List);
  631. } else {
  632. // Thread creation failed.
  633. RtlpFreeTPHeap(ThreadCB);
  634. // If there is even one thread present do not return
  635. // failure since we can still service the work request.
  636. if (NumIOWorkerThreads <= NumLongIOWorkRequests) {
  637. return Status ;
  638. }
  639. }
  640. return STATUS_SUCCESS ;
  641. }
  642. VOID
  643. RtlpExecuteLongIOWorkItem (
  644. PVOID WorkEntryPtr,
  645. PVOID Context,
  646. PVOID ThreadCB
  647. )
  648. /*++
  649. Routine Description:
  650. Executes an IO Work function. RUNs in a APC in the IO Worker thread.
  651. Arguments:
  652. WorkEntryPtr - Work entry to run
  653. Context - Context for the work entry
  654. ThreadCB - The thread running this APC
  655. Return Value:
  656. None.
  657. --*/
  658. {
  659. PRTLP_WORK WorkEntry = (PRTLP_WORK) WorkEntryPtr;
  660. RtlpWorkerCallout(WorkEntry->Function,
  661. Context,
  662. WorkEntry->ActivationContext,
  663. WorkEntry->ImpersonationToken);
  664. ((PRTLP_IOWORKER_TCB)ThreadCB)->LongFunctionFlag = FALSE ;
  665. RtlEnterCriticalSection(&WorkerCriticalSection);
  666. // Decrement pending IO requests count
  667. NumIOWorkRequests--;
  668. // decrement pending long funcitons
  669. NumLongIOWorkRequests--;
  670. RtlLeaveCriticalSection(&WorkerCriticalSection);
  671. if (WorkEntry->ActivationContext != INVALID_ACTIVATION_CONTEXT) {
  672. RtlReleaseActivationContext(WorkEntry->ActivationContext);
  673. }
  674. if (WorkEntry->ImpersonationToken) {
  675. NtClose(WorkEntry->ImpersonationToken);
  676. }
  677. RtlpFreeTPHeap(WorkEntry);
  678. }
  679. VOID
  680. RtlpExecuteIOWorkItem (
  681. PVOID WorkEntryPtr,
  682. PVOID Context,
  683. PVOID Parameter
  684. )
  685. /*++
  686. Routine Description:
  687. Executes an IO Work function. RUNs in a APC in the IO Worker thread.
  688. Arguments:
  689. WorkEntryPtr - Work entry to run
  690. Context - Context for the work entry
  691. Parameter - Argument is not used in this function.
  692. Return Value:
  693. --*/
  694. {
  695. PRTLP_WORK WorkEntry = (PRTLP_WORK) WorkEntryPtr;
  696. UNREFERENCED_PARAMETER(Parameter);
  697. RtlpWorkerCallout(WorkEntry->Function,
  698. Context,
  699. WorkEntry->ActivationContext,
  700. WorkEntry->ImpersonationToken);
  701. RtlEnterCriticalSection(&WorkerCriticalSection);
  702. // Decrement pending IO requests count
  703. NumIOWorkRequests--;
  704. RtlLeaveCriticalSection(&WorkerCriticalSection);
  705. if (WorkEntry->ActivationContext != INVALID_ACTIVATION_CONTEXT) {
  706. RtlReleaseActivationContext(WorkEntry->ActivationContext);
  707. }
  708. if (WorkEntry->ImpersonationToken) {
  709. NtClose(WorkEntry->ImpersonationToken);
  710. }
  711. RtlpFreeTPHeap(WorkEntry);
  712. }
  713. NTSTATUS
  714. RtlpQueueIOWorkerRequest (
  715. WORKERCALLBACKFUNC Function,
  716. PVOID Context,
  717. ULONG Flags,
  718. HANDLE Token
  719. )
  720. /*++
  721. Routine Description:
  722. This routine queues up the request to be executed in an IO worker thread.
  723. Arguments:
  724. Function - Routine that is called by the worker thread
  725. Context - Opaque pointer passed in as an argument to WorkerProc
  726. Flags - Flags passed to RtlQueueWorkItem
  727. Token - Impersonation token to use
  728. Return Value:
  729. --*/
  730. {
  731. NTSTATUS Status ;
  732. PRTLP_IOWORKER_TCB TCB ;
  733. BOOLEAN LongFunction = (Flags & WT_EXECUTELONGFUNCTION) ? TRUE : FALSE ;
  734. PLIST_ENTRY ple ;
  735. PRTLP_WORK WorkEntry ;
  736. WorkEntry = (PRTLP_WORK) RtlpAllocateTPHeap(sizeof(RTLP_WORK),
  737. HEAP_ZERO_MEMORY);
  738. if (! WorkEntry) {
  739. return STATUS_NO_MEMORY;
  740. }
  741. WorkEntry->Function = Function;
  742. WorkEntry->Flags = Flags;
  743. Status = RtlpThreadPoolGetActiveActivationContext(&WorkEntry->ActivationContext);
  744. if (!NT_SUCCESS(Status)) {
  745. if (Status == STATUS_SXS_THREAD_QUERIES_DISABLED) {
  746. WorkEntry->ActivationContext = INVALID_ACTIVATION_CONTEXT;
  747. Status = STATUS_SUCCESS;
  748. } else {
  749. goto cleanup_workentry;
  750. }
  751. }
  752. if (Token) {
  753. Status = NtDuplicateToken(Token,
  754. TOKEN_IMPERSONATE,
  755. NULL,
  756. FALSE,
  757. TokenImpersonation,
  758. &WorkEntry->ImpersonationToken);
  759. if (! NT_SUCCESS(Status)) {
  760. goto cleanup_actctx;
  761. }
  762. } else {
  763. WorkEntry->ImpersonationToken = NULL;
  764. }
  765. if (Flags & WT_EXECUTEINPERSISTENTIOTHREAD) {
  766. if (!PersistentIOTCB) {
  767. for (ple=IOWorkerThreads.Flink; ple!=&IOWorkerThreads; ple=ple->Flink) {
  768. TCB = CONTAINING_RECORD (ple, RTLP_IOWORKER_TCB, List) ;
  769. if (! TCB->LongFunctionFlag)
  770. break;
  771. }
  772. if (ple == &IOWorkerThreads) {
  773. Status = STATUS_NO_MEMORY;
  774. goto cleanup_token;
  775. }
  776. PersistentIOTCB = TCB ;
  777. TCB->Flags |= WT_EXECUTEINPERSISTENTIOTHREAD ;
  778. } else {
  779. TCB = PersistentIOTCB ;
  780. }
  781. } else {
  782. for (ple=IOWorkerThreads.Flink; ple!=&IOWorkerThreads; ple=ple->Flink) {
  783. TCB = CONTAINING_RECORD (ple, RTLP_IOWORKER_TCB, List) ;
  784. // do not queue to the thread if it is executing a long function, or
  785. // if you are queueing a long function and the thread is a persistent thread
  786. if (! TCB->LongFunctionFlag
  787. && (! ((TCB->Flags&WT_EXECUTEINPERSISTENTIOTHREAD)
  788. && (Flags&WT_EXECUTELONGFUNCTION)))) {
  789. break ;
  790. }
  791. }
  792. if ((ple == &IOWorkerThreads) && (NumIOWorkerThreads<1)) {
  793. #if DBG
  794. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  795. RTLP_THREADPOOL_WARNING_MASK,
  796. "Out of memory. "
  797. "Could not execute IOWorkItem(%x)\n", (ULONG_PTR)Function);
  798. #endif
  799. Status = STATUS_NO_MEMORY;
  800. goto cleanup_token;
  801. }
  802. else {
  803. ple = IOWorkerThreads.Flink;
  804. TCB = CONTAINING_RECORD (ple, RTLP_IOWORKER_TCB, List) ;
  805. // treat it as a short function so that counters work fine.
  806. LongFunction = FALSE;
  807. }
  808. // In order to implement "fair" assignment of work items between IO worker threads
  809. // each time remove the entry and reinsert at back.
  810. RemoveEntryList (&TCB->List) ;
  811. InsertTailList (&IOWorkerThreads, &TCB->List) ;
  812. }
  813. // Increment the outstanding work request counter
  814. NumIOWorkRequests++;
  815. if (LongFunction) {
  816. NumLongIOWorkRequests++;
  817. TCB->LongFunctionFlag = TRUE ;
  818. }
  819. // Queue an APC to the IoWorker Thread
  820. Status = NtQueueApcThread(
  821. TCB->ThreadHandle,
  822. LongFunction? (PPS_APC_ROUTINE)RtlpExecuteLongIOWorkItem:
  823. (PPS_APC_ROUTINE)RtlpExecuteIOWorkItem,
  824. (PVOID)WorkEntry,
  825. Context,
  826. TCB
  827. );
  828. if (! NT_SUCCESS( Status ) ) {
  829. goto cleanup_counters;
  830. }
  831. return STATUS_SUCCESS;
  832. cleanup_counters:
  833. NumIOWorkRequests--;
  834. if (LongFunction)
  835. NumLongIOWorkRequests--;
  836. cleanup_token:
  837. if (WorkEntry->ImpersonationToken) {
  838. NtClose(WorkEntry->ImpersonationToken);
  839. }
  840. cleanup_actctx:
  841. if (WorkEntry->ActivationContext != INVALID_ACTIVATION_CONTEXT) {
  842. RtlReleaseActivationContext(WorkEntry->ActivationContext);
  843. }
  844. cleanup_workentry:
  845. RtlpFreeTPHeap(WorkEntry);
  846. return Status;
  847. }
  848. NTSTATUS
  849. RtlSetIoCompletionCallback (
  850. IN HANDLE FileHandle,
  851. IN APC_CALLBACK_FUNCTION CompletionProc,
  852. IN ULONG Flags
  853. )
  854. /*++
  855. Routine Description:
  856. This routine binds an Handle and an associated callback function to the
  857. IoCompletionPort which queues work items to worker threads.
  858. Arguments:
  859. Handle - handle to be bound to the IO completion port
  860. CompletionProc - callback function to be executed when an IO request
  861. pending on the IO handle completes.
  862. Flags - Reserved. pass 0.
  863. --*/
  864. {
  865. IO_STATUS_BLOCK IoSb ;
  866. FILE_COMPLETION_INFORMATION CompletionInfo ;
  867. NTSTATUS Status;
  868. HANDLE Token = NULL;
  869. if (LdrpShutdownInProgress) {
  870. return STATUS_UNSUCCESSFUL;
  871. }
  872. if (Flags) {
  873. return STATUS_INVALID_PARAMETER_3;
  874. }
  875. Status = RtlpCaptureImpersonation(FALSE, &Token);
  876. if (! NT_SUCCESS(Status)) {
  877. return Status;
  878. }
  879. // Make sure that the worker thread pool is initialized as the file handle
  880. // is bound to IO completion port.
  881. if (CompletedWorkerInitialization != 1) {
  882. Status = RtlpInitializeWorkerThreadPool () ;
  883. if (! NT_SUCCESS(Status) ) {
  884. goto cleanup;
  885. }
  886. }
  887. //
  888. // from now on NumMinWorkerThreads should be 1. If there is only 1 worker thread
  889. // create a new one.
  890. //
  891. if ( NumMinWorkerThreads == 0 ) {
  892. // Take lock for the global worker thread pool
  893. RtlEnterCriticalSection (&WorkerCriticalSection) ;
  894. if ((NumWorkerThreads-NumLongWorkRequests) == 0) {
  895. Status = RtlpStartWorkerThread () ;
  896. if ( ! NT_SUCCESS(Status) ) {
  897. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  898. goto cleanup;
  899. }
  900. }
  901. // from now on, there will be at least 1 worker thread
  902. NumMinWorkerThreads = 1 ;
  903. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  904. }
  905. // bind to IoCompletionPort, which queues work items to worker threads
  906. CompletionInfo.Port = WorkerCompletionPort ;
  907. CompletionInfo.Key = (PVOID) CompletionProc ;
  908. Status = NtSetInformationFile (
  909. FileHandle,
  910. &IoSb, //not initialized
  911. &CompletionInfo,
  912. sizeof(CompletionInfo),
  913. FileCompletionInformation //enum flag
  914. ) ;
  915. cleanup:
  916. if (Token) {
  917. RtlpRestartImpersonation(Token);
  918. NtClose(Token);
  919. }
  920. return Status ;
  921. }
  922. VOID
  923. RtlpExecuteWorkerRequest (
  924. NTSTATUS StatusIn, //not used
  925. PVOID Context,
  926. PVOID WorkContext
  927. )
  928. /*++
  929. Routine Description:
  930. This routine executes a work item.
  931. Arguments:
  932. Context - contains context to be passed to the callback function.
  933. WorkContext - contains callback function ptr and flags
  934. Return Value:
  935. Notes:
  936. This function executes in a worker thread or a timer thread if
  937. WT_EXECUTEINTIMERTHREAD flag is set.
  938. --*/
  939. {
  940. PRTLP_WORK WorkEntry = (PRTLP_WORK) WorkContext;
  941. NTSTATUS Status;
  942. if (! (WorkEntry->Flags & WT_EXECUTEINPERSISTENTTHREAD)
  943. && InterlockedDecrement(&NumQueuedWorkRequests)
  944. && NumExecutingWorkerThreads == NumWorkerThreads) {
  945. RtlpWorkerThreadSetTimer();
  946. }
  947. RtlpWorkerCallout(WorkEntry->Function,
  948. Context,
  949. WorkEntry->ActivationContext,
  950. WorkEntry->ImpersonationToken);
  951. RtlEnterCriticalSection(&WorkerCriticalSection);
  952. NumWorkRequests--;
  953. if (WorkEntry->Flags & WT_EXECUTELONGFUNCTION) {
  954. NumLongWorkRequests--;
  955. }
  956. RtlLeaveCriticalSection(&WorkerCriticalSection);
  957. if (WorkEntry->ActivationContext != INVALID_ACTIVATION_CONTEXT)
  958. RtlReleaseActivationContext(WorkEntry->ActivationContext);
  959. if (WorkEntry->ImpersonationToken) {
  960. NtClose(WorkEntry->ImpersonationToken);
  961. }
  962. RtlpFreeTPHeap( WorkEntry ) ;
  963. }
  964. NTSTATUS
  965. RtlpQueueWorkerRequest (
  966. WORKERCALLBACKFUNC Function,
  967. PVOID Context,
  968. ULONG Flags,
  969. HANDLE Token
  970. )
  971. /*++
  972. Routine Description:
  973. This routine queues up the request to be executed in a worker thread.
  974. Arguments:
  975. Function - Routine that is called by the worker thread
  976. Context - Opaque pointer passed in as an argument to WorkerProc
  977. Flags - Flags passed to RtlQueueWorkItem
  978. Token - Impersonation token to use
  979. Return Value:
  980. --*/
  981. {
  982. NTSTATUS Status ;
  983. PRTLP_WORK WorkEntry ;
  984. WorkEntry = (PRTLP_WORK) RtlpAllocateTPHeap ( sizeof (RTLP_WORK),
  985. HEAP_ZERO_MEMORY) ;
  986. if (! WorkEntry) {
  987. return STATUS_NO_MEMORY;
  988. }
  989. Status = RtlpThreadPoolGetActiveActivationContext(&WorkEntry->ActivationContext);
  990. if (!NT_SUCCESS(Status)) {
  991. if (Status == STATUS_SXS_THREAD_QUERIES_DISABLED) {
  992. WorkEntry->ActivationContext = INVALID_ACTIVATION_CONTEXT;
  993. Status = STATUS_SUCCESS;
  994. } else {
  995. goto cleanup_workentry;
  996. }
  997. }
  998. if (Token) {
  999. Status = NtDuplicateToken(Token,
  1000. TOKEN_IMPERSONATE,
  1001. NULL,
  1002. FALSE,
  1003. TokenImpersonation,
  1004. &WorkEntry->ImpersonationToken);
  1005. if (! NT_SUCCESS(Status)) {
  1006. goto cleanup_actctx;
  1007. }
  1008. } else {
  1009. WorkEntry->ImpersonationToken = NULL;
  1010. }
  1011. // Increment the outstanding work request counter
  1012. NumWorkRequests++;
  1013. if (Flags & WT_EXECUTELONGFUNCTION) {
  1014. NumLongWorkRequests++;
  1015. }
  1016. WorkEntry->Function = Function ;
  1017. WorkEntry->Flags = Flags ;
  1018. if (Flags & WT_EXECUTEINPERSISTENTTHREAD) {
  1019. // Queue APC to timer thread
  1020. Status = NtQueueApcThread(
  1021. TimerThreadHandle,
  1022. (PPS_APC_ROUTINE)RtlpExecuteWorkerRequest,
  1023. (PVOID) STATUS_SUCCESS,
  1024. (PVOID) Context,
  1025. (PVOID) WorkEntry
  1026. ) ;
  1027. } else {
  1028. InterlockedIncrement(&NumQueuedWorkRequests);
  1029. Status = NtSetIoCompletion (
  1030. WorkerCompletionPort,
  1031. RtlpExecuteWorkerRequest,
  1032. (PVOID) WorkEntry,
  1033. STATUS_SUCCESS,
  1034. (ULONG_PTR)Context
  1035. );
  1036. if (! NT_SUCCESS(Status)) {
  1037. InterlockedDecrement(&NumQueuedWorkRequests);
  1038. } else {
  1039. if (NumExecutingWorkerThreads == NumWorkerThreads) {
  1040. RtlpWorkerThreadSetTimer();
  1041. }
  1042. }
  1043. }
  1044. if ( ! NT_SUCCESS(Status) ) {
  1045. goto cleanup_counters;
  1046. }
  1047. return STATUS_SUCCESS;
  1048. cleanup_counters:
  1049. NumWorkRequests--;
  1050. if (Flags & WT_EXECUTELONGFUNCTION) {
  1051. NumLongWorkRequests--;
  1052. }
  1053. if (WorkEntry->ImpersonationToken) {
  1054. NtClose(WorkEntry->ImpersonationToken);
  1055. }
  1056. cleanup_actctx:
  1057. if (WorkEntry->ActivationContext != INVALID_ACTIVATION_CONTEXT) {
  1058. RtlReleaseActivationContext(WorkEntry->ActivationContext);
  1059. }
  1060. cleanup_workentry:
  1061. RtlpFreeTPHeap( WorkEntry ) ;
  1062. return Status;
  1063. }
  1064. NTSTATUS
  1065. RtlQueueWorkItem(
  1066. IN WORKERCALLBACKFUNC Function,
  1067. IN PVOID Context,
  1068. IN ULONG Flags
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. This routine queues up the request to be executed in a worker thread.
  1073. Arguments:
  1074. Function - Routine that is called by the worker thread
  1075. Context - Opaque pointer passed in as an argument to WorkerProc
  1076. Flags - Can be:
  1077. WT_EXECUTEINIOTHREAD - Specifies that the WorkerProc should be invoked
  1078. by a thread that is never destroyed when there are pending IO requests.
  1079. This can be used by threads that invoke I/O and/or schedule APCs.
  1080. The below flag can also be set:
  1081. WT_EXECUTELONGFUNCTION - Specifies that the function might block for a
  1082. long duration.
  1083. Return Value:
  1084. STATUS_SUCCESS - Queued successfully.
  1085. STATUS_NO_MEMORY - There was not sufficient heap to perform the
  1086. requested operation.
  1087. --*/
  1088. {
  1089. ULONG Threshold ;
  1090. ULONG CurrentTickCount ;
  1091. NTSTATUS Status = STATUS_SUCCESS ;
  1092. HANDLE Token = NULL;
  1093. HANDLE RequestToken;
  1094. if (LdrpShutdownInProgress) {
  1095. return STATUS_UNSUCCESSFUL;
  1096. }
  1097. Status = RtlpCaptureImpersonation(Flags & WT_TRANSFER_IMPERSONATION,
  1098. &Token);
  1099. if (! NT_SUCCESS(Status)) {
  1100. return Status;
  1101. }
  1102. if (Flags & WT_TRANSFER_IMPERSONATION) {
  1103. RequestToken = Token;
  1104. } else {
  1105. RequestToken = NULL;
  1106. }
  1107. // Make sure the worker thread pool is initialized
  1108. if (CompletedWorkerInitialization != 1) {
  1109. Status = RtlpInitializeWorkerThreadPool () ;
  1110. if (! NT_SUCCESS(Status) ) {
  1111. goto cleanup;
  1112. }
  1113. }
  1114. // Take lock for the global worker thread pool
  1115. RtlEnterCriticalSection (&WorkerCriticalSection) ;
  1116. if (Flags&0xffff0000) {
  1117. MaxThreads = (Flags & 0xffff0000)>>16;
  1118. }
  1119. if (NEEDS_IO_THREAD(Flags)) {
  1120. //
  1121. // execute in IO Worker thread
  1122. //
  1123. ULONG NumEffIOWorkerThreads = NumIOWorkerThreads > NumLongIOWorkRequests
  1124. ? NumIOWorkerThreads - NumLongIOWorkRequests
  1125. : 0;
  1126. ULONG ThreadCreationDampingTime = NumIOWorkerThreads < NEW_THREAD_THRESHOLD
  1127. ? THREAD_CREATION_DAMPING_TIME1
  1128. : THREAD_CREATION_DAMPING_TIME2 ;
  1129. if (NumEffIOWorkerThreads && PersistentIOTCB && (Flags&WT_EXECUTELONGFUNCTION))
  1130. NumEffIOWorkerThreads -- ;
  1131. // Check if we need to grow I/O worker thread pool
  1132. Threshold = (NumEffIOWorkerThreads < MAX_WORKER_THREADS
  1133. ? NEW_THREAD_THRESHOLD * NumEffIOWorkerThreads
  1134. : 0xffffffff) ;
  1135. if (LastThreadCreationTickCount > NtGetTickCount())
  1136. LastThreadCreationTickCount = NtGetTickCount() ;
  1137. if (NumEffIOWorkerThreads == 0
  1138. || ((NumIOWorkRequests - NumLongIOWorkRequests > Threshold)
  1139. && (LastThreadCreationTickCount + ThreadCreationDampingTime
  1140. < NtGetTickCount()))) {
  1141. // Grow the IO worker thread pool
  1142. Status = RtlpStartIOWorkerThread () ;
  1143. }
  1144. if (NT_SUCCESS(Status)) {
  1145. // Queue the work request
  1146. Status = RtlpQueueIOWorkerRequest(Function,
  1147. Context,
  1148. Flags,
  1149. RequestToken);
  1150. }
  1151. } else {
  1152. //
  1153. // execute in regular worker thread
  1154. //
  1155. ULONG NumEffWorkerThreads = NumWorkerThreads > NumLongWorkRequests
  1156. ? NumWorkerThreads - NumLongWorkRequests
  1157. : 0;
  1158. ULONG ThreadCreationDampingTime = NumWorkerThreads < NEW_THREAD_THRESHOLD
  1159. ? THREAD_CREATION_DAMPING_TIME1
  1160. : (NumWorkerThreads < 30
  1161. ? THREAD_CREATION_DAMPING_TIME2
  1162. : NumWorkerThreads << 13);
  1163. // if io completion set, then have 1 more thread
  1164. if (NumMinWorkerThreads && NumEffWorkerThreads)
  1165. NumEffWorkerThreads -- ;
  1166. // Check if we need to grow worker thread pool
  1167. Threshold = (NumWorkerThreads < MAX_WORKER_THREADS
  1168. ? (NumEffWorkerThreads < 7
  1169. ? NumEffWorkerThreads*NumEffWorkerThreads
  1170. : ((NumEffWorkerThreads<40)
  1171. ? NEW_THREAD_THRESHOLD * NumEffWorkerThreads
  1172. : NEW_THREAD_THRESHOLD2 * NumEffWorkerThreads))
  1173. : 0xffffffff) ;
  1174. if (LastThreadCreationTickCount > NtGetTickCount())
  1175. LastThreadCreationTickCount = NtGetTickCount() ;
  1176. if (NumEffWorkerThreads == 0 ||
  1177. ( (NumWorkRequests - NumLongWorkRequests >= Threshold)
  1178. && (LastThreadCreationTickCount + ThreadCreationDampingTime
  1179. < NtGetTickCount())))
  1180. {
  1181. // Grow the worker thread pool
  1182. if (NumWorkerThreads<MaxThreads) {
  1183. Status = RtlpStartWorkerThread () ;
  1184. }
  1185. }
  1186. // Queue the work request
  1187. if (NT_SUCCESS(Status)) {
  1188. Status = RtlpQueueWorkerRequest(Function,
  1189. Context,
  1190. Flags,
  1191. RequestToken);
  1192. }
  1193. }
  1194. // Release lock on the worker thread pool
  1195. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  1196. cleanup:
  1197. if (Token) {
  1198. RtlpRestartImpersonation(Token);
  1199. NtClose(Token);
  1200. }
  1201. return Status ;
  1202. }
  1203. NTSTATUS
  1204. RtlpWorkerCleanup(
  1205. VOID
  1206. )
  1207. {
  1208. PLIST_ENTRY Node;
  1209. ULONG i;
  1210. HANDLE TmpHandle;
  1211. BOOLEAN Cleanup;
  1212. IS_COMPONENT_INITIALIZED( StartedWorkerInitialization,
  1213. CompletedWorkerInitialization,
  1214. Cleanup ) ;
  1215. if ( Cleanup ) {
  1216. RtlEnterCriticalSection (&WorkerCriticalSection) ;
  1217. if ( (NumWorkRequests != 0) || (NumIOWorkRequests != 0) ) {
  1218. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  1219. return STATUS_UNSUCCESSFUL ;
  1220. }
  1221. // queue a cleanup for each worker thread
  1222. for (i = 0 ; i < NumWorkerThreads ; i ++ ) {
  1223. NtSetIoCompletion (
  1224. WorkerCompletionPort,
  1225. RtlpThreadCleanup,
  1226. NULL,
  1227. STATUS_SUCCESS,
  1228. 0
  1229. );
  1230. }
  1231. // queue an apc to cleanup all IO worker threads
  1232. for (Node = IOWorkerThreads.Flink ; Node != &IOWorkerThreads ;
  1233. Node = Node->Flink )
  1234. {
  1235. PRTLP_IOWORKER_TCB ThreadCB ;
  1236. ThreadCB = CONTAINING_RECORD (Node, RTLP_IOWORKER_TCB, List) ;
  1237. RemoveEntryList( &ThreadCB->List) ;
  1238. TmpHandle = ThreadCB->ThreadHandle ;
  1239. NtQueueApcThread(
  1240. ThreadCB->ThreadHandle,
  1241. (PPS_APC_ROUTINE)RtlpThreadCleanup,
  1242. NULL,
  1243. NULL,
  1244. NULL
  1245. );
  1246. NtClose( TmpHandle ) ;
  1247. }
  1248. NumWorkerThreads = NumIOWorkerThreads = 0 ;
  1249. RtlLeaveCriticalSection (&WorkerCriticalSection) ;
  1250. }
  1251. return STATUS_SUCCESS;
  1252. }
  1253. NTSTATUS
  1254. RtlpThreadPoolGetActiveActivationContext(
  1255. PACTIVATION_CONTEXT* ActivationContext
  1256. )
  1257. {
  1258. ACTIVATION_CONTEXT_BASIC_INFORMATION ActivationContextInfo = {0};
  1259. NTSTATUS Status = STATUS_SUCCESS;
  1260. ASSERT(ActivationContext != NULL);
  1261. *ActivationContext = NULL;
  1262. Status =
  1263. RtlQueryInformationActivationContext(
  1264. RTL_QUERY_INFORMATION_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT,
  1265. NULL,
  1266. 0,
  1267. ActivationContextBasicInformation,
  1268. &ActivationContextInfo,
  1269. sizeof(ActivationContextInfo),
  1270. NULL);
  1271. if (!NT_SUCCESS(Status)) {
  1272. goto Exit;
  1273. }
  1274. if ((ActivationContextInfo.Flags & ACTIVATION_CONTEXT_FLAG_NO_INHERIT) != 0) {
  1275. RtlReleaseActivationContext(ActivationContextInfo.ActivationContext);
  1276. ActivationContextInfo.ActivationContext = NULL;
  1277. // fall through
  1278. }
  1279. *ActivationContext = ActivationContextInfo.ActivationContext;
  1280. Status = STATUS_SUCCESS;
  1281. Exit:
  1282. return Status;
  1283. }
  1284. NTSTATUS
  1285. RtlpAcquireWorker(ULONG Flags)
  1286. {
  1287. NTSTATUS Status = STATUS_SUCCESS;
  1288. if (CompletedWorkerInitialization != 1) {
  1289. Status = RtlpInitializeWorkerThreadPool () ;
  1290. if (! NT_SUCCESS(Status) )
  1291. return Status ;
  1292. }
  1293. if (NEEDS_IO_THREAD(Flags)) {
  1294. RtlEnterCriticalSection(&WorkerCriticalSection);
  1295. InterlockedIncrement(&NumFutureIOWorkItems);
  1296. if (NumIOWorkerThreads == 0) {
  1297. Status = RtlpStartIOWorkerThread();
  1298. }
  1299. RtlLeaveCriticalSection(&WorkerCriticalSection);
  1300. } else {
  1301. RtlEnterCriticalSection(&WorkerCriticalSection);
  1302. InterlockedIncrement(&NumFutureWorkItems);
  1303. if (NumWorkerThreads == 0) {
  1304. Status = RtlpStartWorkerThread();
  1305. }
  1306. RtlLeaveCriticalSection(&WorkerCriticalSection);
  1307. }
  1308. return Status;
  1309. }
  1310. VOID
  1311. RtlpReleaseWorker(ULONG Flags)
  1312. {
  1313. if (NEEDS_IO_THREAD(Flags)) {
  1314. ASSERT(NumFutureIOWorkItems > 0);
  1315. InterlockedDecrement(&NumFutureIOWorkItems);
  1316. } else {
  1317. ASSERT(NumFutureWorkItems > 0);
  1318. InterlockedDecrement(&NumFutureWorkItems);
  1319. }
  1320. }