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.

473 lines
8.0 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1994 - 1999
  6. //
  7. // File: delaytab.cxx
  8. //
  9. //--------------------------------------------------------------------------
  10. /*++
  11. Module Name:
  12. delaytab.cxx
  13. Abstract:
  14. interface for DELAYED_ACTION_TABLE, which asynchronously calls
  15. functions after a specified delay.
  16. Author:
  17. Jeff Roberts (jroberts) 2-Nov-1994
  18. Revision History:
  19. 2-Nov-1994 jroberts
  20. Created this module.
  21. --*/
  22. #include <precomp.hxx>
  23. #include "delaytab.hxx"
  24. #include "rpcuuid.hxx"
  25. #include "sdict.hxx"
  26. #include "binding.hxx"
  27. #include "handle.hxx"
  28. #include "rpcssp.h"
  29. #include "secclnt.hxx"
  30. #include "hndlsvr.hxx"
  31. inline unsigned long
  32. CurrentTimeInMsec(
  33. void
  34. )
  35. {
  36. return GetTickCount();
  37. }
  38. BOOL
  39. DelayedActionThread(
  40. LPVOID Parms
  41. )
  42. /*++
  43. Routine Description:
  44. This is a thread proc for the delayed call table.
  45. Arguments:
  46. Parms - address of table
  47. Return Value:
  48. FALSE - thread should be returned to the the cache.
  49. --*/
  50. {
  51. DELAYED_ACTION_TABLE * pTable = (DELAYED_ACTION_TABLE *) Parms;
  52. pTable->ThreadProc();
  53. return FALSE;
  54. }
  55. void
  56. DELAYED_ACTION_TABLE::ThreadProc()
  57. {
  58. /*++
  59. Routine Description:
  60. This thread takes requests off the delayed-action list and processes them.
  61. After 15 seconds of inactivity, this thread terminates.
  62. Arguments:
  63. none
  64. Return Value:
  65. none
  66. --*/
  67. BOOL EmptyList = FALSE;
  68. long CurrentTime;
  69. long WaitTime;
  70. DELAYED_ACTION_NODE Copy;
  71. DELAYED_ACTION_NODE * pNode;
  72. //
  73. // Disabling the event priority boost has the benefit of allowing a thread
  74. // who posts a request to continue processing afterwards. This should
  75. // improve locality of reference, since this thread will not preempt
  76. // the poster.
  77. //
  78. if (FALSE == SetThreadPriorityBoost(GetCurrentThread(), TRUE))
  79. {
  80. #ifdef DEBUGRPC
  81. DbgPrint("RPC DG: SetThreadPriorityBoost failed with %lu\n", GetLastError());
  82. #endif
  83. }
  84. do
  85. {
  86. DELAYED_ACTION_FN pFn;
  87. void * pData;
  88. Mutex.Request();
  89. #ifdef DEBUGRPC
  90. {
  91. unsigned ObservedCount = 0;
  92. DELAYED_ACTION_NODE * Scan = ActiveList.Next;
  93. while (Scan != &ActiveList)
  94. {
  95. ++ObservedCount;
  96. Scan = Scan->Next;
  97. }
  98. if (ObservedCount != NodeCount)
  99. {
  100. PrintToDebugger("RPC DG: delay thread sees %lu nodes but there should be %lu\n", ObservedCount, NodeCount);
  101. RpcpBreakPoint();
  102. }
  103. }
  104. #endif
  105. pNode = ActiveList.Next;
  106. if (pNode != &ActiveList)
  107. {
  108. ASSERT(pNode->TriggerTime != DELAYED_ACTION_NODE::DA_NodeFree);
  109. EmptyList = FALSE;
  110. CurrentTime = CurrentTimeInMsec();
  111. if (pNode->TriggerTime - CurrentTime > 0)
  112. {
  113. WaitTime = pNode->TriggerTime - CurrentTime;
  114. }
  115. else
  116. {
  117. WaitTime = 0;
  118. pFn = pNode->Fn;
  119. pData = pNode->Data;
  120. Cancel(pNode);
  121. }
  122. }
  123. else
  124. {
  125. CurrentTime = CurrentTimeInMsec();
  126. if (!EmptyList)
  127. {
  128. WaitTime = 15000UL;
  129. EmptyList = TRUE;
  130. }
  131. else
  132. {
  133. ThreadActive = FALSE;
  134. Mutex.Clear();
  135. break;
  136. }
  137. }
  138. ThreadWakeupTime = CurrentTime + WaitTime;
  139. Mutex.Clear();
  140. if (WaitTime)
  141. {
  142. ThreadEvent.Wait(WaitTime);
  143. }
  144. else
  145. {
  146. (*pFn)(pData);
  147. }
  148. }
  149. while ( !fExitThread );
  150. }
  151. DELAYED_ACTION_TABLE::DELAYED_ACTION_TABLE(
  152. RPC_STATUS * pStatus
  153. )
  154. /*++
  155. Routine Description:
  156. constructor for the delayed-action table. Initially no thread is created.
  157. Arguments:
  158. pStatus - if an error occurs, this will be filled in
  159. Return Value:
  160. none
  161. --*/
  162. : Mutex(pStatus),
  163. ThreadEvent(pStatus, 0),
  164. ActiveList(0, 0),
  165. fExitThread(0),
  166. ThreadActive(FALSE)
  167. {
  168. fConstructorFinished = FALSE;
  169. if (*pStatus != RPC_S_OK)
  170. {
  171. return;
  172. }
  173. ActiveList.Next = &ActiveList;
  174. ActiveList.Prev = &ActiveList;
  175. #ifdef DEBUGRPC
  176. LastAdded = 0;
  177. LastRemoved = 0;
  178. NodeCount = 0;
  179. #endif
  180. fConstructorFinished = TRUE;
  181. }
  182. DELAYED_ACTION_TABLE::~DELAYED_ACTION_TABLE(
  183. )
  184. /*++
  185. Routine Description:
  186. Dsestructor for the delayed-action table. It tells the associated thread
  187. to terminate, and waits until that happens.
  188. Arguments:
  189. none
  190. Return Value:
  191. none
  192. --*/
  193. {
  194. if (FALSE == fConstructorFinished)
  195. {
  196. return;
  197. }
  198. DELAYED_ACTION_NODE * pNode;
  199. fExitThread = 1;
  200. ThreadEvent.Raise();
  201. while (ActiveList.Next != &ActiveList)
  202. {
  203. Sleep(500);
  204. }
  205. }
  206. RPC_STATUS
  207. DELAYED_ACTION_TABLE::Add(
  208. DELAYED_ACTION_NODE * pNode,
  209. unsigned Delay,
  210. BOOL ForceUpdate
  211. )
  212. /*++
  213. Routine Description:
  214. Adds a node to the table with the specified delay. The action taken if
  215. the node is already in the list depends upon <ForceUpdate>: if FALSE,
  216. the old trigger time is kept; if TRUE, the new time is used and the node
  217. is moved in the list appropriately.
  218. Arguments:
  219. pNode - the node
  220. Delay - delay time in milliseconds
  221. ForceUpdate - only used when pNode is already in the list (see text above)
  222. Return Value:
  223. RPC_S_OK, or an error
  224. --*/
  225. {
  226. if (!ForceUpdate && pNode->IsActive())
  227. {
  228. return RPC_S_OK;
  229. }
  230. CLAIM_MUTEX Lock(Mutex);
  231. if (pNode->IsActive())
  232. {
  233. Cancel(pNode);
  234. }
  235. //
  236. // Add the node to the active list.
  237. //
  238. DELAYED_ACTION_NODE * pScan;
  239. pScan = ActiveList.Next;
  240. pNode->TriggerTime = Delay + CurrentTimeInMsec();
  241. long TriggerTime = pNode->TriggerTime;
  242. while (pScan != &ActiveList && pScan->TriggerTime - TriggerTime < 0)
  243. {
  244. ASSERT(pScan->IsActive());
  245. ASSERT(pScan != pNode);
  246. pScan = pScan->Next;
  247. }
  248. pNode->Next = pScan;
  249. pNode->Prev = pScan->Prev;
  250. pNode->Next->Prev = pNode;
  251. pNode->Prev->Next = pNode;
  252. #ifdef DEBUGRPC
  253. ++NodeCount;
  254. LastAdded = pNode;
  255. #endif
  256. if (!ThreadActive)
  257. {
  258. fExitThread = FALSE;
  259. RPC_STATUS Status = GlobalRpcServer->CreateThread(DelayedActionThread, this);
  260. if (Status)
  261. {
  262. return Status;
  263. }
  264. ThreadActive = TRUE;
  265. }
  266. else if (ActiveList.Next == pNode && TriggerTime - ThreadWakeupTime < 0)
  267. {
  268. ThreadEvent.Raise();
  269. }
  270. return RPC_S_OK;
  271. }
  272. BOOL
  273. DELAYED_ACTION_TABLE::SearchForNode(
  274. DELAYED_ACTION_NODE * pNode
  275. )
  276. /*++
  277. Routine Description:
  278. Finds the node in the table.
  279. Arguments:
  280. Return Value:
  281. TRUE if pNode is in the table
  282. FALSE if not
  283. --*/
  284. {
  285. CLAIM_MUTEX Lock(Mutex);
  286. //
  287. // Search for node in active list.
  288. //
  289. DELAYED_ACTION_NODE * pScan;
  290. pScan = ActiveList.Next;
  291. while (pScan != &ActiveList && pScan != pNode)
  292. {
  293. pScan = pScan->Next;
  294. }
  295. if (pScan)
  296. {
  297. return TRUE;
  298. }
  299. else
  300. {
  301. return FALSE;
  302. }
  303. }
  304. void
  305. DELAYED_ACTION_TABLE::QueueLength(
  306. unsigned * pTotalCount,
  307. unsigned * pOverdueCount
  308. )
  309. /*++
  310. Routine Description:
  311. Determines the number of active entries and the number of entries
  312. that are late.
  313. Arguments:
  314. Return Value:
  315. none
  316. --*/
  317. {
  318. CLAIM_MUTEX Lock(Mutex);
  319. DELAYED_ACTION_NODE * pScan = ActiveList.Next;
  320. unsigned Count = 0;
  321. long CurrentTime = GetTickCount();
  322. while (pScan != &ActiveList && CurrentTime - pScan->TriggerTime > 0)
  323. {
  324. ++Count;
  325. pScan = pScan->Next;
  326. }
  327. *pOverdueCount = Count;
  328. while (pScan != &ActiveList)
  329. {
  330. ++Count;
  331. pScan = pScan->Next;
  332. }
  333. *pTotalCount = Count;
  334. }