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.

826 lines
18 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. frsthrd.c
  5. Abstract:
  6. Simple thread management in addition to the queue management.
  7. Author:
  8. Billy J. Fuller 26-Mar-1997
  9. Revised:
  10. David Orbits - May 2000 : Revised naming.
  11. Environment
  12. User mode winnt
  13. --*/
  14. #include <ntreppch.h>
  15. #pragma hdrstop
  16. #include <frs.h>
  17. #include <perrepsr.h>
  18. #define THSUP_THREAD_TOMBSTONE (5 * 1000)
  19. //
  20. // Global list of threads
  21. //
  22. LIST_ENTRY FrsThreadList;
  23. //
  24. // Protects FrsThreadList
  25. //
  26. CRITICAL_SECTION FrsThreadCriticalSection;
  27. //
  28. // Exiting threads can queue a "wait" request
  29. //
  30. COMMAND_SERVER ThCs;
  31. #if _MSC_FULL_VER >= 13008827
  32. #pragma warning(push)
  33. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  34. #endif
  35. DWORD
  36. ThSupMainCs(
  37. PVOID Arg
  38. )
  39. /*++
  40. Routine Description:
  41. Entry point for thread command server thread. This thread
  42. is needed for abort and exit processing; hence this command
  43. server is never aborted and the thread never exits!
  44. Arguments:
  45. Arg - thread
  46. Return Value:
  47. None.
  48. --*/
  49. {
  50. #undef DEBSUB
  51. #define DEBSUB "ThSupMainCs:"
  52. PCOMMAND_PACKET Cmd;
  53. PCOMMAND_SERVER Cs;
  54. PFRS_THREAD FrsThread = Arg;
  55. Cs = FrsThread->Data;
  56. FRS_ASSERT(Cs == &ThCs);
  57. //
  58. // Our exit routine sets Data to non-NULL as a exit flag
  59. //
  60. while (TRUE) {
  61. Cmd = FrsGetCommandServer(&ThCs);
  62. if (Cmd == NULL) {
  63. continue;
  64. }
  65. FRS_ASSERT(Cmd->Command == CMD_WAIT);
  66. FRS_ASSERT(ThThread(Cmd));
  67. //
  68. // Any thread using the CMD_WAIT must get a thread reference the thread
  69. // before enqueueing the command to protect against multiple waiters.
  70. //
  71. // Wait for the thread to terminate. The assumption is that the thread
  72. // associated with the FrsThread struct will soon be terminating, ending the wait.
  73. //
  74. FrsThread = ThThread(Cmd);
  75. ThSupWaitThread(FrsThread, INFINITE);
  76. //
  77. // When the last ref is dropped the FrsThread struct is freed.
  78. //
  79. ThSupReleaseRef(FrsThread);
  80. FrsCompleteCommand(Cmd, ERROR_SUCCESS);
  81. }
  82. return ERROR_SUCCESS;
  83. }
  84. #if _MSC_FULL_VER >= 13008827
  85. #pragma warning(pop)
  86. #endif
  87. DWORD
  88. ThSupExitWithTombstone(
  89. PFRS_THREAD FrsThread
  90. )
  91. /*++
  92. Routine Description:
  93. Mark the thread as tombstoned. If this thread does not exit within that
  94. time, any calls to ThSupWaitThread() return a timeout error.
  95. Arguments:
  96. FrsThread
  97. Return Value:
  98. ERROR_SUCCESS
  99. --*/
  100. {
  101. #undef DEBSUB
  102. #define DEBSUB "ThSupExitWithTombstone:"
  103. //
  104. // If this thread does not exit within the THSUP_THREAD_TOMBSTONE interval then
  105. // don't wait on it and count it as an exit timeout.
  106. //
  107. FrsThread->ExitTombstone = GetTickCount();
  108. if (FrsThread->ExitTombstone == 0) {
  109. FrsThread->ExitTombstone = 1;
  110. }
  111. return ERROR_SUCCESS;
  112. }
  113. DWORD
  114. ThSupExitThreadNOP(
  115. PVOID Arg
  116. )
  117. /*++
  118. Routine Description:
  119. Use this exit function when you don't want the cleanup code
  120. to kill the thread but you have no exit processing to do. E.g.,
  121. a command server.
  122. Arguments:
  123. None.
  124. Return Value:
  125. ERROR_SUCCESS
  126. --*/
  127. {
  128. #undef DEBSUB
  129. #define DEBSUB "ThSupExitThreadNOP:"
  130. return ERROR_SUCCESS;
  131. }
  132. VOID
  133. ThSupInitialize(
  134. VOID
  135. )
  136. /*++
  137. Routine Description:
  138. Initialize the FRS thread subsystem. Must be called before any other
  139. thread function and must be called only once.
  140. Arguments:
  141. None.
  142. Return Value:
  143. None.
  144. --*/
  145. {
  146. #undef DEBSUB
  147. #define DEBSUB "ThSupInitialize:"
  148. InitializeCriticalSection(&FrsThreadCriticalSection);
  149. InitializeListHead(&FrsThreadList);
  150. FrsInitializeCommandServer(&ThCs, 1, L"ThCs: Wait on threads.", ThSupMainCs);
  151. }
  152. PVOID
  153. ThSupGetThreadData(
  154. PFRS_THREAD FrsThread
  155. )
  156. /*++
  157. Routine Description:
  158. Return the thread specific data portion of the thread context.
  159. Arguments:
  160. FrsThread - thread context.
  161. Return Value:
  162. Thread specific data
  163. --*/
  164. {
  165. #undef DEBSUB
  166. #define DEBSUB "ThSupGetThreadData:"
  167. return (FrsThread->Data);
  168. }
  169. PFRS_THREAD
  170. ThSupAllocThread(
  171. PWCHAR Name,
  172. PVOID Param,
  173. DWORD (*Main)(PVOID),
  174. DWORD (*Exit)(PVOID)
  175. )
  176. /*++
  177. Routine Description:
  178. Allocate a thread context and call its Init routine.
  179. Arguments:
  180. Param - parameter to the thread
  181. Main - entry point
  182. Exit - called to force thread to exit
  183. Return Value:
  184. Thread context.
  185. --*/
  186. {
  187. #undef DEBSUB
  188. #define DEBSUB "ThSupAllocThread:"
  189. ULONG Status;
  190. PFRS_THREAD FrsThread;
  191. //
  192. // Create a thread context for a soon-to-be-running thread
  193. //
  194. FrsThread = FrsAllocType(THREAD_TYPE);
  195. FrsThread->Name = Name;
  196. FrsThread->Running = TRUE;
  197. FrsThread->Ref = 1;
  198. FrsThread->Main = Main;
  199. FrsThread->Exit = Exit;
  200. FrsThread->Data = Param;
  201. InitializeListHead(&FrsThread->List);
  202. //
  203. // Add to global list of threads (Unless this is our command server)
  204. //
  205. if (Main != ThSupMainCs) {
  206. EnterCriticalSection(&FrsThreadCriticalSection);
  207. InsertTailList(&FrsThreadList, &FrsThread->List);
  208. LeaveCriticalSection(&FrsThreadCriticalSection);
  209. }
  210. return FrsThread;
  211. }
  212. VOID
  213. ThSupReleaseRef(
  214. PFRS_THREAD FrsThread
  215. )
  216. /*++
  217. Routine Description:
  218. Dec the thread's reference count. If the count goes to 0, free
  219. the thread context.
  220. Arguments:
  221. FrsThread - thread context
  222. Return Value:
  223. None.
  224. --*/
  225. {
  226. #undef DEBSUB
  227. #define DEBSUB "ThSupReleaseRef:"
  228. if (FrsThread == NULL) {
  229. return;
  230. }
  231. FRS_ASSERT(FrsThread->Main != ThSupMainCs || FrsThread->Running);
  232. //
  233. // If the ref count goes to 0 and the thread isn't running, free the context
  234. //
  235. EnterCriticalSection(&FrsThreadCriticalSection);
  236. FRS_ASSERT(FrsThread->Ref > 0);
  237. if (--FrsThread->Ref == 0 && !FrsThread->Running) {
  238. FrsRemoveEntryList(&FrsThread->List);
  239. } else {
  240. FrsThread = NULL;
  241. }
  242. LeaveCriticalSection(&FrsThreadCriticalSection);
  243. //
  244. // ref count is not 0 or the thread is still running
  245. //
  246. if (FrsThread == NULL) {
  247. return;
  248. }
  249. //
  250. // Ref count is 0; Close the thread's handle.
  251. //
  252. FRS_CLOSE(FrsThread->Handle);
  253. FrsThread = FrsFreeType(FrsThread);
  254. }
  255. PFRS_THREAD
  256. ThSupEnumThreads(
  257. PFRS_THREAD FrsThread
  258. )
  259. /*++
  260. Routine Description:
  261. This routine scans the list of threads. If FrsThread is NULL,
  262. the current head is returned. Otherwise, the next entry is returned.
  263. If FrsThead is non-Null its ref count is decremented.
  264. Arguments:
  265. FrsThread - thread context or NULL
  266. Return Value:
  267. The next thread context or NULL if we hit the end of the list.
  268. A reference is taken on the returned thread.
  269. --*/
  270. {
  271. #undef DEBSUB
  272. #define DEBSUB "ThSupEnumThreads:"
  273. PLIST_ENTRY Entry;
  274. PFRS_THREAD NextFrsThread;
  275. //
  276. // Get the next thread context (the head of the list if FrsThread is NULL)
  277. //
  278. EnterCriticalSection(&FrsThreadCriticalSection);
  279. Entry = (FrsThread != NULL) ? GetListNext(&FrsThread->List)
  280. : GetListNext(&FrsThreadList);
  281. if (Entry == &FrsThreadList) {
  282. //
  283. // back at the head of the list
  284. //
  285. NextFrsThread = NULL;
  286. } else {
  287. //
  288. // Increment the ref count
  289. //
  290. NextFrsThread = CONTAINING_RECORD(Entry, FRS_THREAD, List);
  291. NextFrsThread->Ref++;
  292. }
  293. LeaveCriticalSection(&FrsThreadCriticalSection);
  294. //
  295. // Release the reference on the old thread context.
  296. //
  297. if (FrsThread != NULL) {
  298. ThSupReleaseRef(FrsThread);
  299. }
  300. return NextFrsThread;
  301. }
  302. BOOL
  303. ThSupCreateThread(
  304. PWCHAR Name,
  305. PVOID Param,
  306. DWORD (*Main)(PVOID),
  307. DWORD (*Exit)(PVOID)
  308. )
  309. /*++
  310. Routine Description:
  311. Kick off the thread and return its context.
  312. Note: The caller must release thread reference when done
  313. Arguments:
  314. Param - parameter to the thread
  315. Main - entry point
  316. Exit - called to force thread to exit
  317. Return Value:
  318. Thread context. Caller must call ThSupReleaseRef() to release it.
  319. --*/
  320. {
  321. #undef DEBSUB
  322. #define DEBSUB "ThSupCreateThread:"
  323. PFRS_THREAD FrsThread;
  324. //
  325. // Allocate a thread context
  326. //
  327. FrsThread = ThSupAllocThread(Name, Param, Main, Exit);
  328. if (FrsThread == NULL) {
  329. return FALSE;
  330. }
  331. //
  332. // Kick off the thread
  333. //
  334. FrsThread->Handle = (HANDLE) CreateThread(NULL,
  335. 10000,
  336. Main,
  337. (PVOID)FrsThread,
  338. 0,
  339. &FrsThread->Id);
  340. //
  341. // thread is not running. The following ThSupReleaseRef will clean up.
  342. //
  343. if (!HANDLE_IS_VALID(FrsThread->Handle)) {
  344. DPRINT_WS(0, "Can't start thread; ",GetLastError());
  345. FrsThread->Running = FALSE;
  346. ThSupReleaseRef(FrsThread);
  347. return FALSE;
  348. } else {
  349. //
  350. // Increment the Threads started counter
  351. //
  352. PM_INC_CTR_SERVICE(PMTotalInst, ThreadsStarted, 1);
  353. DPRINT3(4, ":S: Starting thread %ws: Id %d (%08x)\n",
  354. Name, FrsThread->Id, FrsThread->Id);
  355. ThSupReleaseRef(FrsThread);
  356. DbgCaptureThreadInfo2(Name, Main, FrsThread->Id);
  357. return TRUE;
  358. }
  359. }
  360. DWORD
  361. ThSupWaitThread(
  362. PFRS_THREAD FrsThread,
  363. DWORD Millisec
  364. )
  365. /*++
  366. Routine Description:
  367. Wait at most MilliSeconds for the thread to exit. If the thread has set
  368. a wait tombstone (i.e. it is terminating) then don't wait longer than the
  369. time remaining on the tombstone.
  370. Arguments:
  371. FrsThread - thread context
  372. Millisec - Time to wait. Use INFINITE if no timeout desired.
  373. Return Value:
  374. Status of the wait if timeout or the exit code of the thread.
  375. --*/
  376. {
  377. #undef DEBSUB
  378. #define DEBSUB "ThSupWaitThread:"
  379. ULONGLONG CreateTime, ExitTime, KernelTime, UserTime;
  380. DWORD WStatus, Status, ExitCode;
  381. DWORD Beg, End, TimeSinceTombstone;
  382. //
  383. // No problems waiting for this one!
  384. //
  385. if (!FrsThread || !HANDLE_IS_VALID(FrsThread->Handle)) {
  386. return ERROR_SUCCESS;
  387. }
  388. //
  389. // Wait awhile for the thread to exit
  390. //
  391. DPRINT1(1, ":S: %ws: Waiting\n", FrsThread->Name);
  392. Beg = GetTickCount();
  393. if (FrsThread->ExitTombstone != 0) {
  394. //
  395. // The thread has registered an exit tombstone so don't wait past that
  396. // time. Simply return a timeout error. Note: GetTickCount has a
  397. // period of 49.7 days so the unsigned difference handles the wrap problem.
  398. //
  399. TimeSinceTombstone = Beg - FrsThread->ExitTombstone;
  400. if (TimeSinceTombstone >= THSUP_THREAD_TOMBSTONE) {
  401. //
  402. // Tombstone expired
  403. //
  404. DPRINT1(1, ":S: %ws: Tombstone expired\n", FrsThread->Name);
  405. Status = WAIT_TIMEOUT;
  406. } else {
  407. //
  408. // Tombstone has a ways to go; wait only up to the tombstone time.
  409. //
  410. DPRINT1(1, ":S: %ws: Tombstone expiring\n", FrsThread->Name);
  411. Status = WaitForSingleObject(FrsThread->Handle,
  412. THSUP_THREAD_TOMBSTONE - TimeSinceTombstone);
  413. }
  414. } else {
  415. //
  416. // no tombstone; wait the requested time
  417. //
  418. DPRINT1(1, ":S: %ws: normal wait\n", FrsThread->Name);
  419. Status = WaitForSingleObject(FrsThread->Handle, Millisec);
  420. }
  421. //
  422. // Adjust the error status based on the outcome
  423. //
  424. if ((Status == WAIT_OBJECT_0) || (Status == WAIT_ABANDONED)) {
  425. DPRINT1_WS(1, ":S: %ws: wait successful. ", FrsThread->Name, Status);
  426. WStatus = ERROR_SUCCESS;
  427. } else {
  428. if (Status == WAIT_FAILED) {
  429. WStatus = GetLastError();
  430. DPRINT1_WS(1, ":S: %ws: wait failed;", FrsThread->Name, WStatus);
  431. } else {
  432. DPRINT1_WS(1, ":S: %ws: wait timed out. ", FrsThread->Name, Status);
  433. WStatus = ERROR_TIMEOUT;
  434. }
  435. }
  436. //
  437. // Wait over
  438. //
  439. End = GetTickCount();
  440. DPRINT2_WS(1, ":S: Done waiting for thread %ws (%d ms); ", FrsThread->Name, End - Beg, WStatus);
  441. //
  442. // Thread has exited. Get exit status and set thread struct as "not running".
  443. //
  444. if (WIN_SUCCESS(WStatus)) {
  445. FrsThread->Running = FALSE;
  446. if (GetExitCodeThread(FrsThread->Handle, &ExitCode)) {
  447. WStatus = ExitCode;
  448. DPRINT1_WS(1, ":S: %ws: exit code - \n", FrsThread->Name, WStatus);
  449. }
  450. }
  451. if (GetThreadTimes(FrsThread->Handle,
  452. (PFILETIME)&CreateTime,
  453. (PFILETIME)&ExitTime,
  454. (PFILETIME)&KernelTime,
  455. (PFILETIME)&UserTime)) {
  456. //
  457. // Hasn't exited, yet
  458. //
  459. if (ExitTime < CreateTime) {
  460. ExitTime = CreateTime;
  461. }
  462. DPRINT4(4, ":S: %-15ws: %8d CPU Seconds (%d kernel, %d elapsed)\n",
  463. FrsThread->Name,
  464. (DWORD)((KernelTime + UserTime) / (10 * 1000 * 1000)),
  465. (DWORD)((KernelTime) / (10 * 1000 * 1000)),
  466. (DWORD)((ExitTime - CreateTime) / (10 * 1000 * 1000)));
  467. }
  468. return WStatus;
  469. }
  470. DWORD
  471. ThSupExitThreadGroup(
  472. IN DWORD (*Main)(PVOID)
  473. )
  474. /*++
  475. Routine Description:
  476. Force the group of threads with the given Main function to exit by
  477. calling their exit routine. Wait for the threads to exit.
  478. Arguments:
  479. Main - Main function or NULL
  480. Return Value:
  481. None.
  482. --*/
  483. {
  484. #undef DEBSUB
  485. #define DEBSUB "ThSupExitThreadGroup:"
  486. DWORD WStatus;
  487. DWORD RetWStatus;
  488. PFRS_THREAD FrsThread;
  489. ULONGLONG CreateTime;
  490. ULONGLONG ExitTime;
  491. ULONGLONG KernelTime;
  492. ULONGLONG UserTime;
  493. //
  494. // call the threads exit function (forcibly terminate if none)
  495. //
  496. FrsThread = NULL;
  497. while (FrsThread = ThSupEnumThreads(FrsThread)) {
  498. if (Main == NULL || Main == FrsThread->Main) {
  499. ThSupExitSingleThread(FrsThread);
  500. }
  501. }
  502. //
  503. // wait for the threads to exit
  504. //
  505. RetWStatus = ERROR_SUCCESS;
  506. FrsThread = NULL;
  507. while (FrsThread = ThSupEnumThreads(FrsThread)) {
  508. if (Main == NULL || Main == FrsThread->Main) {
  509. WStatus = ThSupWaitThread(FrsThread, INFINITE);
  510. if (!WIN_SUCCESS(WStatus)) {
  511. RetWStatus = WStatus;
  512. }
  513. }
  514. }
  515. if (GetThreadTimes(GetCurrentThread(),
  516. (PFILETIME)&CreateTime,
  517. (PFILETIME)&ExitTime,
  518. (PFILETIME)&KernelTime,
  519. (PFILETIME)&UserTime)) {
  520. //
  521. // Hasn't exited, yet
  522. //
  523. if (ExitTime < CreateTime) {
  524. ExitTime = CreateTime;
  525. }
  526. DPRINT4(4, ":S: %-15ws: %8d CPU Seconds (%d kernel, %d elapsed)\n",
  527. L"SHUTDOWN",
  528. (DWORD)((KernelTime + UserTime) / (10 * 1000 * 1000)),
  529. (DWORD)((KernelTime) / (10 * 1000 * 1000)),
  530. (DWORD)((ExitTime - CreateTime) / (10 * 1000 * 1000)));
  531. }
  532. if (GetProcessTimes(ProcessHandle,
  533. (PFILETIME)&CreateTime,
  534. (PFILETIME)&ExitTime,
  535. (PFILETIME)&KernelTime,
  536. (PFILETIME)&UserTime)) {
  537. //
  538. // Hasn't exited, yet
  539. //
  540. if (ExitTime < CreateTime) {
  541. ExitTime = CreateTime;
  542. }
  543. DPRINT4(0, ":S: %-15ws: %8d CPU Seconds (%d kernel, %d elapsed)\n",
  544. L"PROCESS",
  545. (DWORD)((KernelTime + UserTime) / (10 * 1000 * 1000)),
  546. (DWORD)((KernelTime) / (10 * 1000 * 1000)),
  547. (DWORD)((ExitTime - CreateTime) / (10 * 1000 * 1000)));
  548. }
  549. return RetWStatus;
  550. }
  551. VOID
  552. ThSupExitSingleThread(
  553. PFRS_THREAD FrsThread
  554. )
  555. /*++
  556. Routine Description:
  557. Force the thread to exit
  558. Arguments:
  559. None.
  560. Return Value:
  561. None.
  562. --*/
  563. {
  564. #undef DEBSUB
  565. #define DEBSUB "ThSupExitSingleThread:"
  566. //
  567. // call the thread's exit function (forcibly terminate if none)
  568. //
  569. if (FrsThread->Exit != NULL) {
  570. (*FrsThread->Exit)(FrsThread);
  571. } else {
  572. //
  573. // No exit function; forcibly terminate
  574. //
  575. if (HANDLE_IS_VALID(FrsThread->Handle)) {
  576. TerminateThread(FrsThread->Handle, STATUS_UNSUCCESSFUL);
  577. }
  578. }
  579. //
  580. // Increment the Threads exited counter
  581. //
  582. PM_INC_CTR_SERVICE(PMTotalInst, ThreadsExited, 1);
  583. }
  584. PFRS_THREAD
  585. ThSupGetThread(
  586. DWORD (*Main)(PVOID)
  587. )
  588. /*++
  589. Routine Description:
  590. Locate a thread whose entry point is Main.
  591. Arguments:
  592. Main - entry point to search for.
  593. Return Value:
  594. thread context
  595. --*/
  596. {
  597. #undef DEBSUB
  598. #define DEBSUB "ThSupGetThread:"
  599. PFRS_THREAD FrsThread;
  600. //
  601. // Scan the list of threads looking for one whose entry point is Main
  602. //
  603. FrsThread = NULL;
  604. while (FrsThread = ThSupEnumThreads(FrsThread)) {
  605. if (FrsThread->Main == Main) {
  606. return FrsThread;
  607. }
  608. }
  609. return NULL;
  610. }
  611. VOID
  612. ThSupAcquireRef(
  613. PFRS_THREAD FrsThread
  614. )
  615. /*++
  616. Routine Description:
  617. Inc the thread's reference count.
  618. Arguments:
  619. FrsThread - thread context
  620. Return Value:
  621. None.
  622. --*/
  623. {
  624. #undef DEBSUB
  625. #define DEBSUB "ThSupAcquireRef:"
  626. FRS_ASSERT(FrsThread);
  627. FRS_ASSERT(FrsThread->Running);
  628. //
  629. // If the ref count goes to 0 and the thread isn't running, free the context
  630. //
  631. EnterCriticalSection(&FrsThreadCriticalSection);
  632. ++FrsThread->Ref;
  633. LeaveCriticalSection(&FrsThreadCriticalSection);
  634. }
  635. VOID
  636. ThSupSubmitThreadExitCleanup(
  637. PFRS_THREAD FrsThread
  638. )
  639. /*++
  640. Routine Description:
  641. Submit a wait command for this thread to the thread command server.
  642. The thread command server (ThQs) will do an infinte wait on this thread's exit
  643. and drop the reference on its thread struct so it can be cleaned up.
  644. The assumption is that the thread associated with the FrsThread struct will
  645. soon be terminating.
  646. Arguments:
  647. FrsThread - thread context
  648. Return Value:
  649. None.
  650. --*/
  651. {
  652. #undef DEBSUB
  653. #define DEBSUB "ThSupSubmitThreadExitCleanup:"
  654. PCOMMAND_PACKET Cmd;
  655. //
  656. // Reference the thread until after the wait has completed to guard
  657. // against the case of multiple waiters
  658. //
  659. ThSupAcquireRef(FrsThread);
  660. //
  661. // Allocate a command packet and send the command off to the
  662. // thread command server.
  663. //
  664. Cmd = FrsAllocCommand(&ThCs.Queue, CMD_WAIT);
  665. ThThread(Cmd) = FrsThread;
  666. FrsSubmitCommandServer(&ThCs, Cmd);
  667. }