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.

560 lines
13 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. tpswait.h
  5. Abstract:
  6. Wait classes. Moved out of tpsclass.h
  7. Contents:
  8. CWait
  9. CWaitRequest
  10. CWaitAddRequest
  11. CWaitRemoveRequest
  12. CWaitThreadInfo
  13. Author:
  14. Richard L Firth (rfirth) 08-Aug-1998
  15. Revision History:
  16. 08-Aug-1998 rfirth
  17. Created
  18. --*/
  19. //
  20. // forward declarations
  21. //
  22. class CWaitThreadInfo;
  23. //
  24. // classes
  25. //
  26. //
  27. // CWait
  28. //
  29. class CWait : public CTimedListEntry {
  30. private:
  31. HANDLE m_hObject;
  32. WAITORTIMERCALLBACKFUNC m_pCallback;
  33. LPVOID m_pContext;
  34. CWaitThreadInfo * m_pThreadInfo;
  35. DWORD m_dwFlags;
  36. public:
  37. CWait(HANDLE hObject,
  38. WAITORTIMERCALLBACKFUNC pCallback,
  39. LPVOID pContext,
  40. DWORD dwWaitTime,
  41. DWORD dwFlags,
  42. CWaitThreadInfo * pInfo
  43. ) : CTimedListEntry(dwWaitTime) {
  44. m_hObject = hObject;
  45. m_pCallback = pCallback;
  46. m_pContext = pContext;
  47. m_pThreadInfo = pInfo;
  48. m_dwFlags = dwFlags;
  49. }
  50. CWait() {
  51. }
  52. CWait * Next(VOID) {
  53. return (CWait *)CTimedListEntry::Next();
  54. }
  55. CWaitThreadInfo * GetThreadInfo(VOID) const {
  56. return m_pThreadInfo;
  57. }
  58. VOID Execute(BOOL bTimeout) {
  59. //
  60. // execute function in this thread if required to do so, else we run
  61. // the callback in a non-I/O worker thread
  62. //
  63. //
  64. // APPCOMPAT - can't do this: the callback types for Wait & Work requests
  65. // are different: one takes 2 parameters, the other one. We
  66. // can't make this change until this issue is resolved with
  67. // NT guys
  68. //
  69. //if (m_dwFlags & WT_EXECUTEINWAITTHREAD) {
  70. m_pCallback(m_pContext, bTimeout != 0);
  71. //} else {
  72. //
  73. // //
  74. // // would have to allocate object from heap to hold callback
  75. // // function, context & bTimeout parameters in order to pass
  76. // // them to worker thread (we only have access to one APC
  77. // // parameter and we'd have to nominate different APC)
  78. // //
  79. //
  80. // Ie_QueueUserWorkItem((LPTHREAD_START_ROUTINE)m_pCallback,
  81. // m_pContext,
  82. // FALSE
  83. // );
  84. //}
  85. }
  86. HANDLE GetHandle(VOID) const {
  87. return m_hObject;
  88. }
  89. BOOL IsNoRemoveItem(VOID) {
  90. return (m_dwFlags & SRWSO_NOREMOVE) ? TRUE : FALSE;
  91. }
  92. };
  93. //
  94. // CWaitRequest
  95. //
  96. class CWaitRequest {
  97. private:
  98. BOOL m_bCompleted;
  99. CWait * m_pWait;
  100. public:
  101. CWaitRequest() {
  102. m_bCompleted = FALSE;
  103. }
  104. CWaitRequest(CWait * pWait) {
  105. m_bCompleted = FALSE;
  106. m_pWait = pWait;
  107. }
  108. VOID SetComplete(VOID) {
  109. m_bCompleted = TRUE;
  110. }
  111. VOID WaitForCompletion(VOID) {
  112. while (!m_bCompleted) {
  113. SleepEx(0, TRUE);
  114. }
  115. }
  116. VOID SetWaitPointer(CWait * pWait) {
  117. m_pWait = pWait;
  118. }
  119. CWait * GetWaitPointer(VOID) const {
  120. return m_pWait;
  121. }
  122. };
  123. //
  124. // CWaitAddRequest
  125. //
  126. class CWaitAddRequest : public CWait, public CWaitRequest {
  127. public:
  128. CWaitAddRequest(HANDLE hObject,
  129. WAITORTIMERCALLBACKFUNC pCallback,
  130. LPVOID pContext,
  131. DWORD dwWaitTime,
  132. DWORD dwFlags,
  133. CWaitThreadInfo * pInfo
  134. ) :
  135. CWait(hObject, pCallback, pContext, dwWaitTime, dwFlags, pInfo),
  136. CWaitRequest()
  137. {
  138. }
  139. };
  140. //
  141. // CWaitRemoveRequest
  142. //
  143. class CWaitRemoveRequest : public CWaitRequest {
  144. public:
  145. CWaitRemoveRequest(HANDLE hWait) : CWaitRequest((CWait *)hWait) {
  146. }
  147. };
  148. //
  149. // CWaitThreadInfo
  150. //
  151. class CWaitThreadInfo : public CDoubleLinkedList, public CCriticalSection {
  152. private:
  153. HANDLE m_hThread;
  154. DWORD m_dwObjectCount;
  155. HANDLE m_Objects[MAXIMUM_WAIT_OBJECTS];
  156. CWait * m_pWaiters[MAXIMUM_WAIT_OBJECTS];
  157. CWait m_Waiters[MAXIMUM_WAIT_OBJECTS];
  158. CDoubleLinkedList m_FreeList;
  159. CDoubleLinkedList m_WaitList;
  160. public:
  161. CWaitThreadInfo(CDoubleLinkedList * pList) {
  162. CDoubleLinkedList::Init();
  163. m_hThread = NULL;
  164. m_dwObjectCount = 0;
  165. m_FreeList.Init();
  166. m_WaitList.Init();
  167. for (int i = 0; i < ARRAY_ELEMENTS(m_Waiters); ++i) {
  168. m_Waiters[i].InsertTail(&m_FreeList);
  169. }
  170. InsertHead(pList);
  171. }
  172. VOID SetHandle(HANDLE hThread) {
  173. m_hThread = hThread;
  174. }
  175. HANDLE GetHandle(VOID) const {
  176. return m_hThread;
  177. }
  178. DWORD GetObjectCount(VOID) const {
  179. return m_dwObjectCount;
  180. }
  181. BOOL IsAvailableEntry(VOID) const {
  182. return m_dwObjectCount < ARRAY_ELEMENTS(m_Objects);
  183. }
  184. BOOL IsInvalidHandle(DWORD dwIndex) {
  185. ASSERT(dwIndex < m_dwObjectCount);
  186. //
  187. // GetHandleInformation() doesn't exist on Win95
  188. //
  189. //
  190. //DWORD dwHandleFlags;
  191. //
  192. //return !GetHandleInformation(m_Objects[dwIndex], &dwHandleFlags);
  193. DWORD status = WaitForSingleObject(m_Objects[dwIndex], 0);
  194. if ((status == WAIT_FAILED) && (GetLastError() == ERROR_INVALID_HANDLE)) {
  195. //#if DBG
  196. //char buf[128];
  197. //wsprintf(buf, "IsInvalidHandle(%d): handle %#x is invalid\n", dwIndex, m_Objects[dwIndex]);
  198. //OutputDebugString(buf);
  199. //#endif
  200. return TRUE;
  201. }
  202. return FALSE;
  203. }
  204. VOID Compress(DWORD dwIndex, DWORD dwCount = 1) {
  205. ASSERT(dwCount != 0);
  206. ASSERT((int)m_dwObjectCount > 0);
  207. if ((dwIndex + dwCount) < m_dwObjectCount) {
  208. RtlMoveMemory(&m_Objects[dwIndex],
  209. &m_Objects[dwIndex + dwCount],
  210. sizeof(m_Objects[0]) * (m_dwObjectCount - (dwIndex + dwCount))
  211. );
  212. RtlMoveMemory(&m_pWaiters[dwIndex],
  213. &m_pWaiters[dwIndex + dwCount],
  214. sizeof(m_pWaiters[0]) * (m_dwObjectCount - (dwIndex + dwCount))
  215. );
  216. }
  217. m_dwObjectCount -= dwCount;
  218. }
  219. VOID Expand(DWORD dwIndex) {
  220. RtlMoveMemory(&m_Objects[dwIndex],
  221. &m_Objects[dwIndex + 1],
  222. sizeof(m_Objects[0]) * (m_dwObjectCount - dwIndex)
  223. );
  224. RtlMoveMemory(&m_pWaiters[dwIndex],
  225. &m_pWaiters[dwIndex + 1],
  226. sizeof(m_pWaiters[0]) * (m_dwObjectCount - dwIndex)
  227. );
  228. ++m_dwObjectCount;
  229. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  230. }
  231. //DWORD BuildList(VOID) {
  232. //
  233. // //
  234. // // PERF: only rebuild from changed index
  235. // //
  236. //
  237. // m_dwObjectCount = 0;
  238. // for (CWait * pWait = (CWait *)m_WaitList.Next();
  239. // pWait = pWait->Next();
  240. // !m_WaitList.IsHead(pWait)) {
  241. // m_pWaiters[m_dwObjectCount] = pWait;
  242. // m_Objects[m_dwObjectCount] = pWait->GetHandle();
  243. // ++m_dwObjectCount;
  244. // }
  245. // return GetWaitTime();
  246. //}
  247. DWORD Wait(DWORD dwTimeout = INFINITE) {
  248. //
  249. // if no objects in list, sleep alertably for the timeout period
  250. //
  251. if (m_dwObjectCount == 0) {
  252. SleepEx(dwTimeout, TRUE);
  253. return WAIT_IO_COMPLETION;
  254. }
  255. //
  256. // else wait alertably for the timeout period
  257. //
  258. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  259. return WaitForMultipleObjectsEx(m_dwObjectCount,
  260. m_Objects,
  261. FALSE, // fWaitAll
  262. dwTimeout,
  263. TRUE // fAlertable
  264. );
  265. }
  266. DWORD GetWaitTime(VOID) {
  267. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  268. if (m_dwObjectCount != 0) {
  269. CWait * pWaiter = m_pWaiters[0];
  270. DWORD dwWaitTime = pWaiter->GetWaitTime();
  271. if (dwWaitTime != INFINITE) {
  272. DWORD dwTimeNow = GetTickCount();
  273. DWORD dwTimeStamp = pWaiter->GetTimeStamp();
  274. if (dwTimeNow > dwTimeStamp + dwWaitTime) {
  275. //
  276. // first object expired already
  277. //
  278. return 0;
  279. }
  280. //
  281. // number of milliseconds until next waiter expires
  282. //
  283. return (dwTimeStamp + dwWaitTime) - dwTimeNow;
  284. }
  285. }
  286. //
  287. // nothing in list
  288. //
  289. return INFINITE;
  290. }
  291. CWait * GetFreeWaiter(VOID) {
  292. return (CWait *)m_FreeList.RemoveHead();
  293. }
  294. VOID InsertWaiter(CWait * pWait) {
  295. DWORD dwIndex = 0;
  296. BOOL bAtEnd = TRUE;
  297. CDoubleLinkedListEntry * pHead = m_WaitList.Head();
  298. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  299. if ((m_dwObjectCount != 0) && !pWait->IsInfiniteTimeout()) {
  300. //
  301. // not infinite timeout. Find place in list to insert this object
  302. //
  303. //
  304. // PERF: typically, new wait will be longer than most currently in
  305. // list, so should start from end of non-infinite timeouts
  306. // and work backwards
  307. //
  308. for (; dwIndex < m_dwObjectCount; ++dwIndex) {
  309. if (pWait->ExpiryTime() < m_pWaiters[dwIndex]->ExpiryTime()) {
  310. pHead = m_pWaiters[dwIndex]->Head();
  311. bAtEnd = (dwIndex == (m_dwObjectCount - 1));
  312. break;
  313. }
  314. }
  315. }
  316. //
  317. // insert the new wait object at the correct location
  318. //
  319. pWait->InsertTail(pHead);
  320. if (!bAtEnd && (m_dwObjectCount != 0)) {
  321. Expand(dwIndex);
  322. } else {
  323. dwIndex = m_dwObjectCount;
  324. ++m_dwObjectCount;
  325. }
  326. //
  327. // update object list and pointer list
  328. //
  329. m_Objects[dwIndex] = pWait->GetHandle();
  330. m_pWaiters[dwIndex] = pWait;
  331. }
  332. VOID RemoveWaiter(CWait * pWait, DWORD dwIndex) {
  333. //
  334. // remove the waiter from the wait list and add it back to the
  335. // free list
  336. //
  337. pWait->Remove();
  338. pWait->InsertTail(&m_FreeList);
  339. //
  340. // if the object was not at the end of the list then compress
  341. // the list
  342. //
  343. if (dwIndex != (m_dwObjectCount - 1)) {
  344. Compress(dwIndex, 1);
  345. } else {
  346. --m_dwObjectCount;
  347. }
  348. }
  349. VOID RemoveWaiter(DWORD dwIndex) {
  350. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  351. RemoveWaiter(m_pWaiters[dwIndex], dwIndex);
  352. }
  353. BOOL RemoveWaiter(CWait * pWait) {
  354. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  355. for (DWORD dwIndex = 0; dwIndex < m_dwObjectCount; ++dwIndex) {
  356. if (m_pWaiters[dwIndex] == pWait) {
  357. RemoveWaiter(pWait, dwIndex);
  358. return TRUE;
  359. }
  360. }
  361. return FALSE;
  362. }
  363. VOID ProcessTimeouts(VOID) {
  364. DWORD dwTimeNow = GetTickCount();
  365. DWORD dwCount = 0;
  366. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  367. while (dwCount < m_dwObjectCount) {
  368. CWait * pWait = m_pWaiters[dwCount];
  369. //
  370. // if waiter has expired, invoke its callback then remove it from
  371. // the wait list and add back to the free list
  372. //
  373. if (pWait->IsTimedOut(dwTimeNow)) {
  374. pWait->Execute(TRUE);
  375. pWait->Remove();
  376. pWait->InsertTail(&m_FreeList);
  377. ++dwCount;
  378. } else {
  379. //
  380. // quit loop at first non-timed-out entry
  381. //
  382. break;
  383. }
  384. }
  385. ASSERT(dwCount != 0);
  386. if (dwCount != 0) {
  387. Compress(0, dwCount);
  388. }
  389. }
  390. VOID PurgeInvalidHandles(VOID) {
  391. DWORD dwCount = 0;
  392. DWORD dwIndex = 0;
  393. DWORD dwIndexStart = 0;
  394. ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
  395. while (dwIndex < m_dwObjectCount) {
  396. CWait * pWait = m_pWaiters[dwIndex];
  397. //
  398. // if handle has become invalid, invoke the callback then remove it
  399. // from the wait list and add back to the free list
  400. //
  401. if (IsInvalidHandle(dwIndex)) {
  402. pWait->Execute(FALSE);
  403. pWait->Remove();
  404. pWait->InsertTail(&m_FreeList);
  405. if (dwIndexStart == 0) {
  406. dwIndexStart = dwIndex;
  407. }
  408. ++dwCount;
  409. } else if (dwCount != 0) {
  410. Compress(dwIndexStart, dwCount);
  411. dwIndex = dwIndexStart - 1;
  412. dwIndexStart = 0;
  413. dwCount = 0;
  414. }
  415. ++dwIndex;
  416. }
  417. if (dwCount != 0) {
  418. Compress(dwIndexStart, dwCount);
  419. }
  420. }
  421. VOID ProcessCompletion(DWORD dwIndex) {
  422. CWait * pWait = m_pWaiters[dwIndex];
  423. pWait->Execute(FALSE);
  424. if (!pWait->IsNoRemoveItem()) {
  425. RemoveWaiter(dwIndex);
  426. }
  427. }
  428. };