2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/timerobj.c
5 * PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 KTIMER_TABLE_ENTRY KiTimerTableListHead
[TIMER_TABLE_SIZE
];
18 LARGE_INTEGER KiTimeIncrementReciprocal
;
19 UCHAR KiTimeIncrementShiftCount
;
20 BOOLEAN KiEnableTimerWatchdog
= FALSE
;
22 /* PRIVATE FUNCTIONS *********************************************************/
26 KiInsertTreeTimer(IN PKTIMER Timer
,
27 IN LARGE_INTEGER Interval
)
29 BOOLEAN Inserted
= FALSE
;
31 PKSPIN_LOCK_QUEUE LockQueue
;
32 DPRINT("KiInsertTreeTimer(): Timer %p, Interval: %I64d\n", Timer
, Interval
.QuadPart
);
34 /* Setup the timer's due time */
35 if (KiComputeDueTime(Timer
, Interval
, &Hand
))
37 /* Acquire the lock */
38 LockQueue
= KiAcquireTimerLock(Hand
);
40 /* Insert the timer */
41 if (KiInsertTimerTable(Timer
, Hand
))
43 /* It was already there, remove it */
44 KiRemoveEntryTimer(Timer
);
45 Timer
->Header
.Inserted
= FALSE
;
49 /* Otherwise, we're now inserted */
53 /* Release the lock */
54 KiReleaseTimerLock(LockQueue
);
57 /* Release the lock and return insert status */
63 KiInsertTimerTable(IN PKTIMER Timer
,
66 LARGE_INTEGER InterruptTime
;
67 LONGLONG DueTime
= Timer
->DueTime
.QuadPart
;
68 BOOLEAN Expired
= FALSE
;
69 PLIST_ENTRY ListHead
, NextEntry
;
71 DPRINT("KiInsertTimerTable(): Timer %p, Hand: %lu\n", Timer
, Hand
);
73 /* Check if the period is zero */
74 if (!Timer
->Period
) Timer
->Header
.SignalState
= FALSE
;
77 ASSERT(Hand
== KiComputeTimerTableIndex(DueTime
));
79 /* Loop the timer list backwards */
80 ListHead
= &KiTimerTableListHead
[Hand
].Entry
;
81 NextEntry
= ListHead
->Blink
;
82 while (NextEntry
!= ListHead
)
85 CurrentTimer
= CONTAINING_RECORD(NextEntry
, KTIMER
, TimerListEntry
);
87 /* Now check if we can fit it before */
88 if ((ULONGLONG
)DueTime
>= CurrentTimer
->DueTime
.QuadPart
) break;
91 NextEntry
= NextEntry
->Blink
;
94 /* Looped all the list, insert it here and get the interrupt time again */
95 InsertHeadList(NextEntry
, &Timer
->TimerListEntry
);
97 /* Check if we didn't find it in the list */
98 if (NextEntry
== ListHead
)
101 KiTimerTableListHead
[Hand
].Time
.QuadPart
= DueTime
;
103 /* Make sure it hasn't expired already */
104 InterruptTime
.QuadPart
= KeQueryInterruptTime();
105 if (DueTime
<= InterruptTime
.QuadPart
) Expired
= TRUE
;
108 /* Return expired state */
114 KiSignalTimer(IN PKTIMER Timer
)
116 BOOLEAN RequestInterrupt
= FALSE
;
117 PKDPC Dpc
= Timer
->Dpc
;
118 ULONG Period
= Timer
->Period
;
119 LARGE_INTEGER Interval
, SystemTime
;
120 DPRINT("KiSignalTimer(): Timer %p\n", Timer
);
122 /* Set default values */
123 Timer
->Header
.Inserted
= FALSE
;
124 Timer
->Header
.SignalState
= TRUE
;
126 /* Check if the timer has waiters */
127 if (!IsListEmpty(&Timer
->Header
.WaitListHead
))
129 /* Check the type of event */
130 if (Timer
->Header
.Type
== TimerNotificationObject
)
132 /* Unwait the thread */
133 KxUnwaitThread(&Timer
->Header
, IO_NO_INCREMENT
);
137 /* Otherwise unwait the thread and signal the timer */
138 KxUnwaitThreadForEvent((PKEVENT
)Timer
, IO_NO_INCREMENT
);
142 /* Check if we have a period */
145 /* Calculate the interval and insert the timer */
146 Interval
.QuadPart
= Int32x32To64(Period
, -10000);
147 while (!KiInsertTreeTimer(Timer
, Interval
));
150 /* Check if we have a DPC */
153 /* Insert it in the queue */
154 KeQuerySystemTime(&SystemTime
);
155 KeInsertQueueDpc(Dpc
,
156 ULongToPtr(SystemTime
.LowPart
),
157 ULongToPtr(SystemTime
.HighPart
));
158 RequestInterrupt
= TRUE
;
161 /* Return whether we need to request a DPC interrupt or not */
162 return RequestInterrupt
;
167 KiCompleteTimer(IN PKTIMER Timer
,
168 IN PKSPIN_LOCK_QUEUE LockQueue
)
171 BOOLEAN RequestInterrupt
= FALSE
;
172 DPRINT("KiCompleteTimer(): Timer %p, LockQueue: %p\n", Timer
, LockQueue
);
174 /* Remove it from the timer list */
175 KiRemoveEntryTimer(Timer
);
177 /* Link the timer list to our stack */
178 ListHead
.Flink
= &Timer
->TimerListEntry
;
179 ListHead
.Blink
= &Timer
->TimerListEntry
;
180 Timer
->TimerListEntry
.Flink
= &ListHead
;
181 Timer
->TimerListEntry
.Blink
= &ListHead
;
183 /* Release the timer lock */
184 KiReleaseTimerLock(LockQueue
);
186 /* Acquire dispatcher lock */
187 KiAcquireDispatcherLockAtDpcLevel();
189 /* Signal the timer if it's still on our list */
190 if (!IsListEmpty(&ListHead
)) RequestInterrupt
= KiSignalTimer(Timer
);
192 /* Release the dispatcher lock */
193 KiReleaseDispatcherLockFromDpcLevel();
195 /* Request a DPC if needed */
196 if (RequestInterrupt
) HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
199 /* PUBLIC FUNCTIONS **********************************************************/
206 KeCancelTimer(IN OUT PKTIMER Timer
)
211 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
212 DPRINT("KeCancelTimer(): Timer %p\n", Timer
);
214 /* Lock the Database and Raise IRQL */
215 OldIrql
= KiAcquireDispatcherLock();
217 /* Check if it's inserted, and remove it if it is */
218 Inserted
= Timer
->Header
.Inserted
;
219 if (Inserted
) KxRemoveTreeTimer(Timer
);
221 /* Release Dispatcher Lock */
222 KiReleaseDispatcherLock(OldIrql
);
224 /* Return the old state */
233 KeInitializeTimer(OUT PKTIMER Timer
)
235 /* Call the New Function */
236 KeInitializeTimerEx(Timer
, NotificationTimer
);
244 KeInitializeTimerEx(OUT PKTIMER Timer
,
247 DPRINT("KeInitializeTimerEx(): Timer %p, Type %s\n",
248 Timer
, (Type
== NotificationTimer
) ?
249 "NotificationTimer" : "SynchronizationTimer");
251 /* Initialize the Dispatch Header */
252 Timer
->Header
.Type
= TimerNotificationObject
+ Type
;
253 //Timer->Header.TimerControlFlags = 0; // win does not init this field
254 Timer
->Header
.Hand
= sizeof(KTIMER
) / sizeof(ULONG
);
255 Timer
->Header
.Inserted
= 0; // win7: Timer->Header.TimerMiscFlags = 0;
256 Timer
->Header
.SignalState
= 0;
257 InitializeListHead(&(Timer
->Header
.WaitListHead
));
259 /* Initialize the Other data */
260 Timer
->DueTime
.QuadPart
= 0;
269 KeReadStateTimer(IN PKTIMER Timer
)
271 /* Return the Signal State */
273 return (BOOLEAN
)Timer
->Header
.SignalState
;
281 KeSetTimer(IN OUT PKTIMER Timer
,
282 IN LARGE_INTEGER DueTime
,
283 IN PKDPC Dpc OPTIONAL
)
285 /* Call the newer function and supply a period of 0 */
286 return KeSetTimerEx(Timer
, DueTime
, 0, Dpc
);
294 KeSetTimerEx(IN OUT PKTIMER Timer
,
295 IN LARGE_INTEGER DueTime
,
297 IN PKDPC Dpc OPTIONAL
)
302 BOOLEAN RequestInterrupt
= FALSE
;
304 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
305 DPRINT("KeSetTimerEx(): Timer %p, DueTime %I64d, Period %d, Dpc %p\n",
306 Timer
, DueTime
.QuadPart
, Period
, Dpc
);
308 /* Lock the Database and Raise IRQL */
309 OldIrql
= KiAcquireDispatcherLock();
311 /* Check if it's inserted, and remove it if it is */
312 Inserted
= Timer
->Header
.Inserted
;
313 if (Inserted
) KxRemoveTreeTimer(Timer
);
315 /* Set Default Timer Data */
317 Timer
->Period
= Period
;
318 if (!KiComputeDueTime(Timer
, DueTime
, &Hand
))
320 /* Signal the timer */
321 RequestInterrupt
= KiSignalTimer(Timer
);
323 /* Release the dispatcher lock */
324 KiReleaseDispatcherLockFromDpcLevel();
326 /* Check if we need to do an interrupt */
327 if (RequestInterrupt
) HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
331 /* Insert the timer */
332 Timer
->Header
.SignalState
= FALSE
;
333 KxInsertTimer(Timer
, Hand
);
336 /* Exit the dispatcher */
337 KiExitDispatcher(OldIrql
);
339 /* Return old state */