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.

431 lines
16 KiB

  1. Given the state of the Win32_CurrentEvent class:
  2. class Win32_CurrentTime
  3. {
  4. uint32 Year;
  5. uint32 Month;
  6. uint32 Day;
  7. uint32 DayOfWeek;
  8. uint32 WeekInMonth;
  9. uint32 Quarter;
  10. uint32 Hour;
  11. uint32 Minute;
  12. uint32 Second;
  13. [key] sint32 UTCOffset;
  14. };
  15. Currently we can form queries for a specific point in time
  16. in the future. The basic form of a query is:
  17. select * from __InstanceModificationEvent
  18. where TargetInstance isa "Win32_CurrentTime"
  19. and TargetInstance.Year=1999
  20. and TargetInstance.Month=10
  21. and TargetInstance.Day=4
  22. and TargetInstance.Hour=11
  23. and TargetInstance.Minute=0
  24. and TargetInstance.Second=0
  25. This query specifies a single point in time, irrespective
  26. of the time at which it is to be evaluated, at which an
  27. event should be generated. If the time specified is in
  28. the past at the time it is evaluated, then no event will
  29. occur.
  30. In order to be a valid, a query must have each of the following
  31. properties:
  32. 1. A query must be specific enough to define the complete set
  33. of all times for which an event is to be generated.
  34. 2. Each time the expression is evaluated, it must be possible
  35. to specify unambiguously the closest point after the current
  36. time when the next event will occur.
  37. f(<WQL expression>, <current time>) -> <next event time>
  38. 3. The frequency of events must not be such that it may overwhelm
  39. the system under a normal load.
  40. The following, I believe, are the four most likely scenarios
  41. for which a user will ask for time events:
  42. 1. They want timer events to be generated indefinitely at
  43. a specified periodic interval.
  44. 2. They want a single event to be generated at some specified
  45. time in the future. (alarm functionality)
  46. 3. They want a single event to be generated at some time
  47. in the future relative to the time at which the query is
  48. made. (timer functionality)
  49. 4. They want one or more events to be generated based on some
  50. algebraic criteria.
  51. A single query may embody one or more of the above scenarios.
  52. An example of the first scenario could be the following:
  53. select * from __InstanceModificationEvent
  54. where TargetInstance isa "Win32_CurrentTime"
  55. and TargetInstance.Year=1999
  56. and TargetInstance.Month=10
  57. and TargetInstance.Day=4
  58. and TargetInstance.Hour=11
  59. and TargetInstance.Second % 30 = 0
  60. This specifies that we want an event every thirty seconds
  61. on the minute and half minute boundaries for the eleventh
  62. hour on the date 10/4/99. With the current approach we
  63. cannot completely capture the functionality of the first
  64. scenario. For example, we could not specify a timer to
  65. generate an event exactly seven seconds apart. If, however,
  66. we add a field to Win32_CurrentTime that is the equivalent
  67. file time (64 bit number) rounded to seconds that represents
  68. the same time as the system time fields, it then becomes
  69. possible:
  70. select * from __InstanceModificationEvent
  71. where TargetInstance isa "Win32_CurrentTime"
  72. and TargetInstance.Year=1999
  73. and TargetInstance.Month=10
  74. and TargetInstance.Day=4
  75. and TargetInstance.Hour=11
  76. and TargetInstance.TimeInSeconds % 30 = 0
  77. This is equivalent to the above statement but uses the total
  78. time in seconds instead of just seconds. By changing '30' to
  79. '7' we get an event every seven seconds. Note that by omitting
  80. a field we are signifying that it is a wildcard.
  81. For this type of query we will introduce a reserved symbol that
  82. evaluates to the current time when the expression is evaluated.
  83. There is a reserved symbol for each field in Win32_CurrentTime.
  84. Thus, to say "generate an event 45 seconds from now" we could
  85. write:
  86. select * from __InstanceModificationEvent
  87. where TargetInstance isa "Win32_CurrentTime"
  88. and TargetInstance.TimeInSeconds = %CurrentTimeInSeconds% + 45
  89. Fundamentally, WQL statements are patterns, not program
  90. expressions that can save state as part of their evaluation. It
  91. is valid to have algebraic expressions within WQL as long as they
  92. ultimately evaluate to a constant and there is no concept of
  93. assignment. The symbol %CurrentTimeInSeconds% is actually a
  94. macro that is replaced by the parser with the current time in
  95. seconds when it is encountered. Thus, the above statement will
  96. only generate one event and not an event every 45 seconds.
  97. Each of the examples above are by definition examples of time
  98. specification based on algebraic criteria. Here is an example
  99. that does not fall into any of the first three categories:
  100. select * from __InstanceModificationEvent
  101. where TargetInstance isa "Win32_CurrentTime"
  102. and TargetInstance.Year=1999
  103. and TargetInstance.Month=10
  104. and TargetInstance.Day=10
  105. and TargetInstance.Hour <= 12
  106. and TargetInstance.Minute = TargetInstance.Day
  107. This says to generate an event once an hour for the first half
  108. of the day when the minute in the current hour is equal to the
  109. day of the month. This is only to occur on the date 10/10/99.
  110. The following is an excerpt from the WQL1 grammar that has
  111. been modified to allow support for queries containing simple
  112. algebraic expressions:
  113. <parse> ::= SELECT * FROM <class_name> WHERE <expr>;
  114. <property_name> ::= IDENTIFIER <property_name_2>;
  115. <property_name_2> ::= <>;
  116. <property_name_2> ::= DOT IDENTIFIER <property_name_2>;
  117. <class_name> ::= IDENTIFIER;
  118. <expr> ::= <term> <expr2>;
  119. <expr2> ::= OR <term> <expr2>;
  120. <expr2> ::= <>;
  121. <term> ::= <simple_expr> <term2>;
  122. <term2> ::= AND <simple_expr> <term2>;
  123. <term2> ::= <>;
  124. <operand> ::= <property_name>
  125. <operand> ::= <typed_constant>
  126. <simple_expr> ::= <algebraic_expr> <rel_operator> <algebraic_expr>;
  127. <algebraic_expr> ::= <operand>
  128. <algebraic_expr> ::= <algebraic_expr> <arithmetic_operator> <algebraic_expr>
  129. <algebraic_expr> ::= OPEN_PAREN <algebraic_expr> CLOSE_PAREN;
  130. <typed_constant> ::= VARIANT; // VT_R8, VT_I4, VT_BSTR
  131. <typed_constant> ::= TRUE;
  132. <typed_constant> ::= FALSE;
  133. <typed_constant> ::= <macro_constant>;
  134. <macro_constant> ::= PERCENT IDENTIFIER PERCENT ;
  135. <rel_operator> ::= <equiv_operator>;
  136. <rel_operator> ::= <comp_operator>;
  137. <equiv_operator> ::= EQUIVALENT_OPERATOR; // =, !=, <>
  138. <comp_operator> ::= COMPARE_OPERATOR; // <=, >=, <, >, like, isa
  139. <arithmetic_operator> ::= ARITHMETIC_OPERATOR; // +, -, *, /, %
  140. **** INSERT SECTION ON HOW TO DECOMPOSE A WQL STATEMENT TO A
  141. SERIES OF TIME PATTERNS TO MATCH AGAINST (DNF)
  142. Solution to the Problem of Finding a Closest Date in the Future
  143. to Current Time:
  144. First, let's impose a few key constraints that do not detract from
  145. the fundamental problem but allow us to more clearly illustrate the
  146. solution.
  147. 1. Limit the problem to finding the next date within the current year.
  148. This is reasonable because if a suitable date cannot be found
  149. within the remainder of the current year, find the first matching
  150. date from midnight January 1st of next year.
  151. 2. Limit ourselves to those fields of date that do not have overlaping
  152. meaning and exactly contain each other. For our Win32_CurrentTime
  153. structure those fields are: Year, Month, Day, Hour, Minute, Second.
  154. Week and any representation dependent on week violates this
  155. criteria because years and months do not, in general, start and
  156. stop on week boundaries. This means that weeks are not countable
  157. w.r.t. any larger unit of time.
  158. Given these constraints, it becomes possible to perform addition with
  159. carry on a given date with intervals expressed in any of the above
  160. units quickly.
  161. Now, our problem is one of finding the first date beyond the current
  162. time that matches a specific pattern provided by a WQL query. Because
  163. of the 2nd constraint, we can find a matching time incrementally by
  164. working our way from seconds to years finding the next matching pattern
  165. for each. When complete, we will have found the next closest date to
  166. the current time matching the pattern.
  167. From the parsing phase we derived a set of date "patterns" which can
  168. be used to find matching future dates. Each such pattern is
  169. represented by an object that contains matching criteria for each
  170. date field. For each of second, minute, hour, day and month we
  171. represent the set of valid times as members of a set.
  172. The pattern object looks like:
  173. DatePattern
  174. {
  175. VALIDMATCH Year; // integer
  176. VALIDMATCH Month; // [1..12]
  177. VALIDMATCH Day; // [1..31] *varies with month
  178. VALIDMATCH DayOfWeek; // [1..7]
  179. VALIDMATCH WeekInMonth; // [1..5]
  180. VALIDMATCH Quarter; // [1..4]
  181. VALIDMATCH Hour; // [1..24]
  182. VALIDMATCH Minute; // [1..60]
  183. VALIDMATCH Second; // [1..60]
  184. }
  185. The comment following each field indicates the valid
  186. range of values.
  187. The matching criteria VALIDMATCH looks like:
  188. VALIDMATCH
  189. {
  190. unsigned UpperBound;
  191. unsigned LowerBound;
  192. unsigned Modulus;
  193. unsigned CountNotMatching;
  194. unsigned *NotMatching;
  195. }
  196. Because, for each field above except year, the set of valid values
  197. is finite, we can collectively represent all of the limiting
  198. criteria within a VALIDMATCH object as a set containing matching
  199. members. The set described is a finite set on which there is a
  200. total ordering of its members. This can be represented by a bit
  201. field with each bit identifying one potential member. If a member
  202. is present in a given set its bit is set to one otherwise it is
  203. zero.
  204. With this arrangement, given any value within the range of the
  205. set we can find immediately whether or not that value is, in fact,
  206. a member of the set or, if not, what is the next set value. The
  207. following function outlines this task:
  208. typedef Set __int64; // Set is a bitfield with 1 represented by
  209. // leftmost bit
  210. BOOL FindNextElement(int &iCurrentValue, Set ValidValues)
  211. {
  212. BOOL Carry = FALSE;
  213. Set CurrentValue = 0x80000000000000000 >> iCurrentValue;
  214. while(! (CurrentValue & ValidValues))
  215. {
  216. iCurrentValue >> 1;
  217. CurrentValue++;
  218. if(CurrentValue == 0x0)
  219. {
  220. CurrentValue = 0x8000000000000000;
  221. iCurrentValue = 1;
  222. Carry = TRUE;
  223. }
  224. }
  225. return Carry;
  226. }
  227. Note: this function assumes ValidValues is not empty.
  228. FindNextElement() takes a value as a member of a Set object
  229. and finds the next greater member of the set ValidValues or
  230. the first member encountered in case of roleover. Roleover is
  231. indicated by the carry flag. The first member encountered is
  232. returned in CurrentValue.
  233. Now, applying this to a complete date pattern consisting of year,
  234. month, day, hour, minute and second would involve applying this
  235. function using the method described above with the following special
  236. cases. First, if it was found that there was a carry on a month,
  237. then we would need to go back and ensure that the new day is less
  238. than or equal to the last day of the new month. If not, then keep
  239. adding months until a month is found where this is true. Second,
  240. if there is a carry on months, then we have crossed a year boundary.
  241. In this case we need to obtain the information for a new calendar
  242. year.
  243. To address the additional fields WeekInMonth, DayOfWeek and Quarter,
  244. we will use a technique based on the notion that these are just
  245. alternative ways to specify the pattern for the day field. For each
  246. for each of these fields create a bitfield representing the days of
  247. the current month. In each such field set those bits to one that
  248. represent those days in the month that match for each of WeekInMonth,
  249. DayOfWeek and Quarter. And each of these together and to that of
  250. days. The resultant bitfield is the total number of days for the
  251. current month that match all criteria. This implies that the
  252. problem reduces to that of addition with carry as described above.
  253. Day = Day & WeekInMonth & DayOfWeek & Quarter
  254. There is the additional problem of how to handle addition operations
  255. that cross month boundaries. This will occur when imcrementing the
  256. day field results in a carry over to the next month. When this
  257. happens we need to generate the composite Day field for the next
  258. valid month.
  259. To generate the bitfields for each of Day, WeekInMonth, DayOfWeek
  260. and Quarter w.r.t. some month we need to know how many days there
  261. are in the month, what month it is in the year and what day of the
  262. week is the first day of the month. The month information can be
  263. generated using the system functions SystemTimeToFileTime() and
  264. FileTimeToSystemTime(). Given a month and year we can build these
  265. four bitfields as follows:
  266. 1. Populate a SYSTEMTIME structure with date of the start of the
  267. month of interest at midnight of the first day of that month.
  268. <month>/1/<year> 0:0:0
  269. 2. Convert the SYSTEMTIME to a FILETIME and back to a SYSTEMTIME.
  270. Now SYSTEMTIME::wDayOfWeek will contain the day of the week
  271. for the first day of the month. Note: we will save this
  272. FILETIME for later use below.
  273. 3. Determine the FILETIME for the beginning of the month following
  274. the one we are interested as in step 1 and 2 above.
  275. 4. Subtract the second FILETIME from the first and convert the
  276. result to days. This is the number of days in the month.
  277. 5. Using the sets from each field of the DatePattern structure
  278. we can now construct the bitfields for the current month
  279. corresponding to each of the four fields above.
  280. 6. Combine the bitfields with the Day bitfield using logical and
  281. to obtain the desired composite bitfield.
  282. This functionality can be encapsulated into a single function that
  283. takes a desired month and year plus a DatePattern structure and
  284. returns the desired composite bitfield:
  285. Set GetDaysInMonth(DatePattern *, int year, int month)
  286. First, lets modify the DatePattern structure defined above to
  287. fields for each member that represents it in terms of sets as
  288. follows:
  289. DatePattern
  290. {
  291. VALIDMATCH Year; // integer
  292. VALIDMATCH Month; // [1..12]
  293. Set MonthSet;
  294. VALIDMATCH Day; // [1..31] *varies with month
  295. Set DaySet;
  296. VALIDMATCH DayOfWeek; // [1..7]
  297. Set DayOfWeekSet;
  298. VALIDMATCH WeekInMonth; // [1..5]
  299. Set WeekInMonthSet;
  300. VALIDMATCH Quarter; // [1..4]
  301. VALIDMATCH Hour; // [1..24]
  302. Set HourSet;
  303. VALIDMATCH Minute; // [1..60]
  304. Set MinuteSet;
  305. VALIDMATCH Second; // [1..60]
  306. Set SecondSet;
  307. }
  308. By providing a separate pattern for each of DayOfWeek and WeekInMonth,
  309. we can optimize calculations of the composite field Day.
  310. The current date is a collection of integers from SYSTEMTIME including
  311. wYear, wMonth, wDay, wHour, wMinute and wSecond. Here, then, is the
  312. algorithm for finding the closest next firing time to the current time:
  313. Let Pattern = DatePattern object
  314. Let Date = SYSTEMTIME object
  315. Let Carry = BOOL object
  316. GetSystemTime(&Date);
  317. Carry = FindNextElement(&Date.wSecond, Pattern.SecondSet);
  318. if(Carry) Date.wMinute++;
  319. Carry = FindNextElement(&Date.wMinute, Pattern.MinuteSet);
  320. if(Carry) Date.wHour++;
  321. Carry = FindNextElement(&Date.wHour, Pattern.HourSet);
  322. if(Carry) Date.wDay++;
  323. do
  324. {
  325. Carry = FindNextElement(&Date.wDay, Pattern.DaySet);
  326. if(Carry)
  327. {
  328. do
  329. {
  330. Date.wMonth++;
  331. Carry2 = FindNextElement(&Date.wMonth, Pattern.MonthSet);
  332. if(Carry2)
  333. {
  334. Date.wYear += 1;
  335. <find next matching Date.wYear against Pattern.Year using
  336. numerical methods>
  337. }
  338. Pattern.MonthSet = GetDaysInMonth(&Pattern, Date.wYear, Date.wMonth);
  339. }
  340. while(0x0 == Pattern.MonthSet);
  341. }
  342. }
  343. while(Carry);