:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / ntoskrnl / ke / timer.c
1 /* $Id$
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 }