Use free Windows DDK and compile with latest MinGW releases.
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.52 2002/09/07 15:12:57 chorns Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/timer.c
6 * PURPOSE: Handle timers
7 * PROGRAMMER: David Welch (welch@mcmail.com)
8 * UPDATE HISTORY:
9 * 28/05/98: Created
10 * 12/3/99: Phillip Susi: enabled the timers, fixed spin lock
11 */
12
13 /* NOTES ******************************************************************/
14 /*
15 * System time units are 100-nanosecond intervals
16 */
17
18 /* INCLUDES ***************************************************************/
19
20 #include <ntoskrnl.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25
26 /* TYPES *****************************************************************/
27
28 #define TIMER_IRQ 0
29
30 /* GLOBALS ****************************************************************/
31
32 /*
33 * Current time
34 */
35 static unsigned long long boot_time = 0;
36 static unsigned long long system_time = 0;
37
38 /*
39 * Number of timer interrupts since initialisation
40 */
41 volatile ULONGLONG KeTickCount = 0;
42 volatile ULONG KiRawTicks = 0;
43
44 /*
45 * The increment in the system clock every timer tick (in system time units)
46 *
47 * = (1/18.2)*10^9
48 *
49 * RJJ was 54945055
50 */
51 #define CLOCK_INCREMENT (100000)
52
53 /*
54 * PURPOSE: List of timers
55 */
56 static LIST_ENTRY TimerListHead;
57 static KSPIN_LOCK TimerListLock;
58 static KDPC ExpireTimerDpc;
59
60 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
61
62 extern ULONG PiNrRunnableThreads;
63
64 #define MICROSECONDS_PER_TICK (10000)
65 #define TICKS_TO_CALIBRATE (1)
66 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
67 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
68
69 static BOOLEAN TimerInitDone = FALSE;
70
71 /* FUNCTIONS **************************************************************/
72
73
74 NTSTATUS STDCALL
75 NtQueryTimerResolution(OUT PULONG MinimumResolution,
76 OUT PULONG MaximumResolution,
77 OUT PULONG ActualResolution)
78 {
79 UNIMPLEMENTED;
80 }
81
82
83 NTSTATUS STDCALL
84 NtSetTimerResolution(IN ULONG RequestedResolution,
85 IN BOOL SetOrUnset,
86 OUT PULONG ActualResolution)
87 {
88 UNIMPLEMENTED;
89 }
90
91
92 NTSTATUS STDCALL
93 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
94 IN PLARGE_INTEGER Frequency)
95 {
96 UNIMPLEMENTED;
97 }
98
99
100 NTSTATUS STDCALL
101 NtDelayExecution(IN ULONG Alertable,
102 IN TIME* Interval)
103 {
104 NTSTATUS Status;
105 LARGE_INTEGER Timeout;
106
107 Timeout = *((PLARGE_INTEGER)Interval);
108 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
109 Alertable, Internal, Timeout);
110
111 DPRINT("Execution delay is %d/%d\n",
112 Timeout.u.HighPart, Timeout.u.LowPart);
113 Status = KeDelayExecutionThread(UserMode, Alertable, &Timeout);
114 return(Status);
115 }
116
117
118 NTSTATUS STDCALL
119 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
120 BOOLEAN Alertable,
121 PLARGE_INTEGER Interval)
122 /*
123 * FUNCTION: Puts the current thread into an alertable or nonalertable
124 * wait state for a given internal
125 * ARGUMENTS:
126 * WaitMode = Processor mode in which the caller is waiting
127 * Altertable = Specifies if the wait is alertable
128 * Interval = Specifies the interval to wait
129 * RETURNS: Status
130 */
131 {
132 PKTHREAD Thread = KeGetCurrentThread();
133
134 KeInitializeTimer(&Thread->Timer);
135 KeSetTimer(&Thread->Timer, *Interval, NULL);
136 return (KeWaitForSingleObject(&Thread->Timer,
137 Executive,
138 UserMode,
139 Alertable,
140 NULL));
141 }
142
143
144 ULONG STDCALL
145 KeQueryTimeIncrement(VOID)
146 /*
147 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
148 * the system clock every time the clock interrupts
149 * RETURNS: The increment
150 */
151 {
152 return(CLOCK_INCREMENT);
153 }
154
155
156 VOID STDCALL
157 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
158 /*
159 * FUNCTION: Gets the current system time
160 * ARGUMENTS:
161 * CurrentTime (OUT) = The routine stores the current time here
162 * NOTE: The time is the number of 100-nanosecond intervals since the
163 * 1st of January, 1601.
164 */
165 {
166 CurrentTime->QuadPart = system_time;
167 }
168
169
170 NTSTATUS STDCALL
171 NtGetTickCount (PULONG UpTime)
172 {
173 LARGE_INTEGER TickCount;
174 if (UpTime == NULL)
175 return(STATUS_INVALID_PARAMETER);
176 KeQueryTickCount(&TickCount);
177 *UpTime = TickCount.u.LowPart;
178 return (STATUS_SUCCESS);
179 }
180
181
182 BOOLEAN STDCALL
183 KeSetTimer (PKTIMER Timer,
184 LARGE_INTEGER DueTime,
185 PKDPC Dpc)
186 /*
187 * FUNCTION: Sets the absolute or relative interval at which a timer object
188 * is to be set to the signaled state and optionally supplies a
189 * CustomTimerDpc to be executed when the timer expires.
190 * ARGUMENTS:
191 * Timer = Points to a previously initialized timer object
192 * DueTimer = If positive then absolute time to expire at
193 * If negative then the relative time to expire at
194 * Dpc = If non-NULL then a dpc to be called when the timer expires
195 * RETURNS: True if the timer was already in the system timer queue
196 * False otherwise
197 */
198 {
199 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
200 }
201
202 BOOLEAN STDCALL
203 KeSetTimerEx (PKTIMER Timer,
204 LARGE_INTEGER DueTime,
205 LONG Period,
206 PKDPC Dpc)
207 /*
208 * FUNCTION: Sets the absolute or relative interval at which a timer object
209 * is to be set to the signaled state and optionally supplies a
210 * CustomTimerDpc to be executed when the timer expires.
211 * ARGUMENTS:
212 * Timer = Points to a previously initialized timer object
213 * DueTimer = If positive then absolute time to expire at
214 * If negative then the relative time to expire at
215 * Dpc = If non-NULL then a dpc to be called when the timer expires
216 * RETURNS: True if the timer was already in the system timer queue
217 * False otherwise
218 */
219 {
220 KIRQL oldlvl;
221
222 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
223 KeAcquireSpinLock( &TimerListLock, &oldlvl );
224
225 Timer->Dpc = Dpc;
226 if (DueTime.QuadPart < 0)
227 {
228 Timer->DueTime.QuadPart = system_time - DueTime.QuadPart;
229 }
230 else
231 {
232 Timer->DueTime.QuadPart = DueTime.QuadPart;
233 }
234 Timer->Period = Period;
235 Timer->Header.SignalState = FALSE;
236 if (Timer->TimerListEntry.Flink != NULL)
237 {
238 KeReleaseSpinLock(&TimerListLock, oldlvl);
239 return(TRUE);
240 }
241 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
242 KeReleaseSpinLock(&TimerListLock, oldlvl);
243
244 return(FALSE);
245 }
246
247 BOOLEAN STDCALL
248 KeCancelTimer (PKTIMER Timer)
249 /*
250 * FUNCTION: Removes a timer from the system timer list
251 * ARGUMENTS:
252 * Timer = timer to cancel
253 * RETURNS: True if the timer was running
254 * False otherwise
255 */
256 {
257 KIRQL oldlvl;
258
259 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
260
261 KeRaiseIrql(HIGH_LEVEL, &oldlvl);
262 KeAcquireSpinLockAtDpcLevel( &TimerListLock );
263
264 if (Timer->TimerListEntry.Flink == NULL)
265 {
266 KeReleaseSpinLock(&TimerListLock, oldlvl);
267 return(FALSE);
268 }
269 RemoveEntryList(&Timer->TimerListEntry);
270 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
271 KeReleaseSpinLock(&TimerListLock, oldlvl);
272
273 return(TRUE);
274 }
275
276 BOOLEAN STDCALL
277 KeReadStateTimer (PKTIMER Timer)
278 {
279 return(Timer->Header.SignalState);
280 }
281
282 VOID STDCALL
283 KeInitializeTimer (PKTIMER Timer)
284 /*
285 * FUNCTION: Initalizes a kernel timer object
286 * ARGUMENTS:
287 * Timer = caller supplied storage for the timer
288 * NOTE: This function initializes a notification timer
289 */
290 {
291 KeInitializeTimerEx(Timer, NotificationTimer);
292 }
293
294 VOID STDCALL
295 KeInitializeTimerEx (PKTIMER Timer,
296 TIMER_TYPE Type)
297 /*
298 * FUNCTION: Initializes a kernel timer object
299 * ARGUMENTS:
300 * Timer = caller supplied storage for the timer
301 * Type = the type of timer (notification or synchronization)
302 * NOTE: When a notification type expires all waiting threads are released
303 * and the timer remains signalled until it is explicitly reset. When a
304 * syncrhonization timer expires its state is set to signalled until a
305 * single waiting thread is released and then the timer is reset.
306 */
307 {
308 ULONG IType;
309
310 if (Type == NotificationTimer)
311 {
312 IType = InternalNotificationTimer;
313 }
314 else if (Type == SynchronizationTimer)
315 {
316 IType = InternalSynchronizationTimer;
317 }
318 else
319 {
320 assert(FALSE);
321 return;
322 }
323
324 KeInitializeDispatcherHeader(&Timer->Header,
325 IType,
326 sizeof(KTIMER) / sizeof(ULONG),
327 FALSE);
328 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
329 }
330
331 VOID STDCALL
332 KeQueryTickCount(PLARGE_INTEGER TickCount)
333 /*
334 * FUNCTION: Returns the number of ticks since the system was booted
335 * ARGUMENTS:
336 * TickCount (OUT) = Points to storage for the number of ticks
337 */
338 {
339 TickCount->QuadPart = KeTickCount;
340 }
341
342 STATIC VOID
343 HandleExpiredTimer(PKTIMER current)
344 {
345 DPRINT("HandleExpiredTime(current %x)\n",current);
346 if (current->Dpc != NULL)
347 {
348 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
349 current->Dpc, current->Dpc->DeferredRoutine);
350 KeInsertQueueDpc(current->Dpc,
351 NULL,
352 NULL);
353 DPRINT("Finished dpc routine\n");
354 }
355 KeAcquireDispatcherDatabaseLock(FALSE);
356 current->Header.SignalState = TRUE;
357 KeDispatcherObjectWake(&current->Header);
358 KeReleaseDispatcherDatabaseLock(FALSE);
359 if (current->Period != 0)
360 {
361 current->DueTime.QuadPart +=
362 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
363 }
364 else
365 {
366 RemoveEntryList(&current->TimerListEntry);
367 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
368 }
369 }
370
371 VOID STDCALL
372 KeExpireTimers(PKDPC Dpc,
373 PVOID Context1,
374 PVOID Arg1,
375 PVOID Arg2)
376 {
377 PLIST_ENTRY current_entry = NULL;
378 PKTIMER current = NULL;
379 ULONG Eip = (ULONG)Arg1;
380
381 DPRINT("KeExpireTimers()\n");
382
383 current_entry = TimerListHead.Flink;
384
385 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
386
387 while (current_entry != &TimerListHead)
388 {
389 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
390
391 current_entry = current_entry->Flink;
392
393 if (system_time >= current->DueTime.QuadPart)
394 {
395 HandleExpiredTimer(current);
396 }
397 }
398
399 KiAddProfileEvent(ProfileTime, Eip);
400
401 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
402 }
403
404
405 VOID
406 KiUpdateSystemTime(KIRQL oldIrql,
407 ULONG Eip)
408 /*
409 * FUNCTION: Handles a timer interrupt
410 */
411 {
412 KiRawTicks++;
413
414 if (TimerInitDone == FALSE)
415 {
416 return;
417 }
418 /*
419 * Increment the number of timers ticks
420 */
421 KeTickCount++;
422 SharedUserData->TickCountLow++;
423 system_time = system_time + CLOCK_INCREMENT;
424
425 /*
426 * Queue a DPC that will expire timers
427 */
428 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
429 }
430
431
432 VOID
433 KeInitializeTimerImpl(VOID)
434 /*
435 * FUNCTION: Initializes timer irq handling
436 * NOTE: This is only called once from main()
437 */
438 {
439 TIME_FIELDS TimeFields;
440 LARGE_INTEGER SystemBootTime;
441
442 DPRINT("KeInitializeTimerImpl()\n");
443 InitializeListHead(&TimerListHead);
444 KeInitializeSpinLock(&TimerListLock);
445 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
446 TimerInitDone = TRUE;
447 /*
448 * Calculate the starting time for the system clock
449 */
450 HalQueryRealTimeClock(&TimeFields);
451 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
452 boot_time=SystemBootTime.QuadPart;
453 system_time=boot_time;
454
455 DPRINT("Finished KeInitializeTimerImpl()\n");
456 }