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.

1200 lines
31 KiB

  1. //=============================================================================
  2. // Copyright (c) 1997 Microsoft Corporation
  3. // File Name: igmptimer.c
  4. //
  5. // Abstract: This module implements the igmptimer
  6. //
  7. // Author: K.S.Lokesh (lokeshs@) 11-1-97
  8. //
  9. // Revision History:
  10. //=============================================================================
  11. #include "pchigmp.h"
  12. #if DEBUG_TIMER_LEVEL & DEBUG_TIMER_TIMERID
  13. DWORD TimerId =0;
  14. #endif
  15. //------------------------------------------------------------------------------
  16. //
  17. // FUNCTION PROTOTYPES USED ONLY IN THIS FILE
  18. //
  19. VOID
  20. SetNextTime(
  21. LONGLONG llCurTime,
  22. DWORD dwLowIndex
  23. );
  24. VOID
  25. ResyncTimerBuckets(
  26. LONGLONG llCurTime
  27. );
  28. VOID
  29. InsertTimerInSortedList(
  30. PIGMP_TIMER_ENTRY pteNew,
  31. PLIST_ENTRY pHead
  32. );
  33. //------------------------------------------------------------------------------
  34. //
  35. // #DEFINES USED ONLY IN THIS FILE
  36. //
  37. //
  38. //approx 16 secs in each bucket:
  39. //it is approx not accurate as I divide by 2^10 instead of 1000
  40. //TIMER_BUCKET_GRANULARITY should be 2^TIMER_BUCKET_GRANULARITY_SHIFT
  41. //
  42. #define TIMER_BUCKET_GRANULARITY 16
  43. #define TIMER_BUCKET_GRANULARITY_SHIFT 4
  44. #define SEC_CONV_SHIFT 10
  45. #define TIMER_BUCKET_GRANULARITY_ABS \
  46. ((LONGLONG) (1 << (TIMER_BUCKET_GRANULARITY_SHIFT + SEC_CONV_SHIFT) ))
  47. #define MAP_TO_BUCKET(dwBucket, ilTime) \
  48. dwBucket = (DWORD) (((ilTime)-g_TimerStruct.SyncTime) \
  49. >> (TIMER_BUCKET_GRANULARITY_SHIFT+SEC_CONV_SHIFT)); \
  50. dwBucket = dwBucket>NUM_TIMER_BUCKETS-1? NUM_TIMER_BUCKETS-1: dwBucket
  51. // I fire a timer even if it is set to 10 millisec in the future.
  52. #define FORWARD_TIMER_FIRED 10
  53. //------------------------------------------------------------------------------
  54. //------------------------------------------------------------------------------
  55. // _InsertTimer
  56. //
  57. // Inserts a timer into the local timer queue. Time should always be relative.
  58. //
  59. // Locks: Assumes lock taken on ptg->CS
  60. //------------------------------------------------------------------------------
  61. DWORD
  62. InsertTimer (
  63. PIGMP_TIMER_ENTRY pte,
  64. LONGLONG llNewTime,
  65. BOOL bResync
  66. )
  67. {
  68. LONGLONG llCurTime = GetCurrentIgmpTime();
  69. PIGMP_TIMER_GLOBAL ptg;
  70. DWORD dwBucket, Error = NO_ERROR;
  71. // InsertTimer shouldnt be called when time is active
  72. if (pte->Status & TIMER_STATUS_ACTIVE) {
  73. Trace0(ERR, "ERROR: INSERT TIMER BEING CALLED INSTEAD OF UPDATE TIMER");
  74. UpdateLocalTimer(pte, llNewTime);
  75. return NO_ERROR;
  76. }
  77. Trace0(ENTER1, "_InsertTimer()");
  78. // print the queue before inserting the timer
  79. #if DEBUG_TIMER_INSERTTIMER1
  80. Trace0(TIMER1, "Printing Timer Queue before InsertTimer");
  81. DebugPrintTimerQueue();
  82. #endif
  83. ptg = &g_TimerStruct;
  84. // convert relative time to absolute time
  85. llNewTime += llCurTime;
  86. pte->Timeout = llNewTime;
  87. MAP_TO_BUCKET(dwBucket, pte->Timeout);
  88. // print info about the timer being inserted
  89. #if DEBUG_TIMER_ACTIVITY
  90. {
  91. DWORD dwBucket, dwDiffTime;
  92. CHAR str1[20], str2[20];
  93. MAP_TO_BUCKET(dwBucket, pte->Timeout);
  94. GetTimerDebugInfo(str1, str2, &dwDiffTime, pte, llCurTime);
  95. Trace7(TIMER, "Inserting timer <%d><%d><%d> Timeout:%lu <%s> "
  96. "<%s> Status:%d", dwBucket, pte->Id, pte->Id2, dwDiffTime,
  97. str1, str2, pte->Status);
  98. }
  99. #endif
  100. //
  101. // insert timer in appropriate list
  102. //
  103. if (dwBucket==0) { // bucket 0 contains a sorted list
  104. InsertTimerInSortedList(pte, &ptg->TimesTable[0]);
  105. }
  106. else {
  107. InsertTailList(&ptg->TimesTable[dwBucket], &pte->Link);
  108. }
  109. ptg->NumTimers++;
  110. ptg->TableLowIndex = ptg->TableLowIndex<dwBucket
  111. ? ptg->TableLowIndex : dwBucket;
  112. //resynchronize timer list
  113. if (bResync) {
  114. if ( (ptg->TableLowIndex!=0)
  115. && (ptg->SyncTime + TIMER_BUCKET_GRANULARITY_ABS < llCurTime) ) {
  116. ResyncTimerBuckets(llCurTime);
  117. }
  118. }
  119. //
  120. // if time being inserted is lower than the minimum, then update wait timer
  121. //
  122. if ((IS_TIMER_INFINITE(ptg->WTTimeout)) || (pte->Timeout<=ptg->WTTimeout)) {
  123. ptg->WTTimeout = pte->Timeout;
  124. if (!IS_TIMER_INFINITE(ptg->WTTimeout)) {
  125. BOOL bSuccess ;
  126. bSuccess = ChangeTimerQueueTimer(ptg->WTTimer, ptg->WTTimer1,
  127. (ULONG) ((ptg->WTTimeout - llCurTime)),
  128. 1000000 // set a periodic timer
  129. );
  130. if (!bSuccess) {
  131. Error = GetLastError();
  132. Trace1(ERR, "ChangeTimerQueueTimer returned error:%d", Error);
  133. }
  134. else
  135. Trace1(TIMER1, "ChangeTimerQueueTimer set to %lu",
  136. (ULONG) ((ptg->WTTimeout - llCurTime))/1000);
  137. }
  138. }
  139. pte->Status = TIMER_STATUS_ACTIVE;
  140. #if EXTEND_DEBUG_INSERTTIMER2
  141. Trace0(TIMER1, "Printing Timer Queue after _InsertTimer");
  142. DebugPrintTimerQueue();
  143. #endif
  144. Trace0(LEAVE1, "Leaving _InsertTimer()");
  145. return NO_ERROR;
  146. } //end _InsertTimer
  147. //------------------------------------------------------------------------------
  148. // _UpdateLocalTimer
  149. //
  150. // Change the time in a timer structure and (re)insert it in the timer queue.
  151. // Locks: Assumes lock on the global timer
  152. //------------------------------------------------------------------------------
  153. VOID
  154. UpdateLocalTimer (
  155. PIGMP_TIMER_ENTRY pte,
  156. LONGLONG llNewTime
  157. )
  158. {
  159. Trace0(ENTER1, "_UpdateLocalTimer():");
  160. if (!(pte->Status&TIMER_STATUS_ACTIVE))
  161. Trace0(ERR, "ERROR: UPDATE BEING CALLED INSTEAD OF INSERT TIMER");
  162. // print info about the timer being updated
  163. #if DEBUG_TIMER_ACTIVITY
  164. {
  165. DWORD dwBucket, dwDiffTime;
  166. CHAR str1[20], str2[20];
  167. LONGLONG llCurTime = GetCurrentIgmpTime();
  168. MAP_TO_BUCKET(dwBucket, pte->Timeout);
  169. GetTimerDebugInfo(str1, str2, &dwDiffTime, pte, llCurTime);
  170. Trace8(TIMER, "Updating timer <%d><%d><%d> Timeout:%lu <%s> <%s> "
  171. "to %d Status:%d", dwBucket, pte->Id, pte->Id2, dwDiffTime,
  172. str1, str2, (DWORD)llNewTime, pte->Status);
  173. }
  174. #endif
  175. // first remote the timer
  176. if (pte->Status&TIMER_STATUS_ACTIVE) {
  177. RemoveTimer(pte);
  178. }
  179. // now insert the timer back into the timer queue. Resync flag is set
  180. InsertTimer(pte, llNewTime, TRUE);
  181. Trace0(LEAVE1, "_UpdateLocalTimer()");
  182. return;
  183. }
  184. //------------------------------------------------------------------------------
  185. // _RemoveTimer
  186. //
  187. // Removes the timer from the list. Changes the status of the timer to CREATED.
  188. // Assumes global timer lock.
  189. //------------------------------------------------------------------------------
  190. VOID
  191. RemoveTimer (
  192. PIGMP_TIMER_ENTRY pte
  193. )
  194. {
  195. LONGLONG llCurTime = GetCurrentIgmpTime();
  196. PIGMP_TIMER_GLOBAL ptg = &g_TimerStruct;
  197. Trace0(ENTER1, "_RemoveTimer()");
  198. // print info about the timer being removed
  199. #if DEBUG_TIMER_REMOVETIMER1
  200. {
  201. DWORD dwBucket, dwDiffTime;
  202. CHAR str1[20], str2[20];
  203. MAP_TO_BUCKET(dwBucket, pte->Timeout);
  204. GetTimerDebugInfo(str1, str2, &dwDiffTime, pte, llCurTime);
  205. Trace7(TIMER, "Removing timer <%d><%d><%d> Timeout:%lu <%s> <%s> "
  206. "Status:%d", dwBucket, pte->Id, pte->Id2, dwDiffTime, str1,
  207. str2, pte->Status);
  208. }
  209. #endif
  210. // remove the timer from the timer queue and decrement the number of timers
  211. RemoveEntryList(&pte->Link);
  212. ptg->NumTimers--;
  213. // reset the minimum timeout for the timer queue, if this timer was the min
  214. if (pte->Timeout==ptg->WTTimeout) {
  215. SetNextTime(llCurTime, ptg->TableLowIndex);
  216. }
  217. // reset the timer status to created
  218. pte->Status = TIMER_STATUS_CREATED;
  219. // print timer queue
  220. #if DEBUG_TIMER_REMOVETIMER2
  221. Trace0(TIMER1, "Printing Timer Queue after _RemoveTimer");
  222. DebugPrintTimerQueue();
  223. #endif
  224. Trace0(LEAVE1, "Leaving _RemoveTimer()");
  225. return;
  226. }
  227. //------------------------------------------------------------------------------
  228. // _SetNextTime
  229. // called when a timer==WTTimeout has been removed or fired,used to set the
  230. // next min time.
  231. //------------------------------------------------------------------------------
  232. VOID
  233. SetNextTime (
  234. LONGLONG llCurTime,
  235. DWORD dwLowIndex
  236. )
  237. {
  238. PIGMP_TIMER_GLOBAL ptg = &g_TimerStruct;
  239. PIGMP_TIMER_ENTRY pte, pteMin;
  240. LONGLONG ilMinTime;
  241. PLIST_ENTRY pHead, ple;
  242. DWORD Error = NO_ERROR;
  243. Trace0(ENTER1, "entering _SetNextTime()");
  244. //
  245. // if timer list empty, set lowIndex, and timer to infinite, and return.
  246. //
  247. if (ptg->NumTimers==0) {
  248. ptg->TableLowIndex = (DWORD)~0;
  249. SET_TIMER_INFINITE(ptg->WTTimeout);
  250. ptg->Status = TIMER_STATUS_INACTIVE;
  251. return;
  252. }
  253. //
  254. // find lowest table index having an entry
  255. //
  256. if (dwLowIndex>NUM_TIMER_BUCKETS-1)
  257. dwLowIndex = 0;
  258. for (; dwLowIndex<=NUM_TIMER_BUCKETS-1; dwLowIndex++) {
  259. if (IsListEmpty(&ptg->TimesTable[dwLowIndex]) )
  260. continue;
  261. else
  262. break;
  263. }
  264. ptg->TableLowIndex = dwLowIndex;
  265. //
  266. // find timer entry with the lowest time
  267. //
  268. if (dwLowIndex==0) {
  269. pteMin = CONTAINING_RECORD(ptg->TimesTable[0].Flink,
  270. IGMP_TIMER_ENTRY, Link);
  271. }
  272. else {
  273. // except bucket[0], other buckets are not sorted
  274. pHead = &ptg->TimesTable[dwLowIndex];
  275. ilMinTime = (((LONGLONG)0x7FFFFFF)<<32)+ ~0;
  276. for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
  277. pte = CONTAINING_RECORD(ptg->TimesTable[dwLowIndex].Flink,
  278. IGMP_TIMER_ENTRY, Link);
  279. if (pte->Timeout<ilMinTime) {
  280. ilMinTime = pte->Timeout;
  281. pteMin = pte;
  282. }
  283. }
  284. }
  285. //
  286. // update global time
  287. //
  288. if ((IS_TIMER_INFINITE(ptg->WTTimeout))
  289. || (pteMin->Timeout<=ptg->WTTimeout))
  290. {
  291. ptg->WTTimeout = pteMin->Timeout;
  292. if (!IS_TIMER_INFINITE(ptg->WTTimeout)) {
  293. BOOL bSuccess ;
  294. bSuccess = ChangeTimerQueueTimer(ptg->WTTimer, ptg->WTTimer1,
  295. (ULONG) ((ptg->WTTimeout - llCurTime)),
  296. 1000000 // set a periodic timer
  297. );
  298. if (!bSuccess) {
  299. Error = GetLastError();
  300. Trace1(ERR, "ChangeTimerQueueTimer returned error:%d in SetNextTime",
  301. Error);
  302. }
  303. else
  304. Trace1(TIMER1, "ChangeTimerQueueTimer set to %lu",
  305. (ULONG) ((ptg->WTTimeout - llCurTime))/1000);
  306. }
  307. ptg->Status = TIMER_STATUS_ACTIVE;
  308. }
  309. //
  310. // resynchronize timer list if required
  311. //
  312. if ( (ptg->TableLowIndex!=0)
  313. && (ptg->SyncTime + TIMER_BUCKET_GRANULARITY_ABS > llCurTime) ) {
  314. ResyncTimerBuckets(llCurTime);
  315. }
  316. Trace0(LEAVE1, "_SetNextTime()");
  317. return;
  318. } //end _SetNextTime
  319. //------------------------------------------------------------------------------
  320. // _InitializeIgmpTime
  321. // Initialize the igmp absolute timer
  322. //------------------------------------------------------------------------------
  323. VOID
  324. InitializeIgmpTime(
  325. )
  326. {
  327. g_TimerStruct.CurrentTime.HighPart = 0;
  328. g_TimerStruct.CurrentTime.LowPart = GetTickCount();
  329. return;
  330. }
  331. //------------------------------------------------------------------------------
  332. // _GetCurrentIgmpTimer
  333. // uses GetTickCount(). converts it into 64 bit absolute timer.
  334. //------------------------------------------------------------------------------
  335. LONGLONG
  336. GetCurrentIgmpTime(
  337. )
  338. {
  339. ULONG ulCurTimeLow = GetTickCount();
  340. //
  341. // see if timer has wrapped
  342. //
  343. // since multi-threaded, it might get preempted and CurrentTime
  344. // might get lower than the global variable g_TimerStruct.CurrentTime.LowPart
  345. // which might be set by another thread. So we also explicitly verify the
  346. // switch from a very large DWORD to a small one.
  347. // (code thanks to murlik&jamesg)
  348. //
  349. if ( (ulCurTimeLow < g_TimerStruct.CurrentTime.LowPart)
  350. && ((LONG)g_TimerStruct.CurrentTime.LowPart < 0)
  351. && ((LONG)ulCurTimeLow > 0) )
  352. {
  353. // use global CS instead of creating a new CS
  354. ACQUIRE_GLOBAL_LOCK("_GetCurrentIgmpTime");
  355. // make sure that the global timer has not been updated meanwhile
  356. if ( (LONG)g_TimerStruct.CurrentTime.LowPart < 0)
  357. {
  358. g_TimerStruct.CurrentTime.HighPart++;
  359. g_TimerStruct.CurrentTime.LowPart = ulCurTimeLow;
  360. }
  361. RELEASE_GLOBAL_LOCK("_GetCurrentIgmpTime");
  362. }
  363. g_TimerStruct.CurrentTime.LowPart = ulCurTimeLow;
  364. return g_TimerStruct.CurrentTime.QuadPart;
  365. }
  366. //------------------------------------------------------------------------------
  367. // _WF_ProcessTimerEvent
  368. //
  369. // Processes the timer queue, firing events and sets the next timer at the end.
  370. // Is queued by the Wait Server Thread.
  371. //
  372. // Locks: Acquires global timer lock before entering into the timer queue.
  373. //------------------------------------------------------------------------------
  374. VOID
  375. WF_ProcessTimerEvent (
  376. PVOID pContext
  377. )
  378. {
  379. PIGMP_TIMER_GLOBAL ptg = &g_TimerStruct;
  380. LONGLONG ilDiffTime, llCurTime = GetCurrentIgmpTime();
  381. DWORD Error = NO_ERROR;
  382. PLIST_ENTRY pHead, ple;
  383. PIGMP_TIMER_ENTRY pte;
  384. LONGLONG llFiredTimeout;
  385. if (!EnterIgmpWorker()) {return;}
  386. Trace0(ENTER1, "Entering _WF_ProcessTimerEvent");
  387. // acquire timer lock
  388. ACQUIRE_TIMER_LOCK("_WF_ProcessTimerEvent");
  389. // print the timer queue
  390. #if DEBUG_TIMER_PROCESSQUEUE1
  391. Trace0(TIMER1, "Printing Timer Queue before processing the timer queue");
  392. DebugPrintTimerQueue();
  393. #endif
  394. BEGIN_BREAKOUT_BLOCK1 {
  395. // I fire a timer if it is set to within + FORWARD_TIMER_FIRED from now
  396. llFiredTimeout = llCurTime + FORWARD_TIMER_FIRED;
  397. // if there are no timers, then I am done
  398. if (ptg->NumTimers<1) {
  399. Trace1(TIMER1, "Num timers%d less than 1 in _WF_ProcessTimerEvent",
  400. ptg->NumTimers);
  401. GOTO_END_BLOCK1;
  402. }
  403. //
  404. // find all the timers with lower timeouts and fire callbacks in my context
  405. //
  406. for ( ; ptg->TableLowIndex <= NUM_TIMER_BUCKETS-1; ptg->TableLowIndex++) {
  407. pHead = &ptg->TimesTable[ptg->TableLowIndex];
  408. for (ple=pHead->Flink; ple!=pHead; ) {
  409. pte = CONTAINING_RECORD(ple, IGMP_TIMER_ENTRY, Link);
  410. ple = ple->Flink;
  411. // this timer is fired
  412. if (pte->Timeout < llFiredTimeout) {
  413. RemoveEntryList(&pte->Link);
  414. pte->Status = TIMER_STATUS_FIRED;
  415. ptg->NumTimers --;
  416. //or should i queue to other worker threads
  417. (pte->Function)(pte->Context);
  418. }
  419. else {
  420. if (ptg->TableLowIndex==0) //only the 1st bucket is sorted
  421. break;
  422. }
  423. }
  424. // if any bucket is empty, then I am done, as I start with LowIndex
  425. if (!IsListEmpty(&ptg->TimesTable[ptg->TableLowIndex]))
  426. break;
  427. } //end for loop
  428. if ( (ptg->TableLowIndex!=0)
  429. && (ptg->SyncTime + TIMER_BUCKET_GRANULARITY_ABS < llCurTime) ) {
  430. ResyncTimerBuckets(llCurTime);
  431. }
  432. //
  433. // set the next lowest time
  434. //
  435. SET_TIMER_INFINITE(ptg->WTTimeout);
  436. SetNextTime(llCurTime, ptg->TableLowIndex);
  437. } END_BREAKOUT_BLOCK1;
  438. // print the timer queue
  439. #if DEBUG_TIMER_PROCESSQUEUE2
  440. Trace0(TIMER1, "Printing Timer Queue after processing the timer queue");
  441. DebugPrintTimerQueue();
  442. #endif
  443. RELEASE_TIMER_LOCK("_WF_ProcessTimerEvent");
  444. Trace0(LEAVE1, "Leaving _WF_ProcessTimerEvent()");
  445. LeaveIgmpWorker();
  446. return ;
  447. } //end _WF_ProcessTimerEvent
  448. //------------------------------------------------------------------------------
  449. // WT_ProcessTimerEvent
  450. //
  451. // Callback: fired when the timer set by this dll is timed out by the NtdllTimer
  452. //------------------------------------------------------------------------------
  453. VOID
  454. WT_ProcessTimerEvent (
  455. PVOID pContext,
  456. BOOLEAN Unused
  457. )
  458. {
  459. //enter/leaveIgmpApi not required as the timer queue is persistent
  460. Trace0(ENTER1, "Entering _WT_ProcessTimerEvent()");
  461. QueueIgmpWorker((LPTHREAD_START_ROUTINE)WF_ProcessTimerEvent, pContext);
  462. Trace0(LEAVE1, "Leaving _WT_ProcessTimerEvent()");
  463. return;
  464. }
  465. //------------------------------------------------------------------------------
  466. // _InsertTimerInSortedList
  467. // Used to insert a timer in the sorted bucket=0
  468. //------------------------------------------------------------------------------
  469. VOID
  470. InsertTimerInSortedList(
  471. PIGMP_TIMER_ENTRY pteNew,
  472. PLIST_ENTRY pHead
  473. )
  474. {
  475. PLIST_ENTRY ple;
  476. PIGMP_TIMER_ENTRY pte;
  477. LONGLONG llNewTime;
  478. llNewTime = pteNew->Timeout;
  479. for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
  480. pte = CONTAINING_RECORD(ple, IGMP_TIMER_ENTRY, Link);
  481. if (llNewTime<= pte->Timeout)
  482. break;
  483. }
  484. InsertTailList(ple, &pteNew->Link);
  485. return;
  486. }
  487. //------------------------------------------------------------------------------
  488. // _ResyncTimerBuckets
  489. //
  490. // Called during insert: when the 1st bucket is empty, and other buckets have
  491. // to be moved left
  492. //------------------------------------------------------------------------------
  493. VOID
  494. ResyncTimerBuckets(
  495. LONGLONG llCurTime
  496. )
  497. {
  498. PIGMP_TIMER_GLOBAL ptg = &g_TimerStruct;
  499. PLIST_ENTRY pHead, ple, pleCur;
  500. LIST_ENTRY le;
  501. PIGMP_TIMER_ENTRY pte;
  502. LONGLONG lastBucketTime;
  503. DWORD numShift, dwCount, dwBucket, i, j;
  504. TraceEnter("_ResyncTimerBuckets()");
  505. //
  506. // SyncTime should always be <= to currentTime
  507. //
  508. numShift = 0;
  509. while (ptg->SyncTime+TIMER_BUCKET_GRANULARITY_ABS <= llCurTime) {
  510. if (!IsListEmpty(&ptg->TimesTable[numShift]))
  511. break;
  512. ptg->SyncTime += TIMER_BUCKET_GRANULARITY_ABS;
  513. numShift++;
  514. }
  515. if (numShift==0)
  516. return;
  517. //
  518. // shift all buckets left, except for the last bucket and reinitialize the
  519. // list heads
  520. //
  521. for (i=0,j=numShift; i<NUM_TIMER_BUCKETS-1-numShift; i++,j++) {
  522. if (IsListEmpty(&ptg->TimesTable[j])) {
  523. ptg->TimesTable[j].Flink = ptg->TimesTable[j].Blink
  524. = &ptg->TimesTable[i];
  525. }
  526. else {
  527. ptg->TimesTable[j].Flink->Blink = &ptg->TimesTable[i];
  528. ptg->TimesTable[j].Blink->Flink = &ptg->TimesTable[i];
  529. }
  530. }
  531. MoveMemory( (PVOID)&(ptg->TimesTable[0]),
  532. (VOID *)&(ptg->TimesTable[numShift]),
  533. (sizeof(LIST_ENTRY) * (NUM_TIMER_BUCKETS-1-numShift))
  534. );
  535. for (dwCount=1; dwCount<=numShift; dwCount++)
  536. InitializeListHead(&ptg->TimesTable[NUM_TIMER_BUCKETS-1-dwCount]);
  537. //
  538. // go through the last bucket and redistribute it
  539. //
  540. lastBucketTime = ptg->SyncTime
  541. + (TIMER_BUCKET_GRANULARITY_ABS*(NUM_TIMER_BUCKETS-1));
  542. pHead = &ptg->TimesTable[NUM_TIMER_BUCKETS-1];
  543. for (ple=pHead->Flink; ple!=pHead; ) {
  544. pte = CONTAINING_RECORD(ple, IGMP_TIMER_ENTRY, Link);
  545. pleCur = ple;
  546. ple = ple->Flink;
  547. if (pte->Timeout<lastBucketTime) {
  548. RemoveEntryList(pleCur);
  549. MAP_TO_BUCKET(dwBucket, pte->Timeout);
  550. if (dwBucket==0) {
  551. InsertTimerInSortedList(pte, &ptg->TimesTable[0]);
  552. }
  553. else {
  554. InsertTailList(&ptg->TimesTable[dwBucket], pleCur);
  555. }
  556. }
  557. }
  558. //
  559. // sort the times in the first bucket
  560. //
  561. InitializeListHead(&le);
  562. InsertHeadList(&ptg->TimesTable[0], &le);
  563. RemoveEntryList(&ptg->TimesTable[0]);
  564. InitializeListHead(&ptg->TimesTable[0]);
  565. for (ple=le.Flink; ple!=&le; ) {
  566. pte = CONTAINING_RECORD(ple, IGMP_TIMER_ENTRY, Link);
  567. RemoveEntryList(ple);
  568. ple = ple->Flink;
  569. InsertTimerInSortedList(pte, &ptg->TimesTable[0]);
  570. }
  571. //
  572. // set the TableLowIndex
  573. //
  574. if (ptg->TableLowIndex>=NUM_TIMER_BUCKETS-1) {
  575. for (ptg->TableLowIndex=0; ptg->TableLowIndex<=NUM_TIMER_BUCKETS-1;
  576. ptg->TableLowIndex++)
  577. {
  578. if (IsListEmpty(&ptg->TimesTable[ptg->TableLowIndex]) )
  579. continue;
  580. else
  581. break;
  582. }
  583. }
  584. else
  585. ptg->TableLowIndex -= numShift;
  586. #if DEBUG_TIMER_RESYNCTIMER
  587. Trace0(TIMER1, "Printing Timer Queue after _ResyncTimerBuckets");
  588. DebugPrintTimerQueue();
  589. #endif
  590. Trace0(LEAVE1, "_ResyncTimerBuckets()");
  591. return;
  592. } //end _ResyncTimerBuckets
  593. //------------------------------------------------------------------------------
  594. // _InitializeTimerGlobal
  595. //
  596. // create the timer CS and WaitTimer. registers a queue and timer with NtdllTimer.
  597. //
  598. // Called by: _StartProtocol()
  599. // Locks: no locks taken here.
  600. //------------------------------------------------------------------------------
  601. DWORD
  602. InitializeTimerGlobal (
  603. )
  604. {
  605. DWORD Error = NO_ERROR, i;
  606. PIGMP_TIMER_GLOBAL ptg = &g_TimerStruct;
  607. BOOL bErr;
  608. LONGLONG llCurTime = GetTickCount();
  609. Trace0(ENTER1, "Entering _InitializeTimerGlobal()");
  610. bErr = TRUE;
  611. BEGIN_BREAKOUT_BLOCK1 {
  612. // initialize igmp timer used to get tick count
  613. InitializeIgmpTime();
  614. //
  615. // initialize timer critical section
  616. //
  617. try {
  618. InitializeCriticalSection(&ptg->CS);
  619. }
  620. except (EXCEPTION_EXECUTE_HANDLER) {
  621. Error = GetExceptionCode();
  622. Trace1(
  623. ANY, "exception %d initializing global timer critical section",
  624. Error
  625. );
  626. Logerr0(INIT_CRITSEC_FAILED, Error);
  627. GOTO_END_BLOCK1;
  628. }
  629. // create WaitTimer for igmp
  630. ptg->WTTimer = CreateTimerQueue();
  631. if ( ! ptg->WTTimer) {
  632. Error = GetLastError();
  633. Trace1(ERR, "CreateTimerQueue() failed:%d", Error);
  634. GOTO_END_BLOCK1;
  635. }
  636. //
  637. // create a periodic timer which does not get deletd
  638. //
  639. if (! CreateTimerQueueTimer(
  640. &ptg->WTTimer1,
  641. ptg->WTTimer, WT_ProcessTimerEvent,
  642. NULL, //context
  643. 1000000,
  644. 1000000,
  645. 0
  646. ))
  647. {
  648. Error = GetLastError();
  649. Trace1(ERR, "CreateTimerQueue() failed:%d", Error);
  650. GOTO_END_BLOCK1;
  651. }
  652. // set initial timeout to infinite, and SyncTime to the current time
  653. SET_TIMER_INFINITE(ptg->WTTimeout);
  654. ptg->SyncTime = llCurTime;
  655. ptg->CurrentTime.QuadPart = llCurTime;
  656. ptg->NumTimers = 0;
  657. // initialize the timer buckets
  658. for (i=0; i<NUM_TIMER_BUCKETS; i++) {
  659. InitializeListHead(&ptg->TimesTable[i]);
  660. }
  661. // set the TableLowIndex
  662. ptg->TableLowIndex = (DWORD)~0;
  663. // set the status of the global timer
  664. ptg->Status = TIMER_STATUS_CREATED;
  665. bErr = FALSE;
  666. } END_BREAKOUT_BLOCK1;
  667. if (bErr) {
  668. DeInitializeTimerGlobal();
  669. Trace0(LEAVE1, "Leaving. Could not _InitializeTimerGlobal():");
  670. return ERROR_CAN_NOT_COMPLETE;
  671. }
  672. else {
  673. Trace0(LEAVE1, "Leaving _InitializeTimerGlobal()");
  674. return NO_ERROR;
  675. }
  676. } //end _InitializeTimerGlobal
  677. //------------------------------------------------------------------------------
  678. // _DeInitializeTimerGlobal
  679. //
  680. // deinitializes the timer CS, and deletes the timer queue with Rtl
  681. //------------------------------------------------------------------------------
  682. VOID
  683. DeInitializeTimerGlobal (
  684. )
  685. {
  686. DeleteCriticalSection(&g_TimerStruct.CS);
  687. DeleteTimerQueueEx(g_TimerStruct.WTTimer, NULL);
  688. return;
  689. } //end _DeInitializeTimerGlobal
  690. //------------------------------------------------------------------------------
  691. // _DebugPrintTimerEntry
  692. //
  693. // Assumes DEBUG_TIMER_TIMERID is true
  694. //------------------------------------------------------------------------------
  695. VOID
  696. DebugPrintTimerEntry (
  697. PIGMP_TIMER_ENTRY pte,
  698. DWORD dwBucket,
  699. LONGLONG llCurTime
  700. )
  701. {
  702. DWORD dwDiffTime;
  703. CHAR str1[20], str2[20];
  704. #if DEBUG_TIMER_TIMERID
  705. if (dwBucket==(DWORD)~0) {
  706. MAP_TO_BUCKET(dwBucket, pte->Timeout);
  707. }
  708. GetTimerDebugInfo(str1, str2, &dwDiffTime, pte, llCurTime);
  709. if (pte->Timeout - llCurTime > 0) {
  710. Trace7(TIMER, "---- <%2d><%d><%d> Timeout:%lu <%s> <%s> Status:%d",
  711. dwBucket, pte->Id, pte->Id2, dwDiffTime, str1, str2, pte->Status);
  712. }
  713. else {
  714. Trace7(TIMER, "---- <%d><%d><%d> Timeout:--%lu <%s> <%s> Status:%d",
  715. dwBucket, pte->Id, pte->Id2, dwDiffTime, str1, str2, pte->Status);
  716. }
  717. #endif //#if DEBUG_TIMER_TIMERID
  718. return;
  719. }
  720. //------------------------------------------------------------------------------
  721. // _GetTimerDebugInfo
  722. //
  723. // returns info regarding what type of timer it is
  724. //------------------------------------------------------------------------------
  725. VOID
  726. GetTimerDebugInfo(
  727. CHAR str1[20],
  728. CHAR str2[20],
  729. DWORD *pdwDiffTime,
  730. PIGMP_TIMER_ENTRY pte,
  731. LONGLONG llCurTime
  732. )
  733. {
  734. LONGLONG diffTime;
  735. #if DEBUG_TIMER_TIMERID
  736. diffTime = (pte->Timeout - llCurTime > 0)
  737. ? pte->Timeout - llCurTime
  738. : llCurTime - pte->Timeout;
  739. diffTime /= (LONGLONG)1000; //in seconds
  740. *pdwDiffTime = (DWORD)diffTime;
  741. strcpy(str2, " ");
  742. switch (pte->Id) {
  743. case 110:
  744. case 120: strcpy(str1, "iGenQuery "); break;
  745. case 210:
  746. case 220: strcpy(str1, "iOtherQry "); break;
  747. case 211: strcpy(str1, "iOtherQry* "); break;
  748. case 331:
  749. case 321: strcpy(str1, "gMemTimer* "); lstrcpy(str2, INET_NTOA(pte->Group)); break;
  750. case 300:
  751. case 320:
  752. case 330:
  753. case 340: strcpy(str1, "gMemTimer "); lstrcpy(str2, INET_NTOA(pte->Group)); break;
  754. case 400:
  755. case 410:
  756. case 420: strcpy(str1, "gGrpSpQry "); lstrcpy(str2, INET_NTOA(pte->Group)); break;
  757. case 510:
  758. case 520: strcpy(str1, "gLstV1Rpt "); lstrcpy(str2, INET_NTOA(pte->Group)); break;
  759. case 511: strcpy(str1, "gLstV1Rpt* "); lstrcpy(str2, INET_NTOA(pte->Group)); break;
  760. case 720:
  761. case 740: strcpy(str1, "iV1Router "); break;
  762. case 741: strcpy(str1, "iV1Router* "); break;
  763. case 920:
  764. case 910: strcpy(str1, "_MibTimer "); break;
  765. default: strcpy(str1, "???? "); break;
  766. }
  767. #endif //DEBUG_TIMER_TIMERID
  768. return;
  769. }
  770. //------------------------------------------------------------------------------
  771. // _DebugPrintTimerQueue
  772. // takes the timer lock
  773. //------------------------------------------------------------------------------
  774. VOID
  775. APIENTRY
  776. DebugPrintTimerQueue (
  777. )
  778. {
  779. PIGMP_TIMER_GLOBAL ptg = &g_TimerStruct;
  780. PIGMP_TIMER_ENTRY pte;
  781. PLIST_ENTRY pHead, ple;
  782. LONGLONG llCurTime = GetCurrentIgmpTime();
  783. DWORD Error=NO_ERROR, i, count;
  784. if (g_Info.CurrentGroupMemberships > 40)
  785. return;
  786. #if DEBUG_TIMER_TIMERID
  787. ENTER_CRITICAL_SECTION(&g_CS, "g_CS", "_DebugPrintTimerQueue");
  788. if (g_RunningStatus != IGMP_STATUS_RUNNING) {
  789. Error = ERROR_CAN_NOT_COMPLETE;
  790. }
  791. else {
  792. ++g_ActivityCount;
  793. }
  794. LEAVE_CRITICAL_SECTION(&g_CS, "g_CS", "_DebugPrintTimerQueue");
  795. if (Error!=NO_ERROR)
  796. return;
  797. if (!EnterIgmpWorker()) {return;}
  798. ACQUIRE_TIMER_LOCK("_DebugPrintTimerQueue");
  799. if (g_TimerStruct.NumTimers==0) {
  800. Trace0(TIMER, "No timers present in the timer queue");
  801. }
  802. else {
  803. Trace0(TIMER, "---------------------LOCAL-TIMER-QUEUE-------------------------");
  804. Trace4(TIMER, "-- WTTimeout<%d:%lu> SyncTime<%d:%lu>",
  805. TIMER_HIGH(ptg->WTTimeout), TIMER_LOW(ptg->WTTimeout),
  806. TIMER_HIGH(ptg->SyncTime), TIMER_LOW(ptg->SyncTime) );
  807. Trace3(TIMER, "-- NumTimers:<%d> TableLowIndex:<%lu> Status:<%d>",
  808. ptg->NumTimers, ptg->TableLowIndex, ptg->Status);
  809. Trace0(TIMER, "---------------------------------------------------------------");
  810. count =0;
  811. for (i=0; i<NUM_TIMER_BUCKETS; i++) {
  812. pHead = &ptg->TimesTable[i];
  813. if (IsListEmpty(pHead))
  814. continue;
  815. else {
  816. for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
  817. pte = CONTAINING_RECORD(ple, IGMP_TIMER_ENTRY, Link);
  818. DebugPrintTimerEntry(pte, i, llCurTime);
  819. count ++;
  820. }
  821. }
  822. }
  823. Trace0(TIMER, "---------------------------------------------------------------");
  824. }
  825. RELEASE_TIMER_LOCK("_DebugPrintTimerQueue");
  826. LeaveIgmpWorker();
  827. #endif //DEBUG_TIMER_TIMERID
  828. return;
  829. }