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.

781 lines
21 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1992.
  5. //
  6. // File: thread.cxx
  7. //
  8. // Contents: Job scheduler thread code.
  9. //
  10. // Classes: CWorkerThread
  11. // CWorkerThreadMgr
  12. //
  13. // Functions: RequestService
  14. // WorkerThreadStart
  15. //
  16. // History: 25-Oct-95 MarkBl Created
  17. //
  18. //----------------------------------------------------------------------------
  19. #include "..\pch\headers.hxx"
  20. #pragma hdrstop
  21. #include "debug.hxx"
  22. #include "queue.hxx"
  23. #include "task.hxx"
  24. #include "jpmgr.hxx"
  25. #include "globals.hxx"
  26. #include "thread.hxx"
  27. #include "proto.hxx"
  28. extern "C"
  29. HRESULT WorkerThreadStart(CWorkerThread *);
  30. //
  31. // Thread idle time before termination.
  32. //
  33. #define MAX_THREAD_IDLE_TIME (1000 * 60 * 3) // 3 minutes
  34. //+---------------------------------------------------------------------------
  35. //
  36. // Function: WorkerThreadStart
  37. //
  38. // Synopsis: The entry point for a worker thread.
  39. //
  40. // Arguments: [pWrkThrd] -- Worker thread object to start.
  41. //
  42. // Returns: TBD
  43. //
  44. // Effects: Calls the StartWorking function for the provided worker
  45. //
  46. //----------------------------------------------------------------------------
  47. extern "C"
  48. HRESULT WorkerThreadStart(CWorkerThread * pWrkThrd)
  49. {
  50. schDebugOut((DEB_USER3,
  51. "WorkerThreadStart pWrkThrd(0x%x)\n",
  52. pWrkThrd));
  53. HRESULT hr;
  54. //
  55. // Call the worker thread class
  56. //
  57. hr = pWrkThrd->StartWorking();
  58. delete pWrkThrd;
  59. return(hr);
  60. }
  61. // Class CWorkerThread
  62. //
  63. //+-------------------------------------------------------------------------
  64. //
  65. // Member: CWorkerThread::~CWorkerThread
  66. //
  67. // Synopsis: Destructor
  68. //
  69. // Arguments: N/A
  70. //
  71. // Returns: N/A
  72. //
  73. // Notes: A WorkerThread should only be deleted after its associated
  74. // thread has been terminated. This is noted by having the
  75. // _hThread being == NULL.
  76. //
  77. //--------------------------------------------------------------------------
  78. CWorkerThread::~CWorkerThread()
  79. {
  80. TRACE3(CWorkerThread, ~CWorkerThread);
  81. // a race condition at shutdown may result in this dtor being called *after*
  82. // the thread manager has been destructed.
  83. if (gpThreadMgr)
  84. gpThreadMgr->SignalThreadTermination();
  85. //
  86. // A precondition to destroying this thread is for the thread to
  87. // have been terminated already. If this isn't the case, we are
  88. // in big trouble.
  89. //
  90. // Terminating the thread is not good, since it leaves the threads
  91. // stack allocated in our address space. We also are not sure what
  92. // the thread is up to. It could have resources locked. But, we
  93. // also don't know what it will do next. We will have no record of it.
  94. //
  95. if (_hThread == NULL && _hWorkAvailable != NULL)
  96. {
  97. CloseHandle(_hWorkAvailable);
  98. }
  99. else
  100. {
  101. //
  102. // BUGBUG : Commenting out the assertion below until
  103. // (ServiceStatus != STOPPING) can be added.
  104. //
  105. #if 0
  106. schAssert(0 && (_hThread != NULL) &&
  107. "Destroying CWorkerThread while thread exists. Memory Leak!");
  108. #endif // 0
  109. }
  110. }
  111. //+---------------------------------------------------------------------------
  112. //
  113. // Member: CWorkerThread::AssignTask
  114. //
  115. // Synopsis: Assigns the task passed to this worker. A NULL task signals
  116. // thread termination.
  117. //
  118. // Arguments: [pTask] -- Task to be serviced.
  119. //
  120. // Returns: S_OK
  121. // E_FAIL -- Task already assigned.
  122. // TBD
  123. //
  124. // Notes: None.
  125. //
  126. //----------------------------------------------------------------------------
  127. HRESULT
  128. CWorkerThread::AssignTask(CTask * pTask)
  129. {
  130. schDebugOut((DEB_USER3,
  131. "CWorkerThread::AssignTask(0x%x) pTask(0x%x)\n",
  132. this,
  133. pTask));
  134. HRESULT hr;
  135. //
  136. // A must not already be assigned.
  137. //
  138. if (_pTask != NULL)
  139. {
  140. return(E_FAIL);
  141. }
  142. _pTask = pTask;
  143. if (_pTask != NULL)
  144. {
  145. _pTask->AddRef();
  146. }
  147. //
  148. // Signal the thread to process the task.
  149. //
  150. if (!SetEvent(_hWorkAvailable))
  151. {
  152. if (_pTask != NULL)
  153. {
  154. _pTask->Release();
  155. _pTask = NULL;
  156. }
  157. hr = HRESULT_FROM_WIN32(GetLastError());
  158. CHECK_HRESULT(hr);
  159. return(hr);
  160. }
  161. return(S_OK);
  162. }
  163. //+---------------------------------------------------------------------------
  164. //
  165. // Member: CWorkerThread::Initialize
  166. //
  167. // Synopsis: Performs initialization unable to be performed in the
  168. // constructor.
  169. //
  170. // Arguments: None.
  171. //
  172. // Returns: S_OK
  173. // TBD
  174. //
  175. // Effects: None.
  176. //
  177. //----------------------------------------------------------------------------
  178. HRESULT
  179. CWorkerThread::Initialize(void)
  180. {
  181. TRACE3(CWorkerThread, Initialize);
  182. HRESULT hr = S_OK;
  183. //
  184. // Create the event used to signal the thread to start working.
  185. //
  186. _hWorkAvailable = CreateEvent(NULL, FALSE, FALSE, NULL);
  187. if (_hWorkAvailable == NULL)
  188. {
  189. hr = HRESULT_FROM_WIN32(GetLastError());
  190. CHECK_HRESULT(hr);
  191. return(hr);
  192. }
  193. ULONG ThreadID;
  194. _hThread = CreateThread(NULL,
  195. WORKER_STACK_SIZE,
  196. (LPTHREAD_START_ROUTINE)WorkerThreadStart,
  197. this,
  198. 0,
  199. &ThreadID);
  200. if (_hThread != NULL)
  201. {
  202. if (!SetThreadPriority(_hThread, THREAD_PRIORITY_LOWEST))
  203. {
  204. hr = HRESULT_FROM_WIN32(GetLastError());
  205. CHECK_HRESULT(hr);
  206. }
  207. }
  208. else
  209. {
  210. hr = HRESULT_FROM_WIN32(GetLastError());
  211. CHECK_HRESULT(hr);
  212. }
  213. if (SUCCEEDED(hr))
  214. {
  215. gpThreadMgr->SignalThreadCreation();
  216. }
  217. return(hr);
  218. }
  219. //+---------------------------------------------------------------------------
  220. //
  221. // Member: CWorkerThread::StartWorking
  222. //
  223. // Synopsis: The almost endless loop for a worker thread
  224. //
  225. // Arguments: None.
  226. //
  227. // Returns: TBD
  228. //
  229. // Notes: This routine will sit in a loop, and wait for tasks to
  230. // be assigned. When a task is assigned, it will call the
  231. // PerformTask method of the task. If the assigned task
  232. // is NULL, then the thread will kill itself.
  233. //
  234. //----------------------------------------------------------------------------
  235. HRESULT
  236. CWorkerThread::StartWorking(void)
  237. {
  238. TRACE3(CWorkerThread, StartWorking);
  239. HRESULT hr = E_FAIL;
  240. BOOL fTerminationSignalled = FALSE;
  241. while (1)
  242. {
  243. //
  244. // Wait on the work available semaphore for the signal that a task
  245. // has been assigned.
  246. //
  247. DWORD dwRet = WaitForSingleObject(_hWorkAvailable,
  248. MAX_THREAD_IDLE_TIME);
  249. if (dwRet == WAIT_FAILED)
  250. {
  251. hr = HRESULT_FROM_WIN32(GetLastError());
  252. CHECK_HRESULT(hr);
  253. break;
  254. }
  255. else if (dwRet == WAIT_TIMEOUT)
  256. {
  257. //
  258. // This thread has timed out - see if it can be terminated.
  259. // If this thread object exists in the global free thread
  260. // pool, it can be terminated. If it doesn't exist in the pool,
  261. // it means this thread is in use, therefore re-enter the wait.
  262. //
  263. // More detail on the above: It's possible another thread
  264. // (thread A) can take this thread (thread B) for service just
  265. // after thread B's wait has timed out. In absence of the thread
  266. // pool check, thread B would exit out from under thread A.
  267. //
  268. CWorkerThread * pWrkThrd = gpThreadMgr->RemoveThread(_hThread);
  269. if (pWrkThrd == NULL)
  270. {
  271. //
  272. // This object doesn't exist in the pool. Assume another
  273. // thread has taken this thread for service, re-enter the
  274. // wait.
  275. //
  276. continue;
  277. }
  278. else
  279. {
  280. //
  281. // This thread has expired.
  282. //
  283. // NB: DO NOT delete the thread object!
  284. //
  285. schAssert(pWrkThrd == this);
  286. break;
  287. }
  288. }
  289. //
  290. // A NULL task signals thread termination.
  291. //
  292. if (_pTask == NULL)
  293. {
  294. fTerminationSignalled = TRUE;
  295. break;
  296. }
  297. //
  298. // Perform the task.
  299. //
  300. _pTask->PerformTask();
  301. _pTask->UnServiced();
  302. //
  303. // Release the task.
  304. //
  305. schDebugOut((DEB_USER3, "CWorkerThread::StartWorking(0x%x) "
  306. "Completed and Releasing task(0x%x)\n",
  307. this,
  308. _pTask));
  309. _pTask->Release();
  310. _pTask = NULL;
  311. //
  312. // Return this thread to the global pool, if the service is not
  313. // in the process of stopping. If the service is stopping, this
  314. // thread must exit.
  315. //
  316. if (IsServiceStopping())
  317. {
  318. fTerminationSignalled = TRUE;
  319. break;
  320. }
  321. else
  322. {
  323. gpThreadMgr->AddThread(this);
  324. }
  325. }
  326. //
  327. // Scavenger duty. Perform global job processor pool housekeeping prior
  328. // to thread termination.
  329. //
  330. // Do this only if the thread timed out; not when the thread is
  331. // instructed to terminate.
  332. //
  333. if (!fTerminationSignalled)
  334. {
  335. gpJobProcessorMgr->GarbageCollection();
  336. }
  337. //
  338. // By closing the handle, we allow the system to remove all remaining
  339. // traces of this thread.
  340. //
  341. BOOL fTmp = CloseHandle(_hThread);
  342. schAssert(fTmp && "Thread handle close failed - possible memory leak");
  343. _hThread = NULL;
  344. return(hr);
  345. }
  346. // Class CWorkerThreadMgr
  347. //
  348. //+---------------------------------------------------------------------------
  349. //
  350. // Member: CWorkerThreadMgr::~CWorkerThreadMgr
  351. //
  352. // Synopsis: Destructor.
  353. //
  354. // Arguments: N/A
  355. //
  356. // Returns: None.
  357. //
  358. // Notes: Memory leaks can occur with this destructor. This destructor
  359. // must only be called with process termination.
  360. //
  361. //----------------------------------------------------------------------------
  362. CWorkerThreadMgr::~CWorkerThreadMgr()
  363. {
  364. TRACE3(CWorkerThreadMgr, ~CWorkerThreadMgr);
  365. //
  366. // This destructor must only be called with process termination.
  367. //
  368. // If the global thread count is non-zero, there are threads
  369. // remaining. In this case, DO NOT delete the critical section or
  370. // close the event handle! An active thread could still access it.
  371. //
  372. // This will be a memory leak on process termination if the count is
  373. // non-zero, but the leak is far better than an a/v.
  374. //
  375. if (!_cThreadCount)
  376. {
  377. DeleteCriticalSection(&_csThreadMgrCritSection);
  378. CloseHandle(_hThreadTerminationEvent);
  379. }
  380. else
  381. {
  382. schDebugOut((DEB_FORCE,
  383. "CWorkerThreadMgr dtor(0x%x) : Unavoidable memory leak. " \
  384. "Leaking one or more CWorkerThread objects since worker " \
  385. "thread(s) are still active.\n",
  386. this));
  387. }
  388. }
  389. //+---------------------------------------------------------------------------
  390. //
  391. // Member: CWorkerThreadMgr::AddThread
  392. //
  393. // Synopsis: Adds the worker thread indicated to the pool.
  394. //
  395. // Arguments: [pWrkThrd] -- Worker thread to add.
  396. //
  397. // Returns: None.
  398. //
  399. // Notes: None.
  400. //
  401. //----------------------------------------------------------------------------
  402. void
  403. CWorkerThreadMgr::AddThread(CWorkerThread * pWrkThrd)
  404. {
  405. schDebugOut((DEB_USER3,
  406. "CWorkerThreadMgr::AddThread(0x%x) pWrkThrd(0x%x)\n",
  407. this,
  408. pWrkThrd));
  409. //
  410. // If the service is stopping, instruct the thread to terminate; else,
  411. // add it to the pool. Note, this is safe since only the threads
  412. // themselves perform AddThread - the thread is free.
  413. //
  414. if (IsServiceStopping())
  415. {
  416. pWrkThrd->AssignTask(NULL);
  417. return;
  418. }
  419. EnterCriticalSection(&_csThreadMgrCritSection);
  420. _WorkerThreadQueue.AddElement(pWrkThrd);
  421. LeaveCriticalSection(&_csThreadMgrCritSection);
  422. }
  423. //+---------------------------------------------------------------------------
  424. //
  425. // Member: CWorkerThreadMgr::Initialize
  426. //
  427. // Synopsis: Creates the private data member, _hThreadTerminationEvent.
  428. //
  429. // Arguments: None.
  430. //
  431. // Returns: TRUE -- Creation succeeded;
  432. // FALSE -- otherwise.
  433. //
  434. // Notes: None.
  435. //
  436. //----------------------------------------------------------------------------
  437. BOOL
  438. CWorkerThreadMgr::Initialize(void)
  439. {
  440. if ((_hThreadTerminationEvent = CreateEvent(NULL,
  441. TRUE,
  442. FALSE,
  443. NULL)) == NULL)
  444. {
  445. schDebugOut((DEB_ERROR,
  446. "CWorkerThreadMgr::Initialize(0x%x) CreateEvent failed, " \
  447. "status = 0x%lx\n",
  448. this,
  449. GetLastError()));
  450. return(FALSE);
  451. }
  452. return(TRUE);
  453. }
  454. //+---------------------------------------------------------------------------
  455. //
  456. // Member: CWorkerThreadMgr::Shutdown
  457. //
  458. // Synopsis: This member is called once, no wait, early on in the schedule
  459. // service's shutdown sequence. It relinquishes all free threads.
  460. // Done so by signalling the threads in the pool to terminate.
  461. //
  462. // This member is called a second time, with the wait option, to
  463. // ensure any busy (non-free) worker threads have terminated
  464. // also.
  465. //
  466. // Arguments: [fWait] -- Flag instructing this method to wait indefinitely
  467. // until all worker threads have terminated.
  468. // ** Use only in this case of service stop **
  469. //
  470. // Returns: TRUE -- All worker threads terminated.
  471. // FALSE -- One ore more worker threads still active.
  472. //
  473. // Notes: It must not be possible for worker threads to enter the
  474. // thread pool critical section in their termination code paths.
  475. // Otherwise, a nested, blocking section will result.
  476. //
  477. //----------------------------------------------------------------------------
  478. BOOL
  479. CWorkerThreadMgr::Shutdown(BOOL fWait)
  480. {
  481. #define THRDMGR_INITIAL_WAIT_TIME 2000 // 2 seconds.
  482. EnterCriticalSection(&_csThreadMgrCritSection);
  483. CWorkerThread * pWrkThrd;
  484. while ((pWrkThrd = _WorkerThreadQueue.RemoveElement()) != NULL)
  485. {
  486. pWrkThrd->AssignTask(NULL); // NULL task signals termination.
  487. }
  488. LeaveCriticalSection(&_csThreadMgrCritSection);
  489. //
  490. // Optionally wait for outstanding worker threads to terminate before
  491. // returning. This is only to be done during service shutdown. All worker
  492. // threads have logic to terminate with service shutdown.
  493. //
  494. // To actually have to wait is a very rare. Only occurring in a rare
  495. // case, or if this machine is under *extreme* loads, or the absolutely
  496. // unexpected occurs. The rare case mentioned is a small window where
  497. // a job processor object is spun up immediately prior to the service
  498. // shutdown sequence and its initialization phase coincides with
  499. // CJobProcessor::Shutdown().
  500. //
  501. if (fWait)
  502. {
  503. //
  504. // On destruction, each thread decrements the global thread count
  505. // and sets the thread termination event.
  506. //
  507. DWORD dwWaitTime = THRDMGR_INITIAL_WAIT_TIME;
  508. DWORD dwRet;
  509. while (_cThreadCount)
  510. {
  511. //
  512. // Wait initially THRDMGR_INITIAL_WAIT_TIME amount of time.
  513. // If this wait times-out, re-issue a Shutdown of the global
  514. // processor object then wait infinitely.
  515. //
  516. if ((dwRet = WaitForSingleObject(_hThreadTerminationEvent,
  517. dwWaitTime)) == WAIT_OBJECT_0)
  518. {
  519. ResetEvent(_hThreadTerminationEvent);
  520. }
  521. else if (dwRet == WAIT_TIMEOUT)
  522. {
  523. //
  524. // Address the case where the job processor was spun up
  525. // inadvertently. This will shut it down.
  526. //
  527. gpJobProcessorMgr->Shutdown();
  528. dwWaitTime = INFINITE;
  529. }
  530. else
  531. {
  532. //
  533. // This return code will notify the service cleanup code
  534. // to not free up resources associated with the worker
  535. // threads. Otherwise, they may fault.
  536. //
  537. return(FALSE);
  538. }
  539. }
  540. }
  541. return(TRUE);
  542. }
  543. //+---------------------------------------------------------------------------
  544. //
  545. // Member: CWorkerThreadMgr::RemoveThread
  546. //
  547. // Synopsis: Remove & return a thread from the pool.
  548. //
  549. // Arguments: None.
  550. //
  551. // Returns: CWorkerThread * -- Returned thread.
  552. // NULL -- Pool empty.
  553. //
  554. // Notes: None.
  555. //
  556. //----------------------------------------------------------------------------
  557. CWorkerThread *
  558. CWorkerThreadMgr::RemoveThread(void)
  559. {
  560. TRACE3(CWorkerThread, RemoveThread);
  561. CWorkerThread * pWrkThrd = NULL;
  562. EnterCriticalSection(&_csThreadMgrCritSection);
  563. pWrkThrd = _WorkerThreadQueue.RemoveElement();
  564. LeaveCriticalSection(&_csThreadMgrCritSection);
  565. return(pWrkThrd);
  566. }
  567. //+---------------------------------------------------------------------------
  568. //
  569. // Member: CWorkerThreadMgr::RemoveThread
  570. //
  571. // Synopsis: Remove & return the thread from the pool with the associated
  572. // handle.
  573. //
  574. // Arguments: [hThread] -- Target thread handle.
  575. //
  576. // Returns: CWorkerThread * -- Found it.
  577. // NULL -- Worker thread not found.
  578. //
  579. // Notes: None.
  580. //
  581. //----------------------------------------------------------------------------
  582. CWorkerThread *
  583. CWorkerThreadMgr::RemoveThread(HANDLE hThread)
  584. {
  585. schDebugOut((DEB_USER3,
  586. "CWorkerThreadMgr::RemoveThread(0x%x) hThread(0x%x)\n",
  587. this,
  588. hThread));
  589. CWorkerThread * pWrkThrd;
  590. EnterCriticalSection(&_csThreadMgrCritSection);
  591. pWrkThrd = _WorkerThreadQueue.GetFirstElement();
  592. while (pWrkThrd != NULL)
  593. {
  594. if (pWrkThrd->GetHandle() == hThread)
  595. {
  596. _WorkerThreadQueue.RemoveElement(pWrkThrd);
  597. break;
  598. }
  599. pWrkThrd = pWrkThrd->Next();
  600. }
  601. LeaveCriticalSection(&_csThreadMgrCritSection);
  602. return(pWrkThrd);
  603. }
  604. //+---------------------------------------------------------------------------
  605. //
  606. // Function: RequestService
  607. //
  608. // Synopsis: Request a free worker thread from the global thread pool to
  609. // service the task indicated. If no free threads exist in the
  610. // pool, create a new one.
  611. //
  612. // Arguments: [pTask] -- Task to undergo service.
  613. //
  614. // Returns: S_OK
  615. // E_OUTOFMEMORY
  616. // TBD
  617. //
  618. // Notes: None.
  619. //
  620. //----------------------------------------------------------------------------
  621. HRESULT
  622. RequestService(CTask * pTask)
  623. {
  624. schDebugOut((DEB_USER3, "RequestService pTask(0x%x)\n", pTask));
  625. HRESULT hr = S_OK;
  626. //
  627. // Take no requests if the service is stopping.
  628. //
  629. if (IsServiceStopping())
  630. {
  631. schDebugOut((DEB_ERROR,
  632. "RequestService pTask(0x%x) Service stopping - request " \
  633. "refused\n",
  634. pTask));
  635. return(E_FAIL);
  636. }
  637. //
  638. // Obtain a free thread from the global thread pool.
  639. //
  640. CWorkerThread * pWrkThrd = gpThreadMgr->RemoveThread();
  641. if (pWrkThrd == NULL)
  642. {
  643. //
  644. // Create a new worker thread object if none were available.
  645. //
  646. pWrkThrd = new CWorkerThread;
  647. if (pWrkThrd == NULL)
  648. {
  649. hr = E_OUTOFMEMORY;
  650. CHECK_HRESULT(hr);
  651. return(hr);
  652. }
  653. hr = pWrkThrd->Initialize();
  654. if (FAILED(hr))
  655. {
  656. delete pWrkThrd;
  657. return(hr);
  658. }
  659. }
  660. hr = pWrkThrd->AssignTask(pTask);
  661. if (FAILED(hr))
  662. {
  663. delete pWrkThrd;
  664. }
  665. return(hr);
  666. }