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.

704 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. if (IsBadCodePtr((FARPROC)pfnCallback)) {
  165. error = ERROR_INVALID_PARAMETER;
  166. goto exit;
  167. }
  168. //
  169. // initialize wait thread pool if it isn't already done
  170. //
  171. if (!g_CompletedWaitInitialization) {
  172. error = InitializeWaitThreadPool();
  173. if (error != ERROR_SUCCESS) {
  174. goto exit;
  175. }
  176. }
  177. //
  178. // find or create a wait thread that can accomodate another wait request
  179. //
  180. CWaitThreadInfo * pInfo;
  181. error = FindWaitThreadInfo(&pInfo);
  182. if (error == ERROR_SUCCESS) {
  183. CWaitAddRequest request(hObject,
  184. pfnCallback,
  185. pContext,
  186. dwWaitTime,
  187. dwFlags,
  188. pInfo
  189. );
  190. //
  191. // queue an APC to the wait thread
  192. //
  193. BOOL bSuccess = QueueUserAPC((PAPCFUNC)AddWait,
  194. pInfo->GetHandle(),
  195. (ULONG_PTR)&request
  196. );
  197. ASSERT(bSuccess);
  198. if (bSuccess) {
  199. //
  200. // relinquish the timeslice until the other thread has initialized
  201. //
  202. request.WaitForCompletion();
  203. //
  204. // the returned handle is the address of the wait object copied to
  205. // the wait thread's stack
  206. //
  207. hWait = request.GetWaitPointer();
  208. }
  209. pInfo->Release();
  210. }
  211. exit:
  212. if (error != ERROR_SUCCESS) {
  213. SetLastError(error);
  214. }
  215. InterlockedDecrement((LPLONG)&g_ActiveRequests);
  216. return hWait;
  217. }
  218. LWSTDAPI_(BOOL)
  219. SHUnregisterWait(
  220. IN HANDLE hWait
  221. )
  222. /*++
  223. Routine Description:
  224. This routine removes the specified wait from the pool of objects being waited
  225. on. This routine will block until all callbacks invoked as a result of this
  226. wait have been executed. This function MUST NOT be invoked inside the
  227. callback routines.
  228. Arguments:
  229. hWait - 'handle' indentifying the wait request
  230. Return Value:
  231. BOOL
  232. Success - TRUE
  233. Failure - FALSE. Call GetLastError() for error code
  234. --*/
  235. {
  236. InterlockedIncrement((LPLONG)&g_ActiveRequests);
  237. BOOL bSuccess = FALSE;
  238. DWORD error = ERROR_SUCCESS;
  239. if (hWait) {
  240. if (!g_bTpsTerminating) {
  241. CWaitThreadInfo * pInfo = ((CWait *)hWait)->GetThreadInfo();
  242. CWaitRemoveRequest request(hWait);
  243. //
  244. // lock the thread control block
  245. //
  246. pInfo->Acquire();
  247. //
  248. // queue an APC to the wait thread
  249. //
  250. if (QueueUserAPC((PAPCFUNC)RemoveWait,
  251. pInfo->GetHandle(),
  252. (ULONG_PTR)&request
  253. )) {
  254. //
  255. // relinquish the timeslice until the other thread has initialized
  256. //
  257. request.WaitForCompletion();
  258. if (!(bSuccess = (request.GetWaitPointer() != NULL))) {
  259. error = ERROR_OBJECT_NOT_FOUND; // error code? looks valid -justmann
  260. }
  261. }
  262. //
  263. // release lock to the thread control block
  264. //
  265. pInfo->Release();
  266. } else {
  267. error = ERROR_SHUTDOWN_IN_PROGRESS; // error code? looks valid -justmann
  268. }
  269. } else {
  270. error = ERROR_INVALID_PARAMETER;
  271. }
  272. if (error != ERROR_SUCCESS) {
  273. SetLastError(error);
  274. }
  275. InterlockedDecrement((LPLONG)&g_ActiveRequests);
  276. return bSuccess;
  277. }
  278. //
  279. // private functions
  280. //
  281. PRIVATE
  282. DWORD
  283. InitializeWaitThreadPool(
  284. VOID
  285. )
  286. /*++
  287. Routine Description:
  288. This routine initializes all aspects of the thread pool.
  289. Arguments:
  290. None
  291. Return Value:
  292. DWORD
  293. Success - ERROR_SUCCESS
  294. Failure -
  295. --*/
  296. {
  297. DWORD error = ERROR_SUCCESS;
  298. if (!InterlockedExchange((LPLONG)&g_StartedWaitInitialization, TRUE)) {
  299. g_WaitCriticalSection.Init();
  300. g_WaitThreads.Init();
  301. g_CompletedWaitInitialization = TRUE;
  302. } else {
  303. //
  304. // relinquish the timeslice until the other thread has initialized
  305. //
  306. while (!g_CompletedWaitInitialization) {
  307. SleepEx(0, FALSE); // Sleep(0) without an additional call/return
  308. }
  309. }
  310. return error;
  311. }
  312. PRIVATE
  313. DWORD
  314. FindWaitThreadInfo(
  315. OUT CWaitThreadInfo * * ppInfo
  316. )
  317. /*++
  318. Routine Description:
  319. Walks thru the list of wait threads and finds one which can accomodate
  320. another wait. If one is not found then a new thread is created.
  321. This routine returns with the thread's WaitThreadCriticalSecton owned if it
  322. is successful.
  323. Arguments:
  324. ppInfo - pointer to pointer to returned control block
  325. Return Value:
  326. DWORD
  327. Success - ERROR_SUCCESS
  328. Failure -
  329. --*/
  330. {
  331. HANDLE hThread = NULL;
  332. //
  333. // take exclusive lock to the wait threads list
  334. //
  335. g_WaitCriticalSection.Acquire();
  336. do {
  337. DWORD error;
  338. //
  339. // walk thru the list of Wait Threads and find a Wait thread that can
  340. // accomodate a new wait request
  341. //
  342. //
  343. // *Consider* finding a wait thread with least # of waits to facilitate
  344. // better load balancing of waits
  345. //
  346. for (CWaitThreadInfo * pInfo = (CWaitThreadInfo *)g_WaitThreads.Next();
  347. !g_WaitThreads.IsHead(pInfo);
  348. pInfo = (CWaitThreadInfo *)pInfo->Next()) {
  349. //
  350. // slight cheese: if hThread is not NULL then its because we just
  351. // created a new thread. We know we have g_WaitCriticalSection held
  352. // and no other thread can be accessing the new thread's control
  353. // block, so we can now write in the handle to be used in future
  354. // calls to QueueUserAPC. This saves us having to duplicate the
  355. // thread handle in the new thread
  356. //
  357. if (hThread != NULL) {
  358. pInfo->SetHandle(hThread);
  359. }
  360. //
  361. // take exclusive lock to the wait thread control block
  362. //
  363. pInfo->Acquire();
  364. //
  365. // wait threads can accomodate up to MAX_WAITS (WaitForMultipleObject
  366. // limit)
  367. //
  368. if (pInfo->IsAvailableEntry()) {
  369. //
  370. // found a thread with some wait slots available. Release lock
  371. // on the wait threads list
  372. //
  373. *ppInfo = pInfo;
  374. g_WaitCriticalSection.Release();
  375. return ERROR_SUCCESS;
  376. }
  377. //
  378. // release lock to thread control block
  379. //
  380. pInfo->Release();
  381. }
  382. //
  383. // if we reach here, we don't have any more wait threads so create more
  384. //
  385. error = StartThread((LPTHREAD_START_ROUTINE)WaitThread, &hThread, TRUE);
  386. //
  387. // if thread creation fails then return the failure to caller
  388. //
  389. if (error != ERROR_SUCCESS) {
  390. ASSERT(FALSE);
  391. g_WaitCriticalSection.Release();
  392. return error;
  393. }
  394. //
  395. // loop back now that we have created another thread and put new wait
  396. // request in new thread
  397. //
  398. } while(TRUE);
  399. }
  400. PRIVATE
  401. VOID
  402. AddWait(
  403. IN OUT CWaitAddRequest * pRequest
  404. )
  405. /*++
  406. Routine Description:
  407. This routine is used for adding waits to the wait thread. It is executed in
  408. an APC.
  409. Arguments:
  410. pRequest - pointer to request object
  411. Return Value:
  412. None.
  413. --*/
  414. {
  415. if (!g_bTpsTerminating) {
  416. CWaitThreadInfo * pInfo = pRequest->GetThreadInfo();
  417. CWait * pWait = pInfo->GetFreeWaiter();
  418. //
  419. // copy relevant fields from request object. C++ knows how to pull CWait
  420. // object out of CWaitAddRequest object. Insert the wait request object in
  421. // the list of active waits, in increasing expiration time order
  422. //
  423. *pWait = *pRequest;
  424. pInfo->InsertWaiter(pWait);
  425. //
  426. // return to the caller the address of the wait object on the wait thread's
  427. // stack and indicate to the calling thread that this request is complete
  428. //
  429. pRequest->SetWaitPointer(pWait);
  430. }
  431. pRequest->SetComplete();
  432. }
  433. PRIVATE
  434. VOID
  435. RemoveWait(
  436. IN CWaitRemoveRequest * pRequest
  437. )
  438. /*++
  439. Routine Description:
  440. This routine is used for deleting the specified wait. It is executed in an
  441. APC.
  442. Arguments:
  443. pRequest - pointer to request object
  444. Return Value:
  445. None.
  446. --*/
  447. {
  448. if (!g_bTpsTerminating) {
  449. if (!pRequest->GetWaitPointer()->GetThreadInfo()->RemoveWaiter(pRequest->GetWaitPointer())) {
  450. pRequest->SetWaitPointer(NULL);
  451. }
  452. }
  453. pRequest->SetComplete();
  454. }
  455. PRIVATE
  456. VOID
  457. WaitThread(
  458. IN HANDLE hEvent
  459. )
  460. /*++
  461. Routine Description:
  462. This routine is used for all waits in the wait thread pool
  463. Arguments:
  464. hEvent - event handle to signal once initialization is complete
  465. Return Value:
  466. None.
  467. --*/
  468. {
  469. HMODULE hDll = LoadLibrary(g_cszShlwapi);
  470. ASSERT(hDll != NULL);
  471. ASSERT(g_TpsTls != 0xFFFFFFFF);
  472. TlsSetValue(g_TpsTls, (LPVOID)TPS_WAITER_SIGNATURE);
  473. CWaitThreadInfo waitInfo(&g_WaitThreads);
  474. SetEvent(hEvent);
  475. while (!g_bTpsTerminating || (g_ActiveRequests != 0)) {
  476. DWORD dwIndex = waitInfo.Wait(waitInfo.GetWaitTime());
  477. if (g_bTpsTerminating && (g_ActiveRequests == 0)) {
  478. break;
  479. }
  480. if (dwIndex == WAIT_TIMEOUT) {
  481. waitInfo.ProcessTimeouts();
  482. #pragma warning(push)
  483. #pragma warning(disable:4296)
  484. } else if ((dwIndex >= WAIT_OBJECT_0)
  485. #pragma warning(pop)
  486. && (dwIndex < (WAIT_OBJECT_0 + waitInfo.GetObjectCount()))) {
  487. waitInfo.ProcessCompletion(dwIndex);
  488. } else if ((dwIndex == 0xFFFFFFFF) && GetLastError() == ERROR_INVALID_HANDLE) {
  489. waitInfo.PurgeInvalidHandles();
  490. } else {
  491. ASSERT(dwIndex == WAIT_IO_COMPLETION);
  492. }
  493. }
  494. while (waitInfo.GetHandle() != NULL) {
  495. SleepEx(0, FALSE);
  496. }
  497. if (GetCurrentThreadId() == g_dwTerminationThreadId) {
  498. g_bTpsTerminating = FALSE;
  499. g_bDeferredWaiterTermination = FALSE;
  500. g_dwTerminationThreadId = 0;
  501. }
  502. FreeLibraryAndExitThread(hDll, ERROR_SUCCESS);
  503. }