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