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.

701 lines
15 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. tpswait.cpp
  5. Abstract:
  6. Contains Win32 thread pool services wait functions
  7. Contents:
  8. TerminateWaiters
  9. SHRegisterWaitForSingleObject
  10. SHUnregisterWait
  11. (InitializeWaitThreadPool)
  12. (FindWaitThreadInfo)
  13. (AddWait)
  14. (RemoveWait)
  15. (WaitThread)
  16. Author:
  17. Richard L Firth (rfirth) 10-Feb-1998
  18. Environment:
  19. Win32 user-mode
  20. Notes:
  21. Taken from NT-specific code written by Gurdeep Singh Pall (gurdeep)
  22. Revision History:
  23. 10-Feb-1998 rfirth
  24. Created
  25. --*/
  26. #include "priv.h"
  27. #include "threads.h"
  28. #include "tpsclass.h"
  29. #include "tpswait.h"
  30. //
  31. // private prototypes
  32. //
  33. PRIVATE
  34. DWORD
  35. InitializeWaitThreadPool(
  36. VOID
  37. );
  38. PRIVATE
  39. DWORD
  40. FindWaitThreadInfo(
  41. OUT CWaitThreadInfo * * pInfo
  42. );
  43. PRIVATE
  44. VOID
  45. AddWait(
  46. IN OUT CWaitAddRequest * pRequest
  47. );
  48. PRIVATE
  49. VOID
  50. RemoveWait(
  51. IN CWaitRemoveRequest * pRequest
  52. );
  53. PRIVATE
  54. VOID
  55. WaitThread(
  56. IN HANDLE hEvent
  57. );
  58. //
  59. // global data
  60. //
  61. CDoubleLinkedList g_WaitThreads;
  62. CCriticalSection_NoCtor g_WaitCriticalSection;
  63. BOOL g_StartedWaitInitialization = FALSE;
  64. BOOL g_CompletedWaitInitialization = FALSE;
  65. BOOL g_bDeferredWaiterTermination = FALSE;
  66. //
  67. // functions
  68. //
  69. VOID
  70. TerminateWaiters(
  71. VOID
  72. )
  73. /*++
  74. Routine Description:
  75. Terminate waiter threads and global variables
  76. Arguments:
  77. None.
  78. Return Value:
  79. None.
  80. --*/
  81. {
  82. if (g_CompletedWaitInitialization) {
  83. g_WaitCriticalSection.Acquire();
  84. while (!g_WaitThreads.IsEmpty()) {
  85. CWaitThreadInfo * pInfo;
  86. pInfo = (CWaitThreadInfo *)g_WaitThreads.RemoveHead();
  87. HANDLE hThread = pInfo->GetHandle();
  88. pInfo->SetHandle(NULL);
  89. QueueNullFunc(hThread);
  90. SleepEx(0, FALSE);
  91. }
  92. g_WaitCriticalSection.Release();
  93. g_WaitCriticalSection.Terminate();
  94. g_StartedWaitInitialization = FALSE;
  95. g_CompletedWaitInitialization = FALSE;
  96. }
  97. if (TlsGetValue(g_TpsTls) == (LPVOID)TPS_WAITER_SIGNATURE) {
  98. g_bDeferredWaiterTermination = TRUE;
  99. }
  100. }
  101. LWSTDAPI_(HANDLE)
  102. SHRegisterWaitForSingleObject(
  103. IN HANDLE hObject,
  104. IN WAITORTIMERCALLBACKFUNC pfnCallback,
  105. IN LPVOID pContext,
  106. IN DWORD dwWaitTime,
  107. IN LPCSTR lpszLibrary OPTIONAL,
  108. IN DWORD dwFlags
  109. )
  110. /*++
  111. Routine Description:
  112. This routine adds a new wait request to the pool of objects being waited on.
  113. Arguments:
  114. hObject - handle to the object to be waited on
  115. pfnCallback - routine called when the wait completes or a timeout occurs
  116. pContext - opaque pointer passed in as an argument to pfnCallback
  117. dwWaitTime - Timeout for the wait in milliseconds. 0 means dont timeout.
  118. lpszLibrary - if specified, name of library (DLL) to reference
  119. dwFlags - flags modifying request:
  120. SRWSO_NOREMOVE
  121. - once the handle becomes signalled, do not remove it
  122. from the handle array. Intended to be used with
  123. auto-reset events which become unsignalled again as
  124. soon as the waiting thread is made runnable
  125. Return Value:
  126. HANDLE
  127. Success - Non-NULL handle of created wait object
  128. Failure - NULL. Call GetLastError() for error code
  129. --*/
  130. {
  131. InterlockedIncrement((LPLONG)&g_ActiveRequests);
  132. HANDLE hWait = NULL;
  133. DWORD error = ERROR_SUCCESS;
  134. if (g_bTpsTerminating) {
  135. error = ERROR_SHUTDOWN_IN_PROGRESS; // error code? looks valid - justmann
  136. goto exit;
  137. }
  138. if (dwFlags & SRWSO_INVALID_FLAGS) {
  139. error = ERROR_INVALID_PARAMETER;
  140. goto exit;
  141. }
  142. //DWORD dwHandleFlags;
  143. //
  144. //if (!GetHandleInformation(hObject, &dwHandleFlags)) {
  145. //
  146. // //
  147. // // error == ERROR_SUCCESS returns GetHandleInformation() last error
  148. // //
  149. //
  150. // ASSERT(error == ERROR_SUCCESS);
  151. //
  152. // goto exit;
  153. //}
  154. //
  155. // GetHandleInformation() doesn't work on Win95
  156. //
  157. if (WaitForSingleObject(hObject, 0) == WAIT_FAILED) {
  158. //
  159. // error == ERROR_SUCCESS returns WaitForSingleObject() last error
  160. //
  161. ASSERT(error == ERROR_SUCCESS);
  162. goto exit;
  163. }
  164. //
  165. // initialize wait thread pool if it isn't already done
  166. //
  167. if (!g_CompletedWaitInitialization) {
  168. error = InitializeWaitThreadPool();
  169. if (error != ERROR_SUCCESS) {
  170. goto exit;
  171. }
  172. }
  173. //
  174. // find or create a wait thread that can accomodate another wait request
  175. //
  176. CWaitThreadInfo * pInfo;
  177. error = FindWaitThreadInfo(&pInfo);
  178. if (error == ERROR_SUCCESS) {
  179. CWaitAddRequest request(hObject,
  180. pfnCallback,
  181. pContext,
  182. dwWaitTime,
  183. dwFlags,
  184. pInfo
  185. );
  186. //
  187. // queue an APC to the wait thread
  188. //
  189. BOOL bSuccess = QueueUserAPC((PAPCFUNC)AddWait,
  190. pInfo->GetHandle(),
  191. (ULONG_PTR)&request
  192. );
  193. ASSERT(bSuccess);
  194. if (bSuccess) {
  195. //
  196. // relinquish the timeslice until the other thread has initialized
  197. //
  198. request.WaitForCompletion();
  199. //
  200. // the returned handle is the address of the wait object copied to
  201. // the wait thread's stack
  202. //
  203. hWait = request.GetWaitPointer();
  204. }
  205. pInfo->Release();
  206. }
  207. exit:
  208. if (error != ERROR_SUCCESS) {
  209. SetLastError(error);
  210. }
  211. ASSERT( 0 != g_ActiveRequests );
  212. InterlockedDecrement((LPLONG)&g_ActiveRequests);
  213. return hWait;
  214. }
  215. LWSTDAPI_(BOOL)
  216. SHUnregisterWait(
  217. IN HANDLE hWait
  218. )
  219. /*++
  220. Routine Description:
  221. This routine removes the specified wait from the pool of objects being waited
  222. on. This routine will block until all callbacks invoked as a result of this
  223. wait have been executed. This function MUST NOT be invoked inside the
  224. callback routines.
  225. Arguments:
  226. hWait - 'handle' indentifying the wait request
  227. Return Value:
  228. BOOL
  229. Success - TRUE
  230. Failure - FALSE. Call GetLastError() for error code
  231. --*/
  232. {
  233. InterlockedIncrement((LPLONG)&g_ActiveRequests);
  234. BOOL bSuccess = FALSE;
  235. DWORD error = ERROR_SUCCESS;
  236. if (hWait) {
  237. if (!g_bTpsTerminating) {
  238. CWaitThreadInfo * pInfo = ((CWait *)hWait)->GetThreadInfo();
  239. CWaitRemoveRequest request(hWait);
  240. //
  241. // lock the thread control block
  242. //
  243. pInfo->Acquire();
  244. //
  245. // queue an APC to the wait thread
  246. //
  247. if (QueueUserAPC((PAPCFUNC)RemoveWait,
  248. pInfo->GetHandle(),
  249. (ULONG_PTR)&request
  250. )) {
  251. //
  252. // relinquish the timeslice until the other thread has initialized
  253. //
  254. request.WaitForCompletion();
  255. if (!(bSuccess = (request.GetWaitPointer() != NULL))) {
  256. error = ERROR_OBJECT_NOT_FOUND; // error code? looks valid -justmann
  257. }
  258. }
  259. //
  260. // release lock to the thread control block
  261. //
  262. pInfo->Release();
  263. } else {
  264. error = ERROR_SHUTDOWN_IN_PROGRESS; // error code? looks valid -justmann
  265. }
  266. } else {
  267. error = ERROR_INVALID_PARAMETER;
  268. }
  269. if (error != ERROR_SUCCESS) {
  270. SetLastError(error);
  271. }
  272. ASSERT( 0 != g_ActiveRequests );
  273. InterlockedDecrement((LPLONG)&g_ActiveRequests);
  274. return bSuccess;
  275. }
  276. //
  277. // private functions
  278. //
  279. PRIVATE
  280. DWORD
  281. InitializeWaitThreadPool(
  282. VOID
  283. )
  284. /*++
  285. Routine Description:
  286. This routine initializes all aspects of the thread pool.
  287. Arguments:
  288. None
  289. Return Value:
  290. DWORD
  291. Success - ERROR_SUCCESS
  292. Failure -
  293. --*/
  294. {
  295. DWORD error = ERROR_SUCCESS;
  296. if (!InterlockedExchange((LPLONG)&g_StartedWaitInitialization, TRUE)) {
  297. g_WaitCriticalSection.Init();
  298. g_WaitThreads.Init();
  299. g_CompletedWaitInitialization = TRUE;
  300. } else {
  301. //
  302. // relinquish the timeslice until the other thread has initialized
  303. //
  304. while (!g_CompletedWaitInitialization) {
  305. SleepEx(0, FALSE); // Sleep(0) without an additional call/return
  306. }
  307. }
  308. return error;
  309. }
  310. PRIVATE
  311. DWORD
  312. FindWaitThreadInfo(
  313. OUT CWaitThreadInfo * * ppInfo
  314. )
  315. /*++
  316. Routine Description:
  317. Walks thru the list of wait threads and finds one which can accomodate
  318. another wait. If one is not found then a new thread is created.
  319. This routine returns with the thread's WaitThreadCriticalSecton owned if it
  320. is successful.
  321. Arguments:
  322. ppInfo - pointer to pointer to returned control block
  323. Return Value:
  324. DWORD
  325. Success - ERROR_SUCCESS
  326. Failure -
  327. --*/
  328. {
  329. HANDLE hThread = NULL;
  330. //
  331. // take exclusive lock to the wait threads list
  332. //
  333. g_WaitCriticalSection.Acquire();
  334. do {
  335. DWORD error;
  336. //
  337. // walk thru the list of Wait Threads and find a Wait thread that can
  338. // accomodate a new wait request
  339. //
  340. //
  341. // *Consider* finding a wait thread with least # of waits to facilitate
  342. // better load balancing of waits
  343. //
  344. for (CWaitThreadInfo * pInfo = (CWaitThreadInfo *)g_WaitThreads.Next();
  345. !g_WaitThreads.IsHead(pInfo);
  346. pInfo = (CWaitThreadInfo *)pInfo->Next()) {
  347. //
  348. // slight cheese: if hThread is not NULL then its because we just
  349. // created a new thread. We know we have g_WaitCriticalSection held
  350. // and no other thread can be accessing the new thread's control
  351. // block, so we can now write in the handle to be used in future
  352. // calls to QueueUserAPC. This saves us having to duplicate the
  353. // thread handle in the new thread
  354. //
  355. if (hThread != NULL) {
  356. pInfo->SetHandle(hThread);
  357. }
  358. //
  359. // take exclusive lock to the wait thread control block
  360. //
  361. pInfo->Acquire();
  362. //
  363. // wait threads can accomodate up to MAX_WAITS (WaitForMultipleObject
  364. // limit)
  365. //
  366. if (pInfo->IsAvailableEntry()) {
  367. //
  368. // found a thread with some wait slots available. Release lock
  369. // on the wait threads list
  370. //
  371. *ppInfo = pInfo;
  372. g_WaitCriticalSection.Release();
  373. return ERROR_SUCCESS;
  374. }
  375. //
  376. // release lock to thread control block
  377. //
  378. pInfo->Release();
  379. }
  380. //
  381. // if we reach here, we don't have any more wait threads so create more
  382. //
  383. error = StartThread((LPTHREAD_START_ROUTINE)WaitThread, &hThread, TRUE);
  384. //
  385. // if thread creation fails then return the failure to caller
  386. //
  387. if (error != ERROR_SUCCESS) {
  388. ASSERT(FALSE);
  389. g_WaitCriticalSection.Release();
  390. return error;
  391. }
  392. //
  393. // loop back now that we have created another thread and put new wait
  394. // request in new thread
  395. //
  396. } while(TRUE);
  397. }
  398. PRIVATE
  399. VOID
  400. AddWait(
  401. IN OUT CWaitAddRequest * pRequest
  402. )
  403. /*++
  404. Routine Description:
  405. This routine is used for adding waits to the wait thread. It is executed in
  406. an APC.
  407. Arguments:
  408. pRequest - pointer to request object
  409. Return Value:
  410. None.
  411. --*/
  412. {
  413. if (!g_bTpsTerminating) {
  414. CWaitThreadInfo * pInfo = pRequest->GetThreadInfo();
  415. CWait * pWait = pInfo->GetFreeWaiter();
  416. //
  417. // copy relevant fields from request object. C++ knows how to pull CWait
  418. // object out of CWaitAddRequest object. Insert the wait request object in
  419. // the list of active waits, in increasing expiration time order
  420. //
  421. *pWait = *pRequest;
  422. pInfo->InsertWaiter(pWait);
  423. //
  424. // return to the caller the address of the wait object on the wait thread's
  425. // stack and indicate to the calling thread that this request is complete
  426. //
  427. pRequest->SetWaitPointer(pWait);
  428. }
  429. pRequest->SetComplete();
  430. }
  431. PRIVATE
  432. VOID
  433. RemoveWait(
  434. IN CWaitRemoveRequest * pRequest
  435. )
  436. /*++
  437. Routine Description:
  438. This routine is used for deleting the specified wait. It is executed in an
  439. APC.
  440. Arguments:
  441. pRequest - pointer to request object
  442. Return Value:
  443. None.
  444. --*/
  445. {
  446. if (!g_bTpsTerminating) {
  447. if (!pRequest->GetWaitPointer()->GetThreadInfo()->RemoveWaiter(pRequest->GetWaitPointer())) {
  448. pRequest->SetWaitPointer(NULL);
  449. }
  450. }
  451. pRequest->SetComplete();
  452. }
  453. PRIVATE
  454. VOID
  455. WaitThread(
  456. IN HANDLE hEvent
  457. )
  458. /*++
  459. Routine Description:
  460. This routine is used for all waits in the wait thread pool
  461. Arguments:
  462. hEvent - event handle to signal once initialization is complete
  463. Return Value:
  464. None.
  465. --*/
  466. {
  467. HMODULE hDll = LoadLibrary(g_cszShlwapi);
  468. ASSERT(hDll != NULL);
  469. ASSERT(g_TpsTls != 0xFFFFFFFF);
  470. TlsSetValue(g_TpsTls, (LPVOID)TPS_WAITER_SIGNATURE);
  471. CWaitThreadInfo waitInfo(&g_WaitThreads);
  472. SetEvent(hEvent);
  473. while (!g_bTpsTerminating || (g_ActiveRequests != 0)) {
  474. DWORD dwIndex = waitInfo.Wait(waitInfo.GetWaitTime());
  475. if (g_bTpsTerminating && (g_ActiveRequests == 0)) {
  476. break;
  477. }
  478. if (dwIndex == WAIT_TIMEOUT) {
  479. waitInfo.ProcessTimeouts();
  480. #pragma warning(push)
  481. #pragma warning(disable:4296)
  482. } else if ((dwIndex >= WAIT_OBJECT_0)
  483. #pragma warning(pop)
  484. && (dwIndex < (WAIT_OBJECT_0 + waitInfo.GetObjectCount()))) {
  485. waitInfo.ProcessCompletion(dwIndex);
  486. } else if ((dwIndex == 0xFFFFFFFF) && GetLastError() == ERROR_INVALID_HANDLE) {
  487. waitInfo.PurgeInvalidHandles();
  488. } else {
  489. ASSERT(dwIndex == WAIT_IO_COMPLETION);
  490. }
  491. }
  492. while (waitInfo.GetHandle() != NULL) {
  493. SleepEx(0, FALSE);
  494. }
  495. if (GetCurrentThreadId() == g_dwTerminationThreadId) {
  496. g_bTpsTerminating = FALSE;
  497. g_bDeferredWaiterTermination = FALSE;
  498. g_dwTerminationThreadId = 0;
  499. }
  500. FreeLibraryAndExitThread(hDll, ERROR_SUCCESS);
  501. }