Source code of Windows XP (NT5)
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.

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