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.

541 lines
15 KiB

  1. /*
  2. * for the scheduling of events.
  3. *
  4. * The interface(s) provided are:
  5. * 1. Provide an event to the TimerManager that should be sent back to an
  6. * UpdateableObject when the specified time elapses.
  7. *
  8. * REVISIONS:
  9. * pcy30Nov92: list.h added; Insert, Find, and Remove reworked for new list.
  10. * jod07Dec92: Added Wait to the Timerman.h
  11. * pcy11Dec92: Added global _theTimerManager
  12. * ane16Dec92: Added default constructors for DateObj, TimeObj, and DateTimeObj
  13. * ane23Dec92: Minor changes to DateObj, TimeObj, and WeekObj classes
  14. * ane11Jan93: Changed theTimerList to a sortable list
  15. * ane10Feb93: Added RemoveTimer which takes an EventTimer, also added copy
  16. * of event object when it is added to the timer list
  17. * pcy16Feb93: Cleanup
  18. * jod05Apr93: Added changes for Deep Discharge
  19. * cad02Jul93: Fixed compiler warnings that might be legit
  20. * cad16Sep93: Added -,< operators, new GetDaysInMonth that take month/year
  21. * cad12Oct93: Fixed time and week contstructors, added ==
  22. * pcy04Mar94: Get time until event from a timer
  23. * jps14Jul94: added (int)s; made timer_id s ULONG (os2 1.x)
  24. * ajr02May95: Need to stop carrying time in milliseconds
  25. * ajr29Jul95: More of the above...
  26. * jps24Oct96: fix CalculateMonthlyTime to return time THIS month if not that
  27. * time yet.
  28. * tjg28Jan98: Ensured that all method return paths will release theAccessLock
  29. * tjg02Feb98: Delete the theAccessLock in the destructor
  30. * dma10Sep98: Fixed CalculateMonthlyTime to handle year rollover.
  31. * mholly03Dec98 : removed theAccessLock and instead use the Access/Release
  32. * methods already available in theTimerList. Made sure that
  33. * all accesses to the list are protected correctly, and that
  34. * the list is made available before calling out to other
  35. * objects via ExecuteTimer(). If the list is not made
  36. * available then deadlocks will occur.
  37. *
  38. * v-stebe 29Jul2000 Fixed PREfix errors (bugs #46358, #46366, #112603)
  39. */
  40. #define INCL_BASE
  41. #define INCL_DOS
  42. #define INCL_NOPM
  43. #include "cdefine.h"
  44. extern "C" {
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <time.h>
  48. }
  49. #include "apc.h"
  50. #include "_defs.h"
  51. #include "err.h"
  52. #include "slist.h"
  53. #include "codes.h"
  54. #include "timerman.h"
  55. #include "eventime.h"
  56. #include "cfgmgr.h"
  57. #include "utils.h"
  58. #include "datetime.h"
  59. PTimerManager _theTimerManager=NULL;
  60. /*
  61. Name :TimerManager
  62. Synop :Constructor. INitializes the list.
  63. */
  64. TimerManager::TimerManager(PMainApplication anApplication)
  65. : theTimerList(new ProtectedSortedList()),
  66. theApplication(anApplication)
  67. {
  68. _theTimerManager = this;
  69. }
  70. /*
  71. Name :~TimerManager
  72. Synop :Destroys the internal list.
  73. */
  74. TimerManager::~TimerManager()
  75. {
  76. if (theTimerList) {
  77. theTimerList->FlushAll();
  78. delete theTimerList;
  79. theTimerList = NULL;
  80. }
  81. }
  82. INT TimerManager::Update(PEvent anEvent)
  83. {
  84. return UpdateObj::Update(anEvent);
  85. }
  86. /*
  87. C+
  88. Name :CancelTimer
  89. Synop :Cancels the timer. Please pass a good TimerID.
  90. At this level we just remove it from the list.
  91. */
  92. VOID TimerManager::CancelTimer(ULONG TimerID)
  93. //c-
  94. {
  95. RemoveEventTimer(TimerID);
  96. }
  97. /*
  98. C+
  99. Name :SetTimer
  100. Synop :Use this method to set a timer.
  101. SecondDelay : The delay in seconds
  102. anEvent : The event to use during the update.
  103. anUpdateObj : The object to update with the above event.
  104. RETURNS : Returns the TimerID of the EventTimer
  105. that is associated with the timer.
  106. anUpdateObj will be equal to 'this' if you want the timer
  107. to update YOU. anEvent should be dynamically allocated 'new'
  108. and the timer will delete it when you invoke CancelTimer.
  109. */
  110. ULONG TimerManager::SetTheTimer(ULONG SecondsDelay, PEvent anEvent,
  111. PUpdateObj anUpdateObj)
  112. {
  113. INT timer_id = 0;
  114. // You must have anEvent and anUpdateObj
  115. if(anEvent && anUpdateObj) {
  116. PEvent newEvent = new Event (*anEvent);
  117. if (newEvent) {
  118. ULONG sec_time = SecondsDelay;
  119. PEventTimer anEventTimer = new EventTimer(newEvent, anUpdateObj, sec_time);
  120. if (anEventTimer) {
  121. timer_id = anEventTimer->GetTimerID();
  122. newEvent->SetAttributeValue(TIMER_ID, timer_id);
  123. InsertEventTimer(anEventTimer);
  124. }
  125. }
  126. }
  127. return timer_id;
  128. }
  129. /*
  130. C+
  131. Name :SetTimer
  132. Synop :Use this method to set a timer.
  133. DateTime : A DateTimeObj which is not deallocated by the system
  134. This DateTimeObj will contain an absolute date value.
  135. anEvent : The event to use during the update.
  136. anUpdateObj : The object to update with the above event.
  137. RETURNS : Returns the TimerID of the EventTimer
  138. that is associated with the timer.
  139. anUpdateObj will be equal to 'this' if you want the timer
  140. to update YOU. anEvent should be dynamically allocated 'new'
  141. and the timer will delete it when you invoke CancelTimer.
  142. */
  143. ULONG TimerManager::SetTheTimer(PDateTimeObj aDateTime,
  144. PEvent anEvent,PUpdateObj anUpdateObj)
  145. {
  146. INT timer_id = 0;
  147. // Must have anEvent, anUpdateObj and aDateTime
  148. if(anEvent && anUpdateObj && aDateTime) {
  149. ULONG sec = (ULONG)(aDateTime->GetSeconds());
  150. PEvent newEvent = new Event (*anEvent);
  151. PEventTimer anEventTimer = new EventTimer(newEvent, anUpdateObj, sec);
  152. if (anEventTimer) {
  153. timer_id = anEventTimer->GetTimerID();
  154. InsertEventTimer(anEventTimer);
  155. }
  156. }
  157. return timer_id;
  158. }
  159. /*
  160. Name :InsertEventTimer
  161. Synop :Inserts the Eventtimer inside the internal linked list.
  162. The EventTimer has a unique TimerID which is used to
  163. retrieve the node inthe list.
  164. */
  165. VOID TimerManager::InsertEventTimer(PEventTimer anEventTimer)
  166. {
  167. // Use the add function which should insert in timer order
  168. theTimerList->Add((RApcSortable)*anEventTimer);
  169. }
  170. /*
  171. Name :FindEventTimer
  172. Synop :Finds the Event Timer in the internal linked list.
  173. */
  174. PEventTimer TimerManager::FindEventTimer(ULONG aTimerId)
  175. {
  176. PEventTimer return_timer = NULL;
  177. // Lock access to the list
  178. theTimerList->Access();
  179. // Iterate through the list
  180. ListIterator iter(*theTimerList);
  181. PEventTimer test_node = (PEventTimer)theTimerList->GetHead();
  182. BOOL found = FALSE;
  183. while (test_node && !found) {
  184. if (test_node->GetTimerID() == aTimerId) {
  185. return_timer = test_node;
  186. found = TRUE;
  187. }
  188. test_node = (PEventTimer)iter.Next();
  189. }
  190. theTimerList->Release();
  191. return return_timer;
  192. }
  193. /*
  194. Name :RemoveEventTimer
  195. Synop :Removes the EventTimer object from the internval linked list.
  196. */
  197. VOID TimerManager::RemoveEventTimer(ULONG aTimerId)
  198. {
  199. theTimerList->Access();
  200. PEventTimer aTimer = FindEventTimer(aTimerId);
  201. if (aTimer) {
  202. theTimerList->Detach(*aTimer);
  203. delete aTimer;
  204. aTimer = NULL;
  205. }
  206. theTimerList->Release();
  207. }
  208. ULONG TimerManager::GetTimeEventOccurs(ULONG aTimerID)
  209. {
  210. ULONG return_value = 0;
  211. theTimerList->Access();
  212. PEventTimer timer = FindEventTimer(aTimerID);
  213. if (timer) {
  214. return_value = timer->GetTime();
  215. }
  216. theTimerList->Release();
  217. return return_value;
  218. }
  219. /*
  220. Name :RemoveEventTimer
  221. Synop :Removes the EventTimer object from the internval linked list.
  222. */
  223. VOID TimerManager::RemoveEventTimer(PEventTimer anEventTimer)
  224. {
  225. if (theTimerList && anEventTimer) {
  226. theTimerList->Detach(*anEventTimer);
  227. delete anEventTimer;
  228. anEventTimer = NULL;
  229. }
  230. }
  231. /*
  232. Name :NotifyClient
  233. Synop :Given anEventTimer object, will notify the client that a timer
  234. has fired.
  235. */
  236. VOID TimerManager::NotifyClient(PEventTimer anEventTimer)
  237. {
  238. if (anEventTimer) {
  239. anEventTimer->Execute();
  240. }
  241. }
  242. /*****************************************************************************
  243. ****************************************************************************/
  244. PDateTimeObj TimerManager::CalculateDailyTime (RTimeObj aTime, ULONG threshold)
  245. {
  246. DateObj currDate;
  247. TimeObj currTime;
  248. ULONG newDay = currDate.GetDay();
  249. ULONG newMonth = currDate.GetMonth();
  250. ULONG newYear = currDate.GetYear();
  251. // If the calculated time is earlier than the current time, then
  252. // adjust the day forward by one (and possibly the month and year)
  253. if ((currTime.GetHour() > aTime.GetHour()) ||
  254. ((currTime.GetHour() == aTime.GetHour()) &&
  255. (currTime.GetMinutes() > aTime.GetMinutes())) ||
  256. ((currTime.GetHour() == aTime.GetHour()) &&
  257. (currTime.GetMinutes() == aTime.GetMinutes()) &&
  258. (currTime.GetSeconds() > aTime.GetSeconds())))
  259. {
  260. newDay++;
  261. CalculateNextDay (currDate.GetDaysInMonth(),
  262. currDate.GetMonth(),
  263. newDay, newMonth, newYear);
  264. }
  265. // Take the year, month, day from currDate and the hour,min,and sec
  266. // from specified time
  267. PDateTimeObj retTime = new DateTimeObj (newMonth,
  268. newDay,
  269. newYear,
  270. aTime.GetHour(),
  271. aTime.GetMinutes(),
  272. aTime.GetSeconds());
  273. // If the time until the time to be returned is less than the specified
  274. // threshold, then make it the next day
  275. //if (retTime->GetMilliseconds() < (LONG)threshold)
  276. if (retTime->GetSeconds() < (LONG)threshold)
  277. {
  278. newDay++;
  279. CalculateNextDay (currDate.GetDaysInMonth(),
  280. currDate.GetMonth(),
  281. newDay, newMonth, newYear);
  282. delete retTime;
  283. retTime = NULL;
  284. // Take the year, month, day from currDate and the hour,min,and sec
  285. // from specified time
  286. PDateTimeObj retTime = new DateTimeObj (newMonth,
  287. newDay,
  288. newYear,
  289. aTime.GetHour(),
  290. aTime.GetMinutes(),
  291. aTime.GetSeconds());
  292. }
  293. return retTime;
  294. }
  295. PDateTimeObj TimerManager::CalculateMonthlyTime (PCHAR aDay, PCHAR aTime)
  296. {
  297. time_t now_time = time(NULL);
  298. struct tm *now = localtime(&now_time);
  299. INT day_number = UtilDayToDayOfWeek(aDay);
  300. if(now->tm_wday < day_number) {
  301. now->tm_wday = day_number;
  302. }
  303. else {
  304. now->tm_mon += 1;
  305. if(now->tm_mon > 11) {
  306. now->tm_mon = 0;
  307. now->tm_year += 1;
  308. }
  309. }
  310. now->tm_mday = 0;
  311. mktime(now);
  312. INT hour, minute, second;
  313. hour = minute = second = 0;
  314. if (sscanf(aTime, "%d:%d:%d", &hour, &minute, &second) == EOF) {
  315. // An error parsing the time occured
  316. return NULL;
  317. }
  318. PDateTimeObj monthly_time = new DateTimeObj(now->tm_mon,
  319. now->tm_mday,
  320. now->tm_year,
  321. hour,
  322. minute,
  323. second);
  324. return monthly_time;
  325. }
  326. /*****************************************************************************
  327. ****************************************************************************/
  328. PDateTimeObj TimerManager::CalculateMonthlyTime (RWeekObj aDay, RTimeObj aTime)
  329. {
  330. INT done = FALSE;
  331. DateObj today;
  332. PDateTimeObj weekday = CalculateWeeklyTime (aDay, aTime);
  333. PDateTimeObj retTime = (PDateTimeObj)NULL;
  334. if (weekday != NULL) {
  335. if ( weekday->GetDate()->GetMonth() - 1 == (today.GetMonth()) % 12 ) {
  336. return weekday;
  337. }
  338. ULONG newDay = weekday->GetDate()->GetDay();
  339. ULONG newMonth = weekday->GetDate()->GetMonth();
  340. ULONG newYear = weekday->GetDate()->GetYear();
  341. if (newDay <= 7 && newMonth == today.GetMonth()) {
  342. return weekday;
  343. }
  344. while (!done)
  345. {
  346. newDay += 7;
  347. CalculateNextDay(weekday->GetDate()->GetDaysInMonth(),
  348. weekday->GetDate()->GetMonth(),
  349. newDay, newMonth, newYear);
  350. if (newMonth != weekday->GetDate()->GetMonth())
  351. {
  352. retTime = new DateTimeObj (newMonth, newDay, newYear,
  353. aTime.GetHour(),
  354. aTime.GetMinutes(),
  355. aTime.GetSeconds());
  356. done = TRUE;
  357. }
  358. }
  359. delete weekday;
  360. weekday = NULL;
  361. }
  362. return retTime;
  363. }
  364. /*****************************************************************************
  365. ****************************************************************************/
  366. PDateTimeObj TimerManager::CalculateWeeklyTime (RWeekObj aDay, RTimeObj aTime)
  367. {
  368. WeekObj currWeek;
  369. DateObj currDate;
  370. TimeObj currTime;
  371. ULONG newDay = currDate.GetDay();
  372. ULONG newMonth = currDate.GetMonth();
  373. ULONG newYear = currDate.GetYear();
  374. // Determine which day the shutdown should occur on
  375. if (currWeek.GetWeekDay() < aDay.GetWeekDay())
  376. newDay = currDate.GetDay() +
  377. aDay.GetWeekDay() - currWeek.GetWeekDay();
  378. else if (currWeek.GetWeekDay() > aDay.GetWeekDay())
  379. newDay = currDate.GetDay() + 7 -
  380. (currWeek.GetWeekDay() - aDay.GetWeekDay());
  381. // The day to schedule for is the same as the current day, so check to
  382. // see if the time is before the current time
  383. else if ((currTime.GetHour() > aTime.GetHour()) ||
  384. ((currTime.GetHour() == aTime.GetHour()) &&
  385. (currTime.GetMinutes() > aTime.GetMinutes())) ||
  386. ((currTime.GetHour() == aTime.GetHour()) &&
  387. (currTime.GetMinutes() == aTime.GetMinutes()) &&
  388. (currTime.GetSeconds() > aTime.GetSeconds())))
  389. // If the time to schedule for is before the current time, then
  390. // schedule for next week
  391. newDay += 7;
  392. // If the calculated day is more than there are days in the month,
  393. // then wrap into the next month
  394. CalculateNextDay (currDate.GetDaysInMonth(),
  395. currDate.GetMonth(),
  396. newDay, newMonth, newYear);
  397. PDateTimeObj retTime = new DateTimeObj (newMonth,
  398. newDay,
  399. newYear,
  400. aTime.GetHour(),
  401. aTime.GetMinutes(),
  402. aTime.GetSeconds());
  403. return retTime;
  404. }
  405. /*****************************************************************************
  406. ****************************************************************************/
  407. VOID TimerManager::CalculateNextDay(ULONG daysInMonth,
  408. ULONG theMonth,
  409. ULONG &newDay,
  410. ULONG &newMonth,
  411. ULONG &newYear)
  412. {
  413. if (newDay > daysInMonth) {
  414. newDay %= daysInMonth;
  415. if (theMonth == 12) {
  416. newMonth = 1;
  417. newYear++;
  418. }
  419. else {
  420. newMonth++;
  421. }
  422. }
  423. }
  424. BOOL TimerManager::ExecuteTimer()
  425. {
  426. BOOL timer_executed = FALSE;
  427. ULONG_PTR now = time(0);
  428. //
  429. // get access to the list
  430. //
  431. theTimerList->Access();
  432. PEventTimer timer = (PEventTimer)(theTimerList->GetHead());
  433. if (timer && (timer->GetTime() <= now)) {
  434. //
  435. // detach the eventTimer before firing it
  436. //
  437. theTimerList->Detach(*timer);
  438. //
  439. // now that the eventTimer is detached
  440. // it is safe (and required) that the
  441. // list be released
  442. //
  443. theTimerList->Release();
  444. NotifyClient(timer);
  445. delete timer;
  446. timer_executed = TRUE;
  447. }
  448. else {
  449. theTimerList->Release();
  450. }
  451. return timer_executed;
  452. }