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.

814 lines
22 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. winstmm.c
  5. Abstract:
  6. This module contains the timer manager functions
  7. Functions:
  8. WinsTmmInsertEntry
  9. TmmThdInitFn
  10. WinsTmmInit
  11. HandleReq
  12. SignalClient
  13. WinsTmmDeleteReqs
  14. WinsTmmDeallocReq
  15. Portability:
  16. This module is portable
  17. Author:
  18. Pradeep Bahl (PradeepB) Mar-1993
  19. Revision History:
  20. Modification date Person Description of modification
  21. ----------------- ------- ----------------------------
  22. --*/
  23. /*
  24. * Includes
  25. */
  26. #include <time.h>
  27. #include "wins.h"
  28. #include "nms.h"
  29. #include "winsevt.h"
  30. #include "winsmsc.h"
  31. #include "winstmm.h"
  32. #include "winsque.h"
  33. #include "winsthd.h"
  34. /*
  35. * Local Macro Declarations
  36. */
  37. /*
  38. * Local Typedef Declarations
  39. */
  40. /*
  41. * Global Variable Definitions
  42. */
  43. HANDLE WinsTmmHeapHdl; //handle of heap to allocate TMM work items
  44. // from
  45. /*
  46. * Local Variable Definitions
  47. */
  48. STATIC CRITICAL_SECTION sTmmReqCntCrtSec;
  49. STATIC DWORD sTmmReqCnt = 0;
  50. //
  51. // This stores the req. id of the request at the top of the queue
  52. // (i.e. one for which the timer thread is doing a timed wait)
  53. //
  54. STATIC DWORD sReqIdOfCurrReq;
  55. /*
  56. * Local Function Prototype Declarations
  57. */
  58. STATIC
  59. DWORD
  60. TmmThdInitFn(
  61. LPVOID pParam
  62. );
  63. VOID
  64. HandleReq(
  65. OUT LPLONG pDeltaTime
  66. );
  67. VOID
  68. SignalClient(
  69. VOID
  70. );
  71. /* prototypes for functions local to this module go here */
  72. VOID
  73. WinsTmmInsertEntry(
  74. PQUE_TMM_REQ_WRK_ITM_T pPassedWrkItm,
  75. WINS_CLIENT_E Client_e,
  76. QUE_CMD_TYP_E CmdTyp_e,
  77. BOOL fResubmit,
  78. time_t AbsTime,
  79. DWORD TimeInt,
  80. PQUE_HD_T pRspQueHd,
  81. LPVOID pClientCtx,
  82. DWORD MagicNo,
  83. PWINSTMM_TIMER_REQ_ACCT_T pSetTimerReqs
  84. )
  85. /*++
  86. Routine Description:
  87. This function is called to insert a work item into the Timer Manager's
  88. request queue (delta queue). It allocates a work item if required
  89. (if pWrkItm != NULL) and enqueues it in its proper position in
  90. the delta queue of the TMM thread. It then signals the TMM thread
  91. if required.
  92. Arguments:
  93. pWrkItm - work item to queue (if pWrkItm != NULL)
  94. Client_e - id. of client that submitted the request
  95. CmdTyp_e - type of command (set timer, modify timer, etc)
  96. fResubmit - Is it a resubmit
  97. AbsTime - absolute time (in secs) at which the client needs to be
  98. notified
  99. TimeInt - time interval in seconds after which the client needs to
  100. be notified
  101. pRspQueHd - Que Head of the queue where the notification needs to be
  102. queued
  103. pClientCtx - Client's context that needs to be put in the work item
  104. pSetTimerReqs - For future extensibility
  105. Externals Used:
  106. None
  107. Return Value:
  108. None
  109. Error Handling:
  110. Called by:
  111. Side Effects:
  112. Comments:
  113. None
  114. --*/
  115. {
  116. PQUE_TMM_REQ_WRK_ITM_T pWrkItm;
  117. PQUE_TMM_REQ_WRK_ITM_T pTmp;
  118. BOOL fInserted = FALSE;
  119. UNREFERENCED_PARAMETER(pSetTimerReqs);
  120. if (!pPassedWrkItm)
  121. {
  122. QueAllocWrkItm(
  123. WinsTmmHeapHdl,
  124. sizeof(QUE_TMM_REQ_WRK_ITM_T),
  125. &pWrkItm
  126. );
  127. }
  128. else
  129. {
  130. pWrkItm = pPassedWrkItm;
  131. }
  132. //
  133. // Put a request id (a monotonically increasing number)
  134. //
  135. EnterCriticalSection(&sTmmReqCntCrtSec);
  136. sTmmReqCnt++;
  137. LeaveCriticalSection(&sTmmReqCntCrtSec);
  138. pWrkItm->ReqId = sTmmReqCnt;
  139. //
  140. // If work item was allocated or if it is not a resubmit
  141. // init the rest of the fields appropriately
  142. //
  143. pWrkItm->CmdTyp_e = CmdTyp_e;
  144. pWrkItm->AbsTime = AbsTime;
  145. pWrkItm->TimeInt = TimeInt;
  146. pWrkItm->QueTyp_e = QUE_E_WINSTMQ;
  147. pWrkItm->RspEvtHdl = pRspQueHd->EvtHdl;
  148. pWrkItm->pRspQueHd = pRspQueHd;
  149. pWrkItm->pClientCtx = pClientCtx;
  150. pWrkItm->Client_e = Client_e;
  151. pWrkItm->MagicNo = MagicNo;
  152. EnterCriticalSection(&QueWinsTmmQueHd.CrtSec);
  153. try {
  154. if (IsListEmpty(&QueWinsTmmQueHd.Head))
  155. {
  156. InsertTailList(&QueWinsTmmQueHd.Head, &pWrkItm->Head);
  157. if (!SetEvent(QueWinsTmmQueHd.EvtHdl))
  158. {
  159. WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SIGNAL_TMM_ERR);
  160. DBGPRINT0(EXC, "Could not signal Tmm Thd\n");
  161. WINS_RAISE_EXC_M(WINS_EXC_SIGNAL_TMM_ERR);
  162. }
  163. fInserted = TRUE;
  164. }
  165. else
  166. {
  167. //
  168. // get the address of the first entry in the queue.
  169. //
  170. pTmp = (PQUE_TMM_REQ_WRK_ITM_T)QueWinsTmmQueHd.Head.Flink;
  171. //
  172. // Go over the circular linked list until we hit the head
  173. // of the queue
  174. //
  175. while(pTmp != (PQUE_TMM_REQ_WRK_ITM_T)&QueWinsTmmQueHd.Head)
  176. {
  177. //
  178. // If the list entry has a longer timer than the new entry,
  179. // insert the new entry before it
  180. //
  181. if (pTmp->AbsTime > pWrkItm->AbsTime)
  182. {
  183. pWrkItm->Head.Flink = &pTmp->Head;
  184. pWrkItm->Head.Blink = pTmp->Head.Blink;
  185. pTmp->Head.Blink->Flink = &pWrkItm->Head;
  186. pTmp->Head.Blink = &pWrkItm->Head;
  187. fInserted = TRUE;
  188. //
  189. // The element has been inserted. Let us break out
  190. // of the loop
  191. //
  192. break;
  193. }
  194. pTmp = (PQUE_TMM_REQ_WRK_ITM_T)pTmp->Head.Flink;
  195. }
  196. }
  197. //
  198. // If the entry was not inserted (i.e. all entries in the queue have
  199. // a shorter expiry than our entry), insert the entry at the
  200. // end of the list
  201. //
  202. if (!fInserted)
  203. {
  204. InsertTailList(&QueWinsTmmQueHd.Head, &pWrkItm->Head);
  205. }
  206. //
  207. // If this is the top most entry in the queue and there is
  208. // at least one more entry in the queue, signal the TMM
  209. // thread so that it can start a timer for it. If the above
  210. // is not true, relax (either the TMM thread has already been
  211. // signaled (if this is the only item in the queue) or it does not
  212. // need to be signaled (this is not the top-most item in the queue).
  213. //
  214. if (
  215. (pWrkItm->Head.Blink == &QueWinsTmmQueHd.Head)
  216. &&
  217. (pWrkItm->Head.Flink != &QueWinsTmmQueHd.Head)
  218. )
  219. {
  220. if (!SetEvent(QueWinsTmmQueHd.EvtHdl))
  221. {
  222. WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SIGNAL_TMM_ERR);
  223. DBGPRINT0(EXC, "Could not signal Tmm Thd\n");
  224. WINS_RAISE_EXC_M(WINS_EXC_SIGNAL_TMM_ERR);
  225. }
  226. }
  227. } // end of try ..
  228. finally {
  229. LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec);
  230. }
  231. return;
  232. }
  233. VOID
  234. WinsTmmInit(
  235. VOID
  236. )
  237. /*++
  238. Routine Description:
  239. This is the function that initializes the timer manager
  240. Arguments:
  241. None
  242. Externals Used:
  243. None
  244. Return Value:
  245. None
  246. Error Handling:
  247. Called by:
  248. Init in nms.c
  249. Side Effects:
  250. A timer thread is created
  251. Comments:
  252. None
  253. --*/
  254. {
  255. STATUS RetStat;
  256. //
  257. // Initialize the critical section guarding the counter for
  258. // Tmm requests
  259. //
  260. InitializeCriticalSection(&sTmmReqCntCrtSec);
  261. /*
  262. * Create heap for allocating name challenge work items
  263. */
  264. DBGPRINT0(HEAP_CRDL, "WinsTmmInit: Tmm. Chl. heap\n");
  265. WinsTmmHeapHdl = WinsMscHeapCreate(
  266. 0, /*to have mutual exclusion */
  267. TMM_INIT_HEAP_SIZE
  268. );
  269. InitializeListHead(&QueWinsTmmQueHd.Head);
  270. //
  271. //
  272. // Create the timer thread. This function will
  273. // initialize the critical section and the Evt handle passed
  274. // to it
  275. //
  276. RetStat = WinsMscSetUpThd(
  277. &QueWinsTmmQueHd, //queue head
  278. TmmThdInitFn, //init function
  279. NULL, // no param
  280. &WinsThdPool.TimerThds[0].ThdHdl,
  281. &WinsThdPool.TimerThds[0].ThdId
  282. );
  283. if (RetStat == WINS_SUCCESS)
  284. {
  285. WinsThdPool.TimerThds[0].fTaken = TRUE;
  286. WinsThdPool.ThdCount++; //increment the thread count
  287. }
  288. else
  289. {
  290. WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
  291. }
  292. return;
  293. }
  294. DWORD
  295. TmmThdInitFn(
  296. LPVOID pParam
  297. )
  298. /*++
  299. Routine Description:
  300. This is the top-most function of the TMM thread
  301. Arguments:
  302. pParam - Param to the top most function (not used)
  303. Externals Used:
  304. None
  305. Return Value:
  306. Success status codes -- WINS_SUCCESS
  307. Error status codes -- WINS_FAILURE
  308. Error Handling:
  309. Called by:
  310. WinsTmmInit
  311. Side Effects:
  312. Comments:
  313. None
  314. --*/
  315. {
  316. LONG DeltaTime;
  317. BOOL fSignaled;
  318. UNREFERENCED_PARAMETER(pParam);
  319. try {
  320. while(TRUE)
  321. {
  322. WinsMscWaitInfinite(QueWinsTmmQueHd.EvtHdl);
  323. while(!IsListEmpty(&QueWinsTmmQueHd.Head))
  324. {
  325. HandleReq(&DeltaTime);
  326. if (DeltaTime != 0)
  327. {
  328. //
  329. // Do a timed wait until either the timer expires
  330. // or the event is signaled.
  331. //
  332. WinsMscWaitTimed(
  333. QueWinsTmmQueHd.EvtHdl,
  334. DeltaTime * 1000, //convert to millisecs
  335. &fSignaled
  336. );
  337. //
  338. // If signaled (interrupte from the wait), it means
  339. // that there is a more urgent request in the timer
  340. // queue.
  341. //
  342. if (fSignaled)
  343. {
  344. HandleReq(&DeltaTime);
  345. }
  346. else //timer expired
  347. {
  348. SignalClient();
  349. }
  350. }
  351. else
  352. {
  353. SignalClient();
  354. }
  355. }
  356. }
  357. }
  358. except(EXCEPTION_EXECUTE_HANDLER) {
  359. DBGPRINT0(EXC,
  360. "TmmThdInitFn: Timer Thread encountered an exception\n");
  361. WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_TMM_EXC);
  362. ExitThread(WINS_FAILURE);
  363. }
  364. //
  365. // We should never reach here
  366. //
  367. ASSERT(0);
  368. return(WINS_FAILURE);
  369. }
  370. VOID
  371. HandleReq(
  372. OUT LPLONG pDeltaTime
  373. )
  374. /*++
  375. Routine Description:
  376. This function is called to handle a timer request. The function
  377. is called when the Timer thread is signaled.
  378. Arguments:
  379. pDeltaTime - Time Interval to wait for before signaling the client
  380. Externals Used:
  381. None
  382. Return Value:
  383. None
  384. Error Handling:
  385. Called by:
  386. TmmThdInitFn
  387. Side Effects:
  388. Comments:
  389. None
  390. --*/
  391. {
  392. time_t AbsTime;
  393. time_t CurrTime;
  394. QUE_CMD_TYP_E CmdTyp_e;
  395. (void)time(&CurrTime);
  396. EnterCriticalSection(&QueWinsTmmQueHd.CrtSec);
  397. //
  398. // If list is empty, return.
  399. //
  400. if (IsListEmpty(&QueWinsTmmQueHd.Head))
  401. {
  402. *pDeltaTime = 0;
  403. LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec);
  404. return;
  405. }
  406. //
  407. // Retrieve the absolute time corresponding to the
  408. // first entry in the timer queue.
  409. //
  410. AbsTime = ((PQUE_TMM_REQ_WRK_ITM_T)
  411. (QueWinsTmmQueHd.Head.Flink))->AbsTime;
  412. CmdTyp_e = ((PQUE_TMM_REQ_WRK_ITM_T)
  413. (QueWinsTmmQueHd.Head.Flink))->CmdTyp_e;
  414. ASSERT(CmdTyp_e == QUE_E_CMD_SET_TIMER);
  415. //
  416. // Store the request id of the request that we will wait on
  417. // in the STATIC
  418. //
  419. sReqIdOfCurrReq = ((PQUE_TMM_REQ_WRK_ITM_T)
  420. (QueWinsTmmQueHd.Head.Flink))->ReqId;
  421. LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec);
  422. switch(CmdTyp_e)
  423. {
  424. case(QUE_E_CMD_SET_TIMER):
  425. *pDeltaTime = (LONG)(AbsTime - CurrTime);
  426. //
  427. // It is possible that we might have a small
  428. // negative value here, just because of the
  429. // time it took to process the request.
  430. //
  431. if (*pDeltaTime < 0)
  432. {
  433. *pDeltaTime = 0;
  434. }
  435. break;
  436. case(QUE_E_CMD_MODIFY_TIMER):
  437. WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
  438. DBGPRINT0(ERR, "Not supported yet\n");
  439. WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
  440. break;
  441. case(QUE_E_CMD_CANCEL_TIMER):
  442. WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
  443. DBGPRINT0(ERR, "Not supported yet\n");
  444. WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
  445. break;
  446. default:
  447. WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
  448. DBGPRINT1(EXC, "Wierd: Invalid Request. CmdType is (%d), \n", CmdTyp_e);
  449. WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
  450. break;
  451. }
  452. return;
  453. }
  454. VOID
  455. SignalClient(
  456. VOID
  457. )
  458. /*++
  459. Routine Description:
  460. This function is called to notify the client that its
  461. timer request has expired.
  462. Arguments:
  463. None
  464. Externals Used:
  465. None
  466. Return Value:
  467. None
  468. Error Handling:
  469. Called by:
  470. TmmThdInitFn
  471. Side Effects:
  472. Comments:
  473. None
  474. --*/
  475. {
  476. PQUE_TMM_REQ_WRK_ITM_T pWrkItm;
  477. FUTURES("Optimize to signal all clients whose requests have expired")
  478. EnterCriticalSection(&QueWinsTmmQueHd.CrtSec);
  479. pWrkItm = (PQUE_TMM_REQ_WRK_ITM_T)RemoveHeadList(&QueWinsTmmQueHd.Head);
  480. //
  481. // If the top of the queue has a different work item than the
  482. // one we were doing a timed wait for, it means that the there has
  483. // been a queue purge (see WinsTmmDeleteEntry. Simply return.
  484. //
  485. if (sReqIdOfCurrReq != pWrkItm->ReqId)
  486. {
  487. LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec);
  488. return;
  489. }
  490. LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec);
  491. pWrkItm->CmdTyp_e = QUE_E_CMD_TIMER_EXPIRED;
  492. //
  493. // Insert into client's queue
  494. //
  495. QueInsertWrkItm(
  496. &pWrkItm->Head,
  497. 0, //not used -- change to an enumrator val
  498. pWrkItm->pRspQueHd
  499. );
  500. return;
  501. }
  502. VOID
  503. WinsTmmDeleteReqs(
  504. WINS_CLIENT_E Client_e
  505. )
  506. /*++
  507. Routine Description:
  508. This function is called to delete all set timer requests submitted
  509. by a client
  510. Arguments:
  511. Externals Used:
  512. None
  513. Return Value:
  514. None
  515. Error Handling:
  516. Called by:
  517. Reconfig (in rplpull.c)
  518. Side Effects:
  519. Comments:
  520. In the future, enhance this function so that it delete requests
  521. based on other criteria
  522. --*/
  523. {
  524. PQUE_TMM_REQ_WRK_ITM_T pTmp;
  525. PQUE_TMM_REQ_WRK_ITM_T pMemToDealloc;
  526. BOOL fFirstEntry = FALSE;
  527. EnterCriticalSection(&QueWinsTmmQueHd.CrtSec);
  528. try {
  529. if (!IsListEmpty(&QueWinsTmmQueHd.Head))
  530. {
  531. //
  532. // get the address of the first entry in the queue.
  533. //
  534. pTmp = (PQUE_TMM_REQ_WRK_ITM_T)QueWinsTmmQueHd.Head.Flink;
  535. //
  536. // We loop until we get to the head of the list (the linked
  537. // list is a circular list)
  538. //
  539. while(pTmp != (PQUE_TMM_REQ_WRK_ITM_T)&QueWinsTmmQueHd.Head)
  540. {
  541. //
  542. // If the entry was queued by the client, get rid of
  543. // it
  544. //
  545. if (pTmp->Client_e == Client_e)
  546. {
  547. //
  548. // If this is the first entry in the queue, it
  549. // means that the timer thread is doing a
  550. // wait on it. Signal it.
  551. //
  552. if (
  553. !fFirstEntry
  554. &&
  555. (pTmp->Head.Blink == &QueWinsTmmQueHd.Head)
  556. )
  557. {
  558. fFirstEntry = TRUE;
  559. }
  560. // if (pTmp->Head.Flink == &QueWinsTmmQueHd.Head)
  561. if (fFirstEntry)
  562. {
  563. if (!SetEvent(QueWinsTmmQueHd.EvtHdl))
  564. {
  565. WINSEVT_LOG_M(
  566. WINS_FAILURE,
  567. WINS_EVT_SIGNAL_TMM_ERR
  568. );
  569. DBGPRINT0(EXC,
  570. "Could not signal Tmm for canceling a request\n");
  571. WINS_RAISE_EXC_M(WINS_EXC_SIGNAL_TMM_ERR);
  572. }
  573. }
  574. //
  575. // unlink the request
  576. //
  577. pTmp->Head.Flink->Blink = pTmp->Head.Blink;
  578. pTmp->Head.Blink->Flink = pTmp->Head.Flink;
  579. pMemToDealloc = pTmp;
  580. pTmp = (PQUE_TMM_REQ_WRK_ITM_T)pTmp->Head.Flink;
  581. //
  582. // Dealloc the dequeued work item
  583. //
  584. WinsTmmDeallocReq(pMemToDealloc);
  585. }
  586. else
  587. {
  588. pTmp = (PQUE_TMM_REQ_WRK_ITM_T)pTmp->Head.Flink;
  589. }
  590. }
  591. }
  592. } // end of try block
  593. except(EXCEPTION_EXECUTE_HANDLER) {
  594. DBGPRINT1(EXC, "WinsTmmDeleteReqs: Got exception (%x)\n",
  595. (DWORD)GetExceptionCode());
  596. WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_TMM_EXC);
  597. }
  598. LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec);
  599. return;
  600. }
  601. __inline
  602. VOID
  603. WinsTmmDeallocReq(
  604. PQUE_TMM_REQ_WRK_ITM_T pWrkItm
  605. )
  606. /*++
  607. Routine Description:
  608. This function is called to deallocate a timer request work item
  609. Arguments:
  610. pWrkItm - Work Item
  611. Externals Used:
  612. WinsTmmHeapHdl
  613. Return Value:
  614. None
  615. Error Handling:
  616. Called by:
  617. RplPullInit
  618. Side Effects:
  619. Comments:
  620. None
  621. --*/
  622. {
  623. //
  624. // deallocate the work item
  625. //
  626. QueDeallocWrkItm(
  627. WinsTmmHeapHdl,
  628. pWrkItm
  629. );
  630. return;
  631. }