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.

640 lines
18 KiB

  1. /* timers.c -- Multiplexed timer routines -- run several timers off one
  2. * Windows timer.
  3. *
  4. * Copyright 1994 by Hilgraeve, Inc. -- Monroe, MI
  5. * All rights reserved
  6. *
  7. * $Revision: 6 $
  8. * $Date: 5/29/02 2:17p $
  9. */
  10. #include <windows.h>
  11. #pragma hdrstop
  12. #include "stdtyp.h"
  13. #include <tdll\assert.h>
  14. #include "mc.h"
  15. #include "session.h"
  16. #include "timers.h"
  17. typedef struct s_timer ST_TIMER;
  18. typedef struct s_timer_mux ST_TIMER_MUX;
  19. struct s_timer_mux
  20. {
  21. HSESSION hSession;
  22. HWND hWnd;
  23. UINT uiID;
  24. UINT_PTR uiTimer;
  25. UINT uiLastDuration;
  26. int fInMuxProc;
  27. ST_TIMER *pstFirst;
  28. ST_TIMER *pstCurrent;
  29. };
  30. struct s_timer
  31. {
  32. HSESSION hSession;
  33. ST_TIMER *pstNext;
  34. ST_TIMER_MUX *pstTimerMux;
  35. long lInterval;
  36. long lLastFired;
  37. long lFireTime;
  38. void *pvData;
  39. TIMERCALLBACK pfCallback;
  40. };
  41. void TimerInsert(ST_TIMER_MUX *pstTimerMux, ST_TIMER *pstTimer);
  42. int TimerSet(ST_TIMER_MUX *pstTimerMux);
  43. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  44. * FUNCTION: TimerMuxCreate
  45. *
  46. * DESCRIPTION:
  47. * Creates a Timer Multiplexer from which any number of individual timers
  48. * can be created. A Timer Multiplexer uses only one Windows Timer now
  49. * matter how many individual timers are created from it.
  50. *
  51. * ARGUMENTS:
  52. * pHTM -- A pointer to an HTIMERMUX handle. This handle must be used in
  53. * subsequent calls to CreateTimer
  54. *
  55. * RETURNS:
  56. * TIMER_OK if successful
  57. * TIMER_NOMEM if there is insufficient memory
  58. * TIMER_NOWINTIMER if there are no Windows timers available
  59. * TIMER_ERROR if invalid parameters are passed.
  60. */
  61. int TimerMuxCreate(const HWND hWnd, const UINT uiID, HTIMERMUX * const pHTM, const HSESSION hSession)
  62. {
  63. //
  64. // sessQueryTimerMux() locks the session's TimerMux
  65. // critical section. Call sessReleaseTimerMux() to unlock
  66. // the session's TimerMux critical section. REV: 5/21/2002
  67. //
  68. HTIMERMUX hTM = sessQueryTimerMux(hSession);
  69. int iReturnVal = TIMER_OK;
  70. ST_TIMER_MUX *pstTM;
  71. if (!hWnd || !pHTM)
  72. {
  73. assert(FALSE);
  74. return TIMER_ERROR;
  75. }
  76. if ((pstTM = malloc(sizeof(ST_TIMER_MUX))) == NULL)
  77. {
  78. iReturnVal = TIMER_NOMEM;
  79. }
  80. else
  81. {
  82. pstTM->hSession = hSession;
  83. pstTM->hWnd = hWnd;
  84. pstTM->uiID = uiID;
  85. pstTM->uiTimer = 0;
  86. pstTM->uiLastDuration = 0;
  87. pstTM->pstFirst = (ST_TIMER *)0;
  88. pstTM->pstCurrent = (ST_TIMER *)0;
  89. pstTM->fInMuxProc = FALSE;
  90. iReturnVal = TimerSet(pstTM);
  91. DbgOutStr("TimerMux handle %#lx created.\r\n", pstTM, 0, 0, 0, 0);
  92. }
  93. if (iReturnVal != TIMER_OK)
  94. {
  95. (void)TimerMuxDestroy((HTIMERMUX *)&pstTM, hSession);
  96. }
  97. *pHTM = (HTIMERMUX)pstTM;
  98. //
  99. // Don't forget to call sessReleaseTimerMux() to unlock
  100. // the session's TimerMux critical section locked in
  101. // sessQueryTimerMux(). REV: 5/21/2002
  102. //
  103. sessReleaseTimerMux(hSession);
  104. return iReturnVal;
  105. }
  106. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  107. * FUNCTION: TimerMuxDestroy
  108. *
  109. * DESCRIPTION:
  110. * Destroys a timer multiplexer and any timers still active
  111. *
  112. * ARGUMENTS:
  113. * phTM -- Pointer to a timer mux handle of type HTIMERMUX created by
  114. * an earlier call to TimerMuxCreate. The value pointed to will
  115. * be set to NULL after the TimerMux is destroyed
  116. *
  117. * RETURNS:
  118. * TIMER_OK
  119. */
  120. int TimerMuxDestroy(HTIMERMUX * const phTM, const HSESSION hSession)
  121. {
  122. //
  123. // sessQueryTimerMux() locks the session's TimerMux
  124. // critical section. Call sessReleaseTimerMux() to unlock
  125. // the session's TimerMux critical section. REV: 5/21/2002
  126. //
  127. HTIMERMUX hTM = sessQueryTimerMux(hSession);
  128. ST_TIMER *pstTimer;
  129. ST_TIMER_MUX *pstTimerMux;
  130. assert(phTM);
  131. pstTimerMux = (ST_TIMER_MUX *)*phTM;
  132. if (pstTimerMux)
  133. {
  134. while (pstTimerMux->pstFirst)
  135. {
  136. pstTimer = pstTimerMux->pstFirst;
  137. (void)TimerDestroy((HTIMER *)&pstTimer);
  138. }
  139. if (pstTimerMux->uiTimer)
  140. {
  141. DbgOutStr("KillTimer (timers.c)\r\n",0,0,0,0,0);
  142. (void)KillTimer(pstTimerMux->hWnd, pstTimerMux->uiID);
  143. }
  144. free(pstTimerMux);
  145. pstTimerMux = NULL;
  146. DbgOutStr("TimerMux handle 0x%lx destroyed.\r\n", pstTimerMux, 0, 0, 0, 0);
  147. }
  148. *phTM = (HTIMERMUX)0;
  149. //
  150. // Don't forget to call sessReleaseTimerMux() to unlock
  151. // the session's TimerMux critical section locked in
  152. // sessQueryTimerMux(). REV: 5/21/2002
  153. //
  154. sessReleaseTimerMux(hSession);
  155. return TIMER_OK;
  156. }
  157. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  158. * FUNCTION: TimerCreate
  159. *
  160. * DESCRIPTION:
  161. * Creates a timer that will call a registered call back function at
  162. * regular intervals specified in milliseconds. Once created, a timer
  163. * can be destroyed by calling TimerDestroy(). TimerDestroy can be called
  164. * from within the timer callback procedure.
  165. *
  166. * ARGUMENTS:
  167. * hTM -- A timer multiplexer handle returned from a call to
  168. * TimerMuxCreate
  169. * phTimer -- Pointer to a variable of type HTIMER to receive the
  170. * handle of the new timer.
  171. * lInterval -- The timer interval in milliseconds. The callback function
  172. * will be called repeatedly at roughly this interval until
  173. * the timer is destroyed. The timer will operate at a minimum
  174. * resolution depending on system capabilities. In Windows 3.x,
  175. * the minimum resolution is 55 msecs. Due to the operation
  176. * of the underlying Windows timer function, any interval may
  177. * be extended an arbitrary amount of time.
  178. * pfCallback -- Pointer to a function to be called after each interval.
  179. * This function should be of type TIMER_CALLBACK. The
  180. * value passed should be the result of calling
  181. * MakeProcInstance on the actual callback function.
  182. *
  183. * This function should accept two arguments: a void ptr
  184. * value which will contain any value supplied in the
  185. * pvData argument described below; and an unsigned long
  186. * which will be set to the actual duration of the most
  187. * recent interval in milliseconds.
  188. * pvData -- Can contain any arbitrary data. This value will be
  189. * associated with the timer created and will be passed as
  190. * an argument when the callback funtion is called.
  191. *
  192. * RETURNS:
  193. * TIMER_OK if timer could be created.
  194. * TIMER_NOMEM if there was insufficient memory
  195. * TIMER_NOWINTIMER if no Windows timers are available
  196. * TIMER_ERROR if parameters were invalid (also generates an assert)
  197. */
  198. int TimerCreate(const HSESSION hSession,
  199. HTIMER * const phTimer,
  200. long lInterval,
  201. const TIMERCALLBACK pfCallback,
  202. void *pvData)
  203. {
  204. //
  205. // sessQueryTimerMux() locks the session's TimerMux
  206. // critical section. Call sessReleaseTimerMux() to unlock
  207. // the session's TimerMux critical section. REV: 5/21/2002
  208. //
  209. HTIMERMUX hTM = sessQueryTimerMux(hSession);
  210. ST_TIMER_MUX * const pstTimerMux = (ST_TIMER_MUX *)hTM;
  211. ST_TIMER *pstTimer = (ST_TIMER *)0;
  212. int iReturnVal = TIMER_OK;
  213. assert(pstTimerMux && pfCallback);
  214. if (pstTimerMux)
  215. {
  216. // Guard against a zero interval
  217. if (lInterval == 0L)
  218. {
  219. ++lInterval;
  220. }
  221. if (!pstTimerMux || !pfCallback)
  222. {
  223. iReturnVal = TIMER_ERROR;
  224. }
  225. else if ((pstTimer = malloc(sizeof(*pstTimer))) == NULL)
  226. {
  227. iReturnVal = TIMER_NOMEM;
  228. }
  229. else
  230. {
  231. pstTimer->hSession = hSession;
  232. pstTimer->pstNext = (ST_TIMER *)0;
  233. pstTimer->pstTimerMux = pstTimerMux;
  234. pstTimer->lInterval = lInterval;
  235. pstTimer->lLastFired = (long)GetTickCount();
  236. pstTimer->lFireTime = pstTimer->lLastFired + lInterval;
  237. pstTimer->pvData = pvData;
  238. pstTimer->pfCallback = pfCallback;
  239. TimerInsert(pstTimerMux, pstTimer);
  240. // Following code caused problems when called from a thread other
  241. // than the main thread
  242. // if ((iReturnVal = TimerSet(pstTimerMux)) != TIMER_OK)
  243. // (void)TimerDestroy((HTIMER *)&pstTimer);
  244. PostMessage(pstTimerMux->hWnd, WM_FAKE_TIMER, 0, 0);
  245. DbgOutStr("Timer handle %#lx (%#lx) created.\r\n", pstTimer, pstTimerMux, 0, 0, 0);
  246. }
  247. if (phTimer)
  248. {
  249. *phTimer = (HTIMER)pstTimer;
  250. }
  251. }
  252. //
  253. // Don't forget to call sessReleaseTimerMux() to unlock
  254. // the session's TimerMux critical section locked in
  255. // sessQueryTimerMux(). REV: 5/21/2002
  256. //
  257. sessReleaseTimerMux(hSession);
  258. return iReturnVal;
  259. }
  260. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  261. * FUNCTION: TimerDestroy
  262. *
  263. * DESCRIPTION:
  264. * Destroys a timer created with TimerCreate. This routine can be called
  265. * to destroy a timer from within its own callback functionl
  266. *
  267. * ARGUMENTS:
  268. * hTimer -- A timer handle returned from a call to TimerCreate.
  269. *
  270. * RETURNS:
  271. * TIMER_OK if the timer is found and destroyed.
  272. * TIMER_ERROR if the handle could not be found.
  273. */
  274. int TimerDestroy(HTIMER * const phTimer)
  275. {
  276. int iReturnVal = TIMER_OK;
  277. ST_TIMER stDummy;
  278. ST_TIMER *pstTimer = (ST_TIMER *)*phTimer;
  279. ST_TIMER *pstScan;
  280. ST_TIMER *pstFound;
  281. ST_TIMER_MUX *pstTimerMux;
  282. assert(phTimer);
  283. if (pstTimer)
  284. {
  285. //
  286. // sessQueryTimerMux() locks the session's TimerMux
  287. // critical section. Call sessReleaseTimerMux() to unlock
  288. // the session's TimerMux critical section. REV: 5/21/2002
  289. //
  290. HTIMERMUX hTM = sessQueryTimerMux(pstTimer->hSession);
  291. //
  292. // Get the session handle for call to sessReleaseTimerMux() later.
  293. //
  294. const HSESSION hSession = pstTimer->hSession;
  295. // Get pointer to parent struct
  296. pstTimerMux = pstTimer->pstTimerMux;
  297. if (pstTimerMux)
  298. {
  299. // If a timer is being destroyed from within its own callback, it
  300. // has already been removed from the timer chain. Setting
  301. // pstTimerMux->pstCurrent to NULL will prevent it from being
  302. // rescheduled.
  303. if (pstTimer == pstTimerMux->pstCurrent)
  304. {
  305. free(pstTimer);
  306. pstTimer = NULL;
  307. DbgOutStr("Timer destroyed 0x%lx\r\n", (LONG)pstTimer, 0, 0, 0, 0);
  308. *phTimer = (HTIMER)0;
  309. pstTimerMux->pstCurrent = (ST_TIMER *)0;
  310. }
  311. else
  312. {
  313. // Set up dummy node at head of list to avoid a bunch of
  314. // special cases
  315. stDummy.pstNext = pstTimerMux->pstFirst;
  316. pstScan = &stDummy;
  317. // Scan through list for match, maintaining pointer to the
  318. // node BEFORE
  319. while ((pstFound = pstScan->pstNext) != (ST_TIMER *)0)
  320. {
  321. if (pstFound == pstTimer)
  322. {
  323. break;
  324. }
  325. pstScan = pstFound;
  326. }
  327. // pstFound will be NULL if timer was not in list, otherwise
  328. // pstFound is the node to remove and pstScan is the node
  329. // prior to it
  330. if (!pstFound)
  331. {
  332. iReturnVal = TIMER_ERROR;
  333. }
  334. else
  335. {
  336. pstScan->pstNext = pstFound->pstNext;
  337. DbgOutStr("Timer handle 0x%lx destroyed.\r\n", pstFound, 0, 0, 0, 0);
  338. free(pstFound);
  339. pstFound = NULL;
  340. // If we just destroyed a timer from within its own callback,
  341. // leave a sign so the timer proc will know
  342. if (pstFound == pstTimerMux->pstCurrent)
  343. pstTimerMux->pstCurrent = (ST_TIMER *)0;
  344. // Remove dummy node from start of list
  345. pstTimerMux->pstFirst = stDummy.pstNext;
  346. *phTimer = (HTIMER)0;
  347. }
  348. }
  349. }
  350. else
  351. {
  352. iReturnVal = TIMER_ERROR;
  353. }
  354. //
  355. // Don't forget to call sessReleaseTimerMux() to unlock
  356. // the session's TimerMux critical section locked in
  357. // sessQueryTimerMux(). REV: 5/21/2002
  358. //
  359. sessReleaseTimerMux(hSession);
  360. }
  361. return iReturnVal;
  362. }
  363. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  364. * FUNCTION: TimerMuxProc
  365. *
  366. * DESCRIPTION:
  367. * This function should be called by the window proc of the window whose
  368. * handle was passed to TimerMuxCreate when a WM_TIMER message is received.
  369. * It uses one Windows timer to control any number of individual multiplexed
  370. * timers.
  371. *
  372. * ARGUMENTS:
  373. * hSession -- The session to retreive the TimerMux handle from.
  374. *
  375. * RETURNS:
  376. * nothing
  377. */
  378. void TimerMuxProc(const HSESSION hSession)
  379. {
  380. //
  381. // sessQueryTimerMux() locks the session's TimerMux
  382. // critical section. Call sessReleaseTimerMux() to unlock
  383. // the session's TimerMux critical section. REV: 5/21/2002
  384. //
  385. HTIMERMUX hTM = sessQueryTimerMux(hSession);
  386. ST_TIMER *pstScan;
  387. ST_TIMER_MUX * const pstTimerMux = (ST_TIMER_MUX *)hTM;
  388. long lNow;
  389. TIMERCALLBACK *pfCallback;
  390. // Callbacks to timer procs to the printing routines can take a
  391. // long time because of paper-out, etc. Since the AbortProc in
  392. // the printer routines yields via a message loop, it is possible
  393. // (read probable) that we can recursively enter this routine.
  394. // The fInMuxProc flag guards against such an event. - mrw
  395. if (!pstTimerMux->fInMuxProc)
  396. {
  397. pstTimerMux->fInMuxProc = TRUE;
  398. }
  399. else
  400. {
  401. return;
  402. }
  403. lNow = (long)GetTickCount();
  404. DbgOutStr("%ld ", lNow, 0, 0, 0, 0);
  405. // In the following routine, note that the node associated with the
  406. // current call back is NOT linked into the timer chain during the
  407. // call back. This allows TimerDestroy to be called on a timer from
  408. // within its own call back. It is also OK to call TimerCreate from
  409. // within call backs.
  410. // Since timer ticks can be delayed, more than one event may have expired
  411. pstScan = pstTimerMux->pstFirst;
  412. while (pstScan && lNow > pstScan->lFireTime)
  413. {
  414. // Keep track of which timer is being called
  415. pstTimerMux->pstCurrent = pstScan;
  416. // Remove current node from list (will be added back in later if
  417. // not destroyed)
  418. pstTimerMux->pstFirst = pstScan->pstNext;
  419. pfCallback = &pstScan->pfCallback;
  420. // Give up the critical section while doing the callback so
  421. // a lengthy call back won't delay any other threads
  422. sessReleaseTimerMux(hSession);
  423. // Activate the call back function
  424. (*pfCallback)(pstScan->pvData, lNow - pstScan->lLastFired);
  425. hTM = sessQueryTimerMux(hSession);
  426. assert(pstTimerMux == (ST_TIMER_MUX *)hTM);
  427. lNow = (long)GetTickCount();
  428. DbgOutStr("%ld ", lNow, 0, 0, 0, 0);
  429. // If timer was destroyed during callback, pstTimerMux->pstCurrent will have
  430. // been sent to NULL; otherwise reschedule this timer
  431. if ((pstScan = pstTimerMux->pstCurrent) != (ST_TIMER *)0)
  432. {
  433. DbgOutStr("Reschedule ", 0, 0, 0, 0, 0);
  434. // Reschedule timer
  435. pstScan->lLastFired = lNow;
  436. pstScan->lFireTime = lNow + pstScan->lInterval;
  437. // link this timer back into the list
  438. TimerInsert(pstTimerMux, pstScan);
  439. pstTimerMux->pstCurrent = (ST_TIMER *)0;
  440. }
  441. // First node on list is always the next one due to fire
  442. pstScan = pstTimerMux->pstFirst;
  443. }
  444. (void)TimerSet(pstTimerMux);
  445. pstTimerMux->fInMuxProc = FALSE;
  446. // LeaveCriticalSection(&pstTimerMux->critsec);
  447. //
  448. // Don't forget to call sessReleaseTimerMux() to unlock
  449. // the session's TimerMux critical section locked in
  450. // sessQueryTimerMux(). REV: 5/21/2002
  451. //
  452. sessReleaseTimerMux(hSession);
  453. return;
  454. }
  455. // INTERNAL ROUTINES
  456. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  457. * FUNCTION: TimerInsert
  458. *
  459. * DESCRIPTION:
  460. * Links a timer control node into the linked list of all multiplexed timers.
  461. * The list is maintained in order by when the node is due to fire.
  462. *
  463. * ARGUMENTS:
  464. * pstTimerMux -- Handle to the timer multiplexer.
  465. * pstTimer -- Pointer to a node to be inserted.
  466. *
  467. * RETURNS:
  468. * nothing
  469. */
  470. void TimerInsert(ST_TIMER_MUX *pstTimerMux,
  471. ST_TIMER *pstTimer)
  472. {
  473. ST_TIMER *pstScan;
  474. pstScan = pstTimerMux->pstFirst;
  475. // If there are no other nodes in the list or if the new timer is
  476. // scheduled before the first one in the list, link new one in first
  477. if (!pstScan || pstTimer->lFireTime < pstScan->lFireTime)
  478. {
  479. pstTimer->pstNext = pstScan;
  480. pstTimerMux->pstFirst = pstTimer;
  481. }
  482. else
  483. {
  484. // Insert sorted by lFireTime
  485. while (pstScan->pstNext &&
  486. pstScan->pstNext->lFireTime < pstTimer->lFireTime)
  487. {
  488. pstScan = pstScan->pstNext;
  489. }
  490. // Link into chain
  491. pstTimer->pstNext = pstScan->pstNext;
  492. pstScan->pstNext = pstTimer;
  493. }
  494. return;
  495. }
  496. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  497. * FUNCTION: TimerSet
  498. *
  499. * DESCRIPTION:
  500. * Sets up a Windows timer using SetTimer to fire when the next multiplexed
  501. * timer needs attention. Since the Window timer operates with an interval
  502. * specified in a USHORT, there are times when the timer may have to be
  503. * set to go off before the next required interval. If there are no
  504. * multiplexed timers to be serviced, the timer is set to its maximum time
  505. * anyway. By keeping one timer whether we need it or not, we guarantee
  506. * that one will be available when we DO need it.
  507. *
  508. * ARGUMENTS:
  509. * none
  510. *
  511. * RETURNS:
  512. * TIMER_OK if successful.
  513. * TIMER_NOWINTIMER if no Windows timers were available
  514. */
  515. int TimerSet(ST_TIMER_MUX *pstTimerMux)
  516. {
  517. UINT uiDuration = 100000;
  518. int iReturnVal = TIMER_OK;
  519. long lTickCount;
  520. if (pstTimerMux->pstFirst)
  521. {
  522. lTickCount = (long)GetTickCount();
  523. if (pstTimerMux->pstFirst->lFireTime <= lTickCount)
  524. uiDuration = 1; // Timer has already expired
  525. else
  526. uiDuration = (UINT)(pstTimerMux->pstFirst->lFireTime - lTickCount);
  527. }
  528. if (pstTimerMux->uiTimer == 0 || uiDuration != pstTimerMux->uiLastDuration)
  529. {
  530. // if (pstTimerMux->uiTimer != 0)
  531. // {
  532. // DbgOutStr("KillTimer (timers.c)\r\n",0,0,0,0,0);
  533. // fResult = KillTimer(pstTimerMux->hWnd, pstTimerMux->uiID);
  534. // assert(fResult);
  535. // }
  536. pstTimerMux->uiTimer =
  537. SetTimer(pstTimerMux->hWnd, pstTimerMux->uiID, uiDuration, NULL);
  538. DbgOutStr("SetTimer (timers.c)\r\n", 0,0,0,0,0);
  539. if (pstTimerMux->uiTimer == 0)
  540. {
  541. iReturnVal = TIMER_NOWINTIMER;
  542. }
  543. pstTimerMux->uiLastDuration = uiDuration;
  544. }
  545. return (iReturnVal);
  546. }
  547. // End of timers.c