Continue of MSVC-compiling changes....
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.64 2003/12/30 18:52:04 fireball 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 <limits.h>
21 #include <ddk/ntddk.h>
22 #include <internal/ke.h>
23 #include <internal/id.h>
24 #include <internal/ps.h>
25 #include <internal/safe.h>
26
27 #define NDEBUG
28 #include <internal/debug.h>
29
30 /* TYPES *****************************************************************/
31
32 #define TIMER_IRQ 0
33
34 /* GLOBALS ****************************************************************/
35
36 /*
37 * Current time
38 */
39 static LONGLONG boot_time = 0;
40 static LONGLONG system_time = 0;
41
42 /*
43 * Number of timer interrupts since initialisation
44 */
45 volatile ULONGLONG KeTickCount = 0;
46 volatile ULONG KiRawTicks = 0;
47
48 /*
49 * The increment in the system clock every timer tick (in system time units)
50 *
51 * = (1/18.2)*10^9
52 *
53 * RJJ was 54945055
54 */
55 #define CLOCK_INCREMENT (100000)
56
57 /*
58 * PURPOSE: List of timers
59 */
60 static LIST_ENTRY TimerListHead;
61 static KSPIN_LOCK TimerListLock;
62 static KSPIN_LOCK TimerValueLock;
63 static KDPC ExpireTimerDpc;
64
65 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
66
67 extern ULONG PiNrRunnableThreads;
68
69 #define MICROSECONDS_PER_TICK (10000)
70 #define TICKS_TO_CALIBRATE (1)
71 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
72 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
73
74 static BOOLEAN TimerInitDone = FALSE;
75
76 /* FUNCTIONS **************************************************************/
77
78
79 NTSTATUS STDCALL
80 NtQueryTimerResolution(OUT PULONG MinimumResolution,
81 OUT PULONG MaximumResolution,
82 OUT PULONG ActualResolution)
83 {
84 UNIMPLEMENTED;
85 }
86
87
88 NTSTATUS STDCALL
89 NtSetTimerResolution(IN ULONG RequestedResolution,
90 IN BOOL SetOrUnset,
91 OUT PULONG ActualResolution)
92 {
93 UNIMPLEMENTED;
94 }
95
96
97 NTSTATUS STDCALL
98 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
99 IN PLARGE_INTEGER Frequency)
100 {
101 LARGE_INTEGER PerfCounter;
102 LARGE_INTEGER PerfFrequency;
103 NTSTATUS Status;
104
105 PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
106
107 if (Counter != NULL)
108 {
109 Status = MmCopyToCaller(&Counter->QuadPart, &PerfCounter.QuadPart, sizeof(PerfCounter.QuadPart));
110 if (!NT_SUCCESS(Status))
111 {
112 return(Status);
113 }
114 }
115
116 if (Frequency != NULL)
117 {
118 Status = MmCopyToCaller(&Frequency->QuadPart, &PerfFrequency.QuadPart, sizeof(PerfFrequency.QuadPart));
119 if (!NT_SUCCESS(Status))
120 {
121 return(Status);
122 }
123 }
124
125 return(STATUS_SUCCESS);
126 }
127
128
129 NTSTATUS STDCALL
130 NtDelayExecution(IN ULONG Alertable,
131 IN TIME* Interval)
132 {
133 NTSTATUS Status;
134 LARGE_INTEGER Timeout;
135
136 Status = MmCopyFromCaller(&Timeout, Interval, sizeof(Timeout));
137 if (!NT_SUCCESS(Status))
138 {
139 return(Status);
140 }
141
142 Timeout = *((PLARGE_INTEGER)Interval);
143 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
144 Alertable, Internal, Timeout);
145
146 DPRINT("Execution delay is %d/%d\n",
147 Timeout.u.HighPart, Timeout.u.LowPart);
148 Status = KeDelayExecutionThread(UserMode, (BOOLEAN)Alertable, &Timeout);
149 return(Status);
150 }
151
152
153 /*
154 * @implemented
155 */
156 NTSTATUS STDCALL
157 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
158 BOOLEAN Alertable,
159 PLARGE_INTEGER Interval)
160 /*
161 * FUNCTION: Puts the current thread into an alertable or nonalertable
162 * wait state for a given internal
163 * ARGUMENTS:
164 * WaitMode = Processor mode in which the caller is waiting
165 * Altertable = Specifies if the wait is alertable
166 * Interval = Specifies the interval to wait
167 * RETURNS: Status
168 */
169 {
170 PKTHREAD Thread = KeGetCurrentThread();
171
172 KeSetTimer(&Thread->Timer, *Interval, NULL);
173 return (KeWaitForSingleObject(&Thread->Timer,
174 (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */
175 WaitMode, /* TMN: Was UserMode */
176 Alertable,
177 NULL));
178 }
179
180
181 /*
182 * @implemented
183 */
184 ULONG STDCALL
185 KeQueryTimeIncrement(VOID)
186 /*
187 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
188 * the system clock every time the clock interrupts
189 * RETURNS: The increment
190 */
191 {
192 return(CLOCK_INCREMENT);
193 }
194
195
196 /*
197 * @implemented
198 */
199 VOID STDCALL
200 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
201 /*
202 * FUNCTION: Gets the current system time
203 * ARGUMENTS:
204 * CurrentTime (OUT) = The routine stores the current time here
205 * NOTE: The time is the number of 100-nanosecond intervals since the
206 * 1st of January, 1601.
207 */
208 {
209 KIRQL oldIrql;
210
211 KeRaiseIrql(PROFILE_LEVEL, &oldIrql);
212 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
213 CurrentTime->QuadPart = system_time;
214 KeReleaseSpinLockFromDpcLevel(&TimerValueLock);
215 KeLowerIrql(oldIrql);
216 }
217
218
219 NTSTATUS STDCALL
220 NtGetTickCount (PULONG UpTime)
221 {
222 LARGE_INTEGER TickCount;
223 if (UpTime == NULL)
224 {
225 return(STATUS_INVALID_PARAMETER);
226 }
227
228 KeQueryTickCount(&TickCount);
229 return(MmCopyToCaller(UpTime, &TickCount.u.LowPart, sizeof(*UpTime)));
230 }
231
232
233 /*
234 * @implemented
235 */
236 BOOLEAN STDCALL
237 KeSetTimer (PKTIMER Timer,
238 LARGE_INTEGER DueTime,
239 PKDPC Dpc)
240 /*
241 * FUNCTION: Sets the absolute or relative interval at which a timer object
242 * is to be set to the signaled state and optionally supplies a
243 * CustomTimerDpc to be executed when the timer expires.
244 * ARGUMENTS:
245 * Timer = Points to a previously initialized timer object
246 * DueTimer = If positive then absolute time to expire at
247 * If negative then the relative time to expire at
248 * Dpc = If non-NULL then a dpc to be called when the timer expires
249 * RETURNS: True if the timer was already in the system timer queue
250 * False otherwise
251 */
252 {
253 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
254 }
255
256 /*
257 * @implemented
258 */
259 BOOLEAN STDCALL
260 KeSetTimerEx (PKTIMER Timer,
261 LARGE_INTEGER DueTime,
262 LONG Period,
263 PKDPC Dpc)
264 /*
265 * FUNCTION: Sets the absolute or relative interval at which a timer object
266 * is to be set to the signaled state and optionally supplies a
267 * CustomTimerDpc to be executed when the timer expires.
268 * ARGUMENTS:
269 * Timer = Points to a previously initialized timer object
270 * DueTimer = If positive then absolute time to expire at
271 * If negative then the relative time to expire at
272 * Dpc = If non-NULL then a dpc to be called when the timer expires
273 * RETURNS: True if the timer was already in the system timer queue
274 * False otherwise
275 */
276 {
277 KIRQL oldlvl;
278 LARGE_INTEGER SystemTime;
279 BOOLEAN AlreadyInList;
280
281 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
282
283 assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
284
285 KeRaiseIrql(DISPATCH_LEVEL, &oldlvl);
286 KeQuerySystemTime(&SystemTime);
287 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
288
289 Timer->Dpc = Dpc;
290 if (DueTime.QuadPart < 0)
291 {
292 Timer->DueTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart;
293 }
294 else
295 {
296 Timer->DueTime.QuadPart = DueTime.QuadPart;
297 }
298 Timer->Period = Period;
299 Timer->Header.SignalState = FALSE;
300 AlreadyInList = (Timer->TimerListEntry.Flink == NULL) ? FALSE : TRUE;
301 assert((Timer->TimerListEntry.Flink == NULL && Timer->TimerListEntry.Blink == NULL) ||
302 (Timer->TimerListEntry.Flink != NULL && Timer->TimerListEntry.Blink != NULL));
303 if (!AlreadyInList)
304 {
305 assert(&TimerListHead != &Timer->TimerListEntry);
306 InsertTailList(&TimerListHead, &Timer->TimerListEntry);
307 assert(TimerListHead.Flink != &TimerListHead);
308 assert(Timer->TimerListEntry.Flink != &Timer->TimerListEntry);
309 }
310 /*
311 * TODO: Perhaps verify that the timer really is in
312 * the TimerListHead list if AlreadyInList is TRUE?
313 */
314 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
315 KeLowerIrql(oldlvl);
316
317 return AlreadyInList;
318 }
319
320 /*
321 * @implemented
322 */
323 BOOLEAN STDCALL
324 KeCancelTimer (PKTIMER Timer)
325 /*
326 * FUNCTION: Removes a timer from the system timer list
327 * ARGUMENTS:
328 * Timer = timer to cancel
329 * RETURNS: True if the timer was running
330 * False otherwise
331 */
332 {
333 KIRQL oldlvl;
334
335 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
336
337 KeAcquireSpinLock(&TimerListLock, &oldlvl);
338
339 if (Timer->TimerListEntry.Flink == NULL)
340 {
341 KeReleaseSpinLock(&TimerListLock, oldlvl);
342 return(FALSE);
343 }
344 assert(&Timer->TimerListEntry != &TimerListHead);
345 assert(Timer->TimerListEntry.Flink != &Timer->TimerListEntry);
346 RemoveEntryList(&Timer->TimerListEntry);
347 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
348 KeReleaseSpinLock(&TimerListLock, oldlvl);
349
350 return(TRUE);
351 }
352
353 /*
354 * @implemented
355 */
356 BOOLEAN STDCALL
357 KeReadStateTimer (PKTIMER Timer)
358 {
359 return (BOOLEAN)(Timer->Header.SignalState);
360 }
361
362 /*
363 * @implemented
364 */
365 VOID STDCALL
366 KeInitializeTimer (PKTIMER Timer)
367 /*
368 * FUNCTION: Initalizes a kernel timer object
369 * ARGUMENTS:
370 * Timer = caller supplied storage for the timer
371 * NOTE: This function initializes a notification timer
372 */
373 {
374 KeInitializeTimerEx(Timer, NotificationTimer);
375 }
376
377 /*
378 * @implemented
379 */
380 VOID STDCALL
381 KeInitializeTimerEx (PKTIMER Timer,
382 TIMER_TYPE Type)
383 /*
384 * FUNCTION: Initializes a kernel timer object
385 * ARGUMENTS:
386 * Timer = caller supplied storage for the timer
387 * Type = the type of timer (notification or synchronization)
388 * NOTE: When a notification type expires all waiting threads are released
389 * and the timer remains signalled until it is explicitly reset. When a
390 * syncrhonization timer expires its state is set to signalled until a
391 * single waiting thread is released and then the timer is reset.
392 */
393 {
394 ULONG IType;
395
396 if (Type == NotificationTimer)
397 {
398 IType = InternalNotificationTimer;
399 }
400 else if (Type == SynchronizationTimer)
401 {
402 IType = InternalSynchronizationTimer;
403 }
404 else
405 {
406 assert(FALSE);
407 return;
408 }
409
410 KeInitializeDispatcherHeader(&Timer->Header,
411 IType,
412 sizeof(KTIMER) / sizeof(ULONG),
413 FALSE);
414 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
415 }
416
417 /*
418 * @implemented
419 */
420 VOID STDCALL
421 KeQueryTickCount(PLARGE_INTEGER TickCount)
422 /*
423 * FUNCTION: Returns the number of ticks since the system was booted
424 * ARGUMENTS:
425 * TickCount (OUT) = Points to storage for the number of ticks
426 */
427 {
428 TickCount->QuadPart = KeTickCount;
429 }
430
431 /*
432 * We enter this function at IRQL DISPATCH_LEVEL, and with the
433 * TimerListLock held.
434 */
435 STATIC VOID
436 HandleExpiredTimer(PKTIMER Timer)
437 {
438 DPRINT("HandleExpiredTime(Timer %x) at IRQL: %d\n", Timer, KeGetCurrentIrql());
439 if (Timer->Dpc != NULL)
440 {
441 DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n",
442 Timer->Dpc, Timer->Dpc->DeferredRoutine);
443 KeInsertQueueDpc(Timer->Dpc,
444 NULL,
445 NULL);
446 DPRINT("Finished dpc routine\n");
447 }
448
449 assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
450
451 KeAcquireDispatcherDatabaseLockAtDpcLevel();
452 Timer->Header.SignalState = TRUE;
453 KeDispatcherObjectWake(&Timer->Header);
454 KeReleaseDispatcherDatabaseLockFromDpcLevel();
455 if (Timer->Period != 0)
456 {
457 Timer->DueTime.QuadPart +=
458 Timer->Period * SYSTEM_TIME_UNITS_PER_MSEC;
459 }
460 else
461 {
462 assert(&Timer->TimerListEntry != &TimerListHead);
463 RemoveEntryList(&Timer->TimerListEntry);
464 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
465 }
466 }
467
468 VOID STDCALL
469 KeExpireTimers(PKDPC Dpc,
470 PVOID Context1,
471 PVOID Arg1,
472 PVOID Arg2)
473 {
474 PLIST_ENTRY current_entry = NULL;
475 PKTIMER current = NULL;
476 ULONG Eip = (ULONG)Arg1;
477 LARGE_INTEGER SystemTime;
478
479 DPRINT("KeExpireTimers()\n");
480
481 assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
482
483 KeQuerySystemTime(&SystemTime);
484
485 if (KeGetCurrentIrql() > DISPATCH_LEVEL)
486 {
487 DPRINT1("-----------------------------\n");
488 KEBUGCHECK(0);
489 }
490
491 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
492
493 current_entry = TimerListHead.Flink;
494
495 assert(current_entry);
496
497 while (current_entry != &TimerListHead)
498 {
499 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
500 assert(current);
501 assert(current_entry != &TimerListHead);
502 assert(current_entry->Flink != current_entry);
503
504 current_entry = current_entry->Flink;
505 if ((ULONGLONG) SystemTime.QuadPart >= current->DueTime.QuadPart)
506 {
507 HandleExpiredTimer(current);
508 }
509 }
510
511 KiAddProfileEvent(ProfileTime, Eip);
512
513 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
514 }
515
516
517 VOID
518 KiUpdateSystemTime(KIRQL oldIrql,
519 ULONG Eip)
520 /*
521 * FUNCTION: Handles a timer interrupt
522 */
523 {
524
525 assert(KeGetCurrentIrql() == PROFILE_LEVEL);
526
527 KiRawTicks++;
528
529 if (TimerInitDone == FALSE)
530 {
531 return;
532 }
533 /*
534 * Increment the number of timers ticks
535 */
536 KeTickCount++;
537 SharedUserData->TickCountLow++;
538
539 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
540 system_time += CLOCK_INCREMENT;
541 KeReleaseSpinLockFromDpcLevel(&TimerValueLock);
542
543 /*
544 * Queue a DPC that will expire timers
545 */
546 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
547 }
548
549
550 VOID INIT_FUNCTION
551 KeInitializeTimerImpl(VOID)
552 /*
553 * FUNCTION: Initializes timer irq handling
554 * NOTE: This is only called once from main()
555 */
556 {
557 TIME_FIELDS TimeFields;
558 LARGE_INTEGER SystemBootTime;
559
560 DPRINT("KeInitializeTimerImpl()\n");
561 InitializeListHead(&TimerListHead);
562 KeInitializeSpinLock(&TimerListLock);
563 KeInitializeSpinLock(&TimerValueLock);
564 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
565 TimerInitDone = TRUE;
566 /*
567 * Calculate the starting time for the system clock
568 */
569 HalQueryRealTimeClock(&TimeFields);
570 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
571 boot_time=SystemBootTime.QuadPart;
572 system_time=boot_time;
573
574 SharedUserData->TickCountLow = 0;
575 SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310
576
577 DPRINT("Finished KeInitializeTimerImpl()\n");
578 }