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.

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