Created bugs in wait and timer code
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/timer.c
5 * PURPOSE: Handle timers
6 * PROGRAMMER: David Welch (welch@mcmail.com)
7 * UPDATE HISTORY:
8 * 28/05/98: Created
9 */
10
11 /* NOTES ******************************************************************/
12 /*
13 * System time units are 100-nanosecond intervals
14 */
15
16 /* INCLUDES ***************************************************************/
17
18 #include <limits.h>
19 #include <ddk/ntddk.h>
20 #include <string.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25 /* TYPES *****************************************************************/
26
27 #define TIMER_IRQ 0
28
29
30 /* GLOBALS ****************************************************************/
31
32 /*
33 * Current time
34 */
35 static unsigned long long system_time = 0;
36
37 /*
38 * Number of timer interrupts since initialisation
39 */
40 volatile ULONGLONG KiTimerTicks;
41
42 /*
43 * The increment in the system clock every timer tick (in system time units)
44 *
45 * = (1/18.2)*10^9
46 *
47 * RJJ was 54945055
48 */
49 #define CLOCK_INCREMENT (549450)
50
51 /*
52 * PURPOSE: List of timers
53 */
54 static LIST_ENTRY timer_list_head = {NULL,NULL};
55 static KSPIN_LOCK timer_list_lock = {0,};
56
57
58
59 #define MICROSECONDS_PER_TICK (54945)
60 #define TICKS_TO_CALIBRATE (1)
61 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
62 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
63
64 static unsigned int loops_per_microsecond = 100;
65
66 /* FUNCTIONS **************************************************************/
67
68 VOID KeCalibrateTimerLoop(VOID)
69 {
70 unsigned int start_tick;
71 // unsigned int end_tick;
72 // unsigned int nr_ticks;
73 unsigned int i;
74 unsigned int microseconds;
75
76 for (i=0;i<20;i++)
77 {
78
79 start_tick = KiTimerTicks;
80 microseconds = 0;
81 while (start_tick == KiTimerTicks);
82 while (KiTimerTicks == (start_tick+TICKS_TO_CALIBRATE))
83 {
84 KeStallExecutionProcessor(1);
85 microseconds++;
86 };
87
88 // DbgPrint("microseconds %d\n",microseconds);
89
90 if (microseconds > (CALIBRATE_PERIOD+1000))
91 {
92 loops_per_microsecond = loops_per_microsecond + 1;
93 }
94 if (microseconds < (CALIBRATE_PERIOD-1000))
95 {
96 loops_per_microsecond = loops_per_microsecond - 1;
97 }
98 // DbgPrint("loops_per_microsecond %d\n",loops_per_microsecond);
99 }
100 // for(;;);
101 }
102
103
104 NTSTATUS STDCALL NtQueryTimerResolution (OUT PULONG MinimumResolution,
105 OUT PULONG MaximumResolution,
106 OUT PULONG ActualResolution)
107 {
108 return(ZwQueryTimerResolution(MinimumResolution,MaximumResolution,
109 ActualResolution));
110 }
111
112 NTSTATUS STDCALL ZwQueryTimerResolution (OUT PULONG MinimumResolution,
113 OUT PULONG MaximumResolution,
114 OUT PULONG ActualResolution)
115 {
116 UNIMPLEMENTED;
117 }
118
119 NTSTATUS STDCALL NtSetTimerResolution(IN ULONG RequestedResolution,
120 IN BOOL SetOrUnset,
121 OUT PULONG ActualResolution)
122 {
123 return(ZwSetTimerResolution(RequestedResolution,
124 SetOrUnset,
125 ActualResolution));
126 }
127
128 NTSTATUS STDCALL ZwSetTimerResolution(IN ULONG RequestedResolution,
129 IN BOOL SetOrUnset,
130 OUT PULONG ActualResolution)
131 {
132 UNIMPLEMENTED;
133 }
134
135 NTSTATUS STDCALL NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
136 IN PLARGE_INTEGER Frequency)
137 {
138 return(ZwQueryPerformanceCounter(Counter,
139 Frequency));
140 }
141
142 NTSTATUS STDCALL ZwQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
143 IN PLARGE_INTEGER Frequency)
144 {
145 UNIMPLEMENTED;
146 }
147
148
149 NTSTATUS KeAddThreadTimeout(PKTHREAD Thread, PLARGE_INTEGER Interval)
150 {
151 assert(Thread != NULL);
152 assert(Interval != NULL);
153
154 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread,Interval);
155
156 KeInitializeTimer(&(Thread->TimerBlock));
157 KeSetTimer(&(Thread->TimerBlock),*Interval,NULL);
158
159 DPRINT("Thread->TimerBlock.entry.Flink %x\n",
160 Thread->TimerBlock.entry.Flink);
161
162 return STATUS_SUCCESS;
163 }
164
165
166 NTSTATUS STDCALL NtDelayExecution(IN BOOLEAN Alertable,
167 IN TIME *Interval)
168 {
169 return(ZwDelayExecution(Alertable,Interval));
170 }
171
172 NTSTATUS STDCALL ZwDelayExecution(IN BOOLEAN Alertable,
173 IN TIME *Interval)
174 {
175 UNIMPLEMENTED;
176 }
177
178 NTSTATUS KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
179 BOOLEAN Alertable,
180 PLARGE_INTEGER Interval)
181 /*
182 * FUNCTION: Puts the current thread into an alertable or nonalertable
183 * wait state for a given internal
184 * ARGUMENTS:
185 * WaitMode = Processor mode in which the caller is waiting
186 * Altertable = Specifies if the wait is alertable
187 * Interval = Specifies the interval to wait
188 * RETURNS: Status
189 */
190 {
191 PKTHREAD CurrentThread = KeGetCurrentThread();
192 KeAddThreadTimeout(CurrentThread,Interval);
193 return(KeWaitForSingleObject(&(CurrentThread->TimerBlock),Executive,
194 KernelMode,Alertable,NULL));
195 }
196
197 VOID KeStallExecutionProcessor(ULONG MicroSeconds)
198 {
199 unsigned int i;
200 for (i=0; i<(loops_per_microsecond*MicroSeconds) ;i++)
201 {
202 __asm__("nop\n\t");
203 }
204 }
205
206 #if 0
207 static inline void ULLToLargeInteger(unsigned long long src,
208 PLARGE_INTEGER dest)
209 {
210 dest->LowPart = src & 0xffffffff;
211 dest->HighPart = (src>>32);
212 }
213
214 static inline void SLLToLargeInteger(signed long long src,
215 PLARGE_INTEGER dest)
216 {
217 if (src > 0)
218 {
219 dest->LowPart = src & 0xffffffff;
220 dest->HighPart = (src>>32);
221 }
222 else
223 {
224 src = -src;
225 dest->LowPart = src & 0xffffffff;
226 dest->HighPart = -(src>>32);
227 }
228 }
229
230 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src)
231 {
232 signed long long r;
233
234 r = src->LowPart;
235 if (src->HighPart >= 0)
236 {
237 r = r | (((unsigned long long)src->HighPart)<<32);
238 }
239 else
240 {
241 r = r | (((unsigned long long)(-(src->HighPart)))<<32);
242 r = -r;
243 }
244 return(r);
245 }
246
247 #endif
248
249
250 LARGE_INTEGER KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
251 /*
252 * FUNCTION: Queries the finest grained running count avaiable in the system
253 * ARGUMENTS:
254 * PerformanceFreq (OUT) = The routine stores the number of
255 * performance counters tick per second here
256 * RETURNS: The performance counter value in HERTZ
257 * NOTE: Returns the system tick count or the time-stamp on the pentium
258 */
259 {
260 if (PerformanceFreq != NULL)
261 {
262 LARGE_INTEGER_QUAD_PART(*PerformanceFreq) = 0;
263 }
264
265 return *PerformanceFreq;
266 }
267
268 ULONG KeQueryTimeIncrement(VOID)
269 /*
270 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
271 * the system clock every time the clock interrupts
272 * RETURNS: The increment
273 */
274 {
275 return(CLOCK_INCREMENT);
276 }
277
278 VOID KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
279 /*
280 * FUNCTION: Gets the current system time
281 * ARGUMENTS:
282 * CurrentTime (OUT) = The routine stores the current time here
283 * NOTE: The time is the number of 100-nanosecond intervals since the
284 * 1st of January, 1601.
285 */
286 {
287 LARGE_INTEGER_QUAD_PART(*CurrentTime) = system_time;
288 }
289
290 NTSTATUS STDCALL NtGetTickCount(PULONG UpTime)
291 {
292 return(ZwGetTickCount(UpTime));
293 }
294
295 NTSTATUS STDCALL ZwGetTickCount(PULONG UpTime)
296 {
297 UNIMPLEMENTED;
298 }
299
300 BOOLEAN KeSetTimer(PKTIMER Timer, LARGE_INTEGER DueTime, PKDPC Dpc)
301 /*
302 * FUNCTION: Sets the absolute or relative interval at which a timer object
303 * is to be set to the signaled state and optionally supplies a
304 * CustomTimerDpc to be executed when the timer expires.
305 * ARGUMENTS:
306 * Timer = Points to a previously initialized timer object
307 * DueTimer = If positive then absolute time to expire at
308 * If negative then the relative time to expire at
309 * Dpc = If non-NULL then a dpc to be called when the timer expires
310 * RETURNS: True if the timer was already in the system timer queue
311 * False otherwise
312 */
313 {
314 return(KeSetTimerEx(Timer,DueTime,0,Dpc));
315 }
316
317 BOOLEAN KeSetTimerEx(PKTIMER Timer, LARGE_INTEGER DueTime, LONG Period,
318 PKDPC Dpc)
319 /*
320 * FUNCTION: Sets the absolute or relative interval at which a timer object
321 * is to be set to the signaled state and optionally supplies a
322 * CustomTimerDpc to be executed when the timer expires.
323 * ARGUMENTS:
324 * Timer = Points to a previously initialized timer object
325 * DueTimer = If positive then absolute time to expire at
326 * If negative then the relative time to expire at
327 * Dpc = If non-NULL then a dpc to be called when the timer expires
328 * RETURNS: True if the timer was already in the system timer queue
329 * False otherwise
330 */
331 {
332 KIRQL oldlvl;
333
334 DPRINT("KeSetTimerEx(Timer %x)\n",Timer);
335
336 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
337
338 Timer->dpc=Dpc;
339 Timer->period=Period;
340 Timer->expire_time = (*(long long int *)&DueTime);
341 if (Timer->expire_time < 0)
342 {
343 Timer->expire_time = system_time + (-Timer->expire_time);
344 }
345 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
346 (unsigned long) (system_time & 0xffffffff),
347 (unsigned long) ((system_time >> 32) & 0xffffffff),
348 (unsigned long) (Timer->expire_time & 0xffffffff),
349 (unsigned long) ((Timer->expire_time >> 32) & 0xffffffff),
350 Timer->period);
351 Timer->signaled = FALSE;
352 if (Timer->running)
353 {
354 KeReleaseSpinLock(&timer_list_lock,oldlvl);
355 return TRUE;
356 }
357 DPRINT("Inserting %x in list\n",&Timer->entry);
358 DPRINT("Timer->entry.Flink %x\n",Timer->entry.Flink);
359 Timer->running=TRUE;
360 InsertTailList(&timer_list_head,&Timer->entry);
361 KeReleaseSpinLock(&timer_list_lock,oldlvl);
362
363 return FALSE;
364 }
365
366 BOOLEAN KeCancelTimer(PKTIMER Timer)
367 /*
368 * FUNCTION: Removes a timer from the system timer list
369 * ARGUMENTS:
370 * Timer = timer to cancel
371 * RETURNS: True if the timer was running
372 * False otherwise
373 */
374 {
375 KIRQL oldlvl;
376
377 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
378
379 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
380
381 if (!Timer->running)
382 {
383 KeReleaseSpinLock(&timer_list_lock,oldlvl);
384 return FALSE;
385 }
386 RemoveEntryList(&Timer->entry);
387 Timer->running = FALSE;
388 KeReleaseSpinLock(&timer_list_lock,oldlvl);
389
390 return TRUE;
391 }
392
393 BOOLEAN KeReadStateTimer(PKTIMER Timer)
394 {
395 return Timer->signaled;
396 }
397
398 VOID KeInitializeTimer(PKTIMER Timer)
399 /*
400 * FUNCTION: Initalizes a kernel timer object
401 * ARGUMENTS:
402 * Timer = caller supplied storage for the timer
403 * NOTE: This function initializes a notification timer
404 */
405 {
406 KeInitializeTimerEx(Timer,NotificationTimer);
407 }
408
409 VOID KeInitializeTimerEx(PKTIMER Timer, TIMER_TYPE Type)
410 /*
411 * FUNCTION: Initializes a kernel timer object
412 * ARGUMENTS:
413 * Timer = caller supplied storage for the timer
414 * Type = the type of timer (notification or synchronization)
415 * NOTE: When a notification type expires all waiting threads are released
416 * and the timer remains signalled until it is explicitly reset. When a
417 * syncrhonization timer expires its state is set to signalled until a
418 * single waiting thread is released and then the timer is reset.
419 */
420 {
421 Timer->running=FALSE;
422 Timer->type=Type;
423 Timer->signaled=FALSE;
424 }
425
426 VOID KeQueryTickCount(PLARGE_INTEGER TickCount)
427 /*
428 * FUNCTION: Returns the number of ticks since the system was booted
429 * ARGUMENTS:
430 * TickCount (OUT) = Points to storage for the number of ticks
431 */
432 {
433 LARGE_INTEGER_QUAD_PART(*TickCount) = KiTimerTicks;
434 }
435
436 static void HandleExpiredTimer(PKTIMER current)
437 {
438 DPRINT("HandleExpiredTime(current %x)\n",current);
439 if (current->dpc!=NULL)
440 {
441 DPRINT("current->dpc->DeferredRoutine %x\n",
442 current->dpc->DeferredRoutine);
443 current->dpc->DeferredRoutine(current->dpc,
444 current->dpc->DeferredContext,
445 current->dpc->SystemArgument1,
446 current->dpc->SystemArgument2);
447 DPRINT("Finished dpc routine\n");
448 }
449 current->signaled=TRUE;
450 if (current->period != 0)
451 {
452 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
453 (unsigned long) (system_time & 0xffffffff),
454 (unsigned long) ((system_time >> 32) & 0xffffffff),
455 (unsigned long) (current->expire_time & 0xffffffff),
456 (unsigned long) ((current->expire_time >> 32) & 0xffffffff),
457 current->period);
458 current->expire_time += current->period * SYSTEM_TIME_UNITS_PER_MSEC;
459 }
460 else
461 {
462 RemoveEntryList(&current->entry);
463 current->running=FALSE;
464 }
465 }
466
467 void KeExpireTimers(void)
468 {
469 PLIST_ENTRY current_entry = timer_list_head.Flink;
470 PKTIMER current = CONTAINING_RECORD(current_entry,KTIMER,entry);
471 KIRQL oldlvl;
472
473 DPRINT("KeExpireTimers()\n");
474 DPRINT("&timer_list_head %x\n",&timer_list_head);
475 DPRINT("current_entry %x\n",current_entry);
476 DPRINT("current_entry->Flink %x\n",current_entry->Flink);
477 DPRINT("current_entry->Flink->Flink %x\n",current_entry->Flink->Flink);
478
479 KeAcquireSpinLock(&timer_list_lock,&oldlvl);
480
481 while (current_entry!=(&timer_list_head))
482 {
483 if (system_time >= current->expire_time)
484 {
485 HandleExpiredTimer(current);
486 }
487
488 current_entry = current_entry->Flink;
489 current = CONTAINING_RECORD(current_entry,KTIMER,entry);
490 }
491 KeReleaseSpinLock(&timer_list_lock,oldlvl);
492 DPRINT("Finished KeExpireTimers()\n");
493 }
494
495 extern unsigned int nr_used_blocks;
496 extern unsigned int EiFreeNonPagedPool;
497 extern unsigned int EiUsedNonPagedPool;
498
499 BOOLEAN KiTimerInterrupt(VOID)
500 /*
501 * FUNCTION: Handles a timer interrupt
502 */
503 {
504 char str[36];
505 char* vidmem=(char *)physical_to_linear(0xb8000 + 160 - 36);
506 int i;
507 int x,y;
508 extern ULONG PiNrThreads;
509 extern ULONG EiNrUsedBlocks;
510
511 /*
512 * Increment the number of timers ticks
513 */
514 KiTimerTicks++;
515 system_time = system_time + CLOCK_INCREMENT;
516
517 /*
518 * Display the tick count in the top left of the screen as a debugging
519 * aid
520 */
521 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
522 if ((EiFreeNonPagedPool + EiUsedNonPagedPool) == 0)
523 {
524 x = y = 0;
525 }
526 else
527 {
528 x = (EiFreeNonPagedPool * 100) /
529 (EiFreeNonPagedPool + EiUsedNonPagedPool);
530 y = (EiUsedNonPagedPool * 100) /
531 (EiFreeNonPagedPool + EiUsedNonPagedPool);
532 }
533 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,ticks);
534 memset(str, 0, sizeof(str));
535 // sprintf(str,"%.8u %.8u",EiNrUsedBlocks,KiTimerTicks);
536 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,EiUsedNonPagedPool);
537 sprintf(str,"%.8u %.8u",PiNrThreads,KiTimerTicks);
538 for (i=0;i<17;i++)
539 {
540 *vidmem=str[i];
541 vidmem++;
542 *vidmem=0x7;
543 vidmem++;
544 }
545
546 return TRUE;
547 }
548
549
550 void InitializeTimer(void)
551 /*
552 * FUNCTION: Initializes timer irq handling
553 * NOTE: This is only called once from main()
554 */
555 {
556 InitializeListHead(&timer_list_head);
557 KeInitializeSpinLock(&timer_list_lock);
558
559 /*
560 * Calculate the starting time for the system clock
561 */
562 }