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