[WINSOCK]
[reactos.git] / reactos / ntoskrnl / ex / timer.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/timer.c
5 * PURPOSE: Executive Timer Implementation
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 /* Timer Object Type */
18 POBJECT_TYPE ExTimerType = NULL;
19
20 KSPIN_LOCK ExpWakeListLock;
21 LIST_ENTRY ExpWakeList;
22
23 /* Timer Mapping */
24 static GENERIC_MAPPING ExpTimerMapping =
25 {
26 STANDARD_RIGHTS_READ | TIMER_QUERY_STATE,
27 STANDARD_RIGHTS_WRITE | TIMER_MODIFY_STATE,
28 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
29 TIMER_ALL_ACCESS
30 };
31
32 /* Timer Information Classes */
33 static const INFORMATION_CLASS_INFO ExTimerInfoClass[] =
34 {
35 /* TimerBasicInformation */
36 ICI_SQ_SAME(sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY),
37 };
38
39 /* PRIVATE FUNCTIONS *********************************************************/
40
41 VOID
42 NTAPI
43 ExTimerRundown(VOID)
44 {
45 PETHREAD Thread = PsGetCurrentThread();
46 KIRQL OldIrql;
47 PLIST_ENTRY CurrentEntry;
48 PETIMER Timer;
49 ULONG DerefsToDo;
50
51 /* Lock the Thread's Active Timer List and loop it */
52 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql);
53 CurrentEntry = Thread->ActiveTimerListHead.Flink;
54 while (CurrentEntry != &Thread->ActiveTimerListHead)
55 {
56 /* Get the timer */
57 Timer = CONTAINING_RECORD(CurrentEntry, ETIMER, ActiveTimerListEntry);
58
59 /* Reference it */
60 ObReferenceObject(Timer);
61 DerefsToDo = 1;
62
63 /* Unlock the list */
64 KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql);
65
66 /* Lock the Timer */
67 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
68
69 /* Lock the list again */
70 KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
71
72 /* Make sure that the timer is valid */
73 if ((Timer->ApcAssociated) && (&Thread->Tcb == Timer->TimerApc.Thread))
74 {
75 /* Remove it from the list */
76 RemoveEntryList(&Timer->ActiveTimerListEntry);
77 Timer->ApcAssociated = FALSE;
78
79 /* Cancel the timer and remove its DPC and APC */
80 KeCancelTimer(&Timer->KeTimer);
81 KeRemoveQueueDpc(&Timer->TimerDpc);
82 if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
83
84 /* Add another dereference to do */
85 DerefsToDo++;
86 }
87
88 /* Unlock the list */
89 KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
90
91 /* Unlock the Timer */
92 KeReleaseSpinLock(&Timer->Lock, OldIrql);
93
94 /* Dereference it */
95 ObDereferenceObjectEx(Timer, DerefsToDo);
96
97 /* Loop again */
98 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql);
99 CurrentEntry = Thread->ActiveTimerListHead.Flink;
100 }
101
102 /* Release lock and return */
103 KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql);
104 }
105
106 VOID
107 NTAPI
108 ExpDeleteTimer(IN PVOID ObjectBody)
109 {
110 KIRQL OldIrql;
111 PETIMER Timer = ObjectBody;
112
113 /* Check if it has a Wait List */
114 if (Timer->WakeTimerListEntry.Flink)
115 {
116 /* Lock the Wake List */
117 KeAcquireSpinLock(&ExpWakeListLock, &OldIrql);
118
119 /* Check again, since it might've changed before we locked */
120 if (Timer->WakeTimerListEntry.Flink)
121 {
122 /* Remove it from the Wait List */
123 RemoveEntryList(&Timer->WakeTimerListEntry);
124 Timer->WakeTimerListEntry.Flink = NULL;
125 }
126
127 /* Release the Wake List */
128 KeReleaseSpinLock(&ExpWakeListLock, OldIrql);
129 }
130
131 /* Tell the Kernel to cancel the Timer and flush all queued DPCs */
132 KeCancelTimer(&Timer->KeTimer);
133 KeFlushQueuedDpcs();
134 }
135
136 VOID
137 NTAPI
138 ExpTimerDpcRoutine(IN PKDPC Dpc,
139 IN PVOID DeferredContext,
140 IN PVOID SystemArgument1,
141 IN PVOID SystemArgument2)
142 {
143 PETIMER Timer = DeferredContext;
144 BOOLEAN Inserted = FALSE;
145
146 /* Reference the timer */
147 if (!ObReferenceObjectSafe(Timer)) return;
148
149 /* Lock the Timer */
150 KeAcquireSpinLockAtDpcLevel(&Timer->Lock);
151
152 /* Check if the timer is associated */
153 if (Timer->ApcAssociated)
154 {
155 /* Queue the APC */
156 Inserted = KeInsertQueueApc(&Timer->TimerApc,
157 SystemArgument1,
158 SystemArgument2,
159 IO_NO_INCREMENT);
160 }
161
162 /* Release the Timer */
163 KeReleaseSpinLockFromDpcLevel(&Timer->Lock);
164
165 /* Dereference it if we couldn't queue the APC */
166 if (!Inserted) ObDereferenceObject(Timer);
167 }
168
169 VOID
170 NTAPI
171 ExpTimerApcKernelRoutine(IN PKAPC Apc,
172 IN OUT PKNORMAL_ROUTINE* NormalRoutine,
173 IN OUT PVOID* NormalContext,
174 IN OUT PVOID* SystemArgument1,
175 IN OUT PVOID* SystemArguemnt2)
176 {
177 PETIMER Timer;
178 KIRQL OldIrql;
179 ULONG DerefsToDo = 1;
180 PETHREAD Thread = PsGetCurrentThread();
181
182 /* We need to find out which Timer we are */
183 Timer = CONTAINING_RECORD(Apc, ETIMER, TimerApc);
184
185 /* Lock the Timer */
186 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
187
188 /* Lock the Thread's Active Timer List*/
189 KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
190
191 /* Make sure that the Timer is valid, and that it belongs to this thread */
192 if ((Timer->ApcAssociated) && (&Thread->Tcb == Timer->TimerApc.Thread))
193 {
194 /* Check if it's not periodic */
195 if (!Timer->Period)
196 {
197 /* Remove it from the Active Timers List */
198 RemoveEntryList(&Timer->ActiveTimerListEntry);
199
200 /* Disable it */
201 Timer->ApcAssociated = FALSE;
202 DerefsToDo++;
203 }
204 }
205 else
206 {
207 /* Clear the normal routine */
208 *NormalRoutine = NULL;
209 }
210
211 /* Release locks */
212 KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
213 KeReleaseSpinLock(&Timer->Lock, OldIrql);
214
215 /* Dereference as needed */
216 ObDereferenceObjectEx(Timer, DerefsToDo);
217 }
218
219 VOID
220 INIT_FUNCTION
221 NTAPI
222 ExpInitializeTimerImplementation(VOID)
223 {
224 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
225 UNICODE_STRING Name;
226
227 /* Create the Timer Object Type */
228 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
229 RtlInitUnicodeString(&Name, L"Timer");
230 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
231 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
232 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER);
233 ObjectTypeInitializer.GenericMapping = ExpTimerMapping;
234 ObjectTypeInitializer.PoolType = NonPagedPool;
235 ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS;
236 ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer;
237 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExTimerType);
238
239 /* Initialize the Wait List and Lock */
240 KeInitializeSpinLock(&ExpWakeListLock);
241 InitializeListHead(&ExpWakeList);
242 }
243
244 /* PUBLIC FUNCTIONS **********************************************************/
245
246 NTSTATUS
247 NTAPI
248 NtCancelTimer(IN HANDLE TimerHandle,
249 OUT PBOOLEAN CurrentState OPTIONAL)
250 {
251 PETIMER Timer;
252 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
253 BOOLEAN State;
254 KIRQL OldIrql;
255 PETHREAD TimerThread;
256 ULONG DerefsToDo = 1;
257 NTSTATUS Status;
258 PAGED_CODE();
259
260 /* Check if we need to probe */
261 if ((CurrentState) && (PreviousMode != KernelMode))
262 {
263 _SEH2_TRY
264 {
265 /* Make sure the pointer is valid */
266 ProbeForWriteBoolean(CurrentState);
267 }
268 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
269 {
270 /* Return the exception code */
271 _SEH2_YIELD(return _SEH2_GetExceptionCode());
272 }
273 _SEH2_END;
274 }
275
276 /* Get the Timer Object */
277 Status = ObReferenceObjectByHandle(TimerHandle,
278 TIMER_MODIFY_STATE,
279 ExTimerType,
280 PreviousMode,
281 (PVOID*)&Timer,
282 NULL);
283 if (NT_SUCCESS(Status))
284 {
285 /* Lock the Timer */
286 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
287
288 /* Check if it's enabled */
289 if (Timer->ApcAssociated)
290 {
291 /* Get the Thread. */
292 TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread,
293 ETHREAD,
294 Tcb);
295
296 /* Lock its active list */
297 KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock);
298
299 /* Remove it */
300 RemoveEntryList(&TimerThread->ActiveTimerListHead);
301 Timer->ApcAssociated = FALSE;
302
303 /* Unlock the list */
304 KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock);
305
306 /* Cancel the Timer */
307 KeCancelTimer(&Timer->KeTimer);
308 KeRemoveQueueDpc(&Timer->TimerDpc);
309 if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
310 DerefsToDo++;
311 }
312 else
313 {
314 /* If timer was disabled, we still need to cancel it */
315 KeCancelTimer(&Timer->KeTimer);
316 }
317
318 /* Handle a Wake Timer */
319 if (Timer->WakeTimerListEntry.Flink)
320 {
321 /* Lock the Wake List */
322 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock);
323
324 /* Check again, since it might've changed before we locked */
325 if (Timer->WakeTimerListEntry.Flink)
326 {
327 /* Remove it from the Wait List */
328 RemoveEntryList(&Timer->WakeTimerListEntry);
329 Timer->WakeTimerListEntry.Flink = NULL;
330 }
331
332 /* Release the Wake List */
333 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock);
334 }
335
336 /* Unlock the Timer */
337 KeReleaseSpinLock(&Timer->Lock, OldIrql);
338
339 /* Read the old State */
340 State = KeReadStateTimer(&Timer->KeTimer);
341
342 /* Dereference the Object */
343 ObDereferenceObjectEx(Timer, DerefsToDo);
344
345 /* Check if caller wants the state */
346 if (CurrentState)
347 {
348 _SEH2_TRY
349 {
350 /* Return the Timer State */
351 *CurrentState = State;
352 }
353 _SEH2_EXCEPT(ExSystemExceptionFilter())
354 {
355
356 }
357 _SEH2_END;
358 }
359 }
360
361 /* Return to Caller */
362 return Status;
363 }
364
365 NTSTATUS
366 NTAPI
367 NtCreateTimer(OUT PHANDLE TimerHandle,
368 IN ACCESS_MASK DesiredAccess,
369 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
370 IN TIMER_TYPE TimerType)
371 {
372 PETIMER Timer;
373 HANDLE hTimer;
374 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
375 NTSTATUS Status;
376 PAGED_CODE();
377
378 /* Check for correct timer type */
379 if ((TimerType != NotificationTimer) &&
380 (TimerType != SynchronizationTimer))
381 {
382 /* Fail */
383 return STATUS_INVALID_PARAMETER_4;
384 }
385
386 /* Check if we need to probe */
387 if (PreviousMode != KernelMode)
388 {
389 _SEH2_TRY
390 {
391 /* Make sure the pointer is valid */
392 ProbeForWriteHandle(TimerHandle);
393 }
394 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
395 {
396 /* Return the exception code */
397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
398 }
399 _SEH2_END;
400 }
401
402 /* Create the Object */
403 Status = ObCreateObject(PreviousMode,
404 ExTimerType,
405 ObjectAttributes,
406 PreviousMode,
407 NULL,
408 sizeof(ETIMER),
409 0,
410 0,
411 (PVOID*)&Timer);
412 if (NT_SUCCESS(Status))
413 {
414 /* Initialize the DPC */
415 KeInitializeDpc(&Timer->TimerDpc, ExpTimerDpcRoutine, Timer);
416
417 /* Initialize the Kernel Timer */
418 KeInitializeTimerEx(&Timer->KeTimer, TimerType);
419
420 /* Initialize the timer fields */
421 KeInitializeSpinLock(&Timer->Lock);
422 Timer->ApcAssociated = FALSE;
423 Timer->WakeTimer = FALSE;
424 Timer->WakeTimerListEntry.Flink = NULL;
425
426 /* Insert the Timer */
427 Status = ObInsertObject((PVOID)Timer,
428 NULL,
429 DesiredAccess,
430 0,
431 NULL,
432 &hTimer);
433
434 /* Check for success */
435 if (NT_SUCCESS(Status))
436 {
437 /* Enter SEH */
438 _SEH2_TRY
439 {
440 /* Return the Timer Handle */
441 *TimerHandle = hTimer;
442 }
443 _SEH2_EXCEPT(ExSystemExceptionFilter())
444 {
445
446 }
447 _SEH2_END;
448 }
449 }
450
451 /* Return to Caller */
452 return Status;
453 }
454
455 NTSTATUS
456 NTAPI
457 NtOpenTimer(OUT PHANDLE TimerHandle,
458 IN ACCESS_MASK DesiredAccess,
459 IN POBJECT_ATTRIBUTES ObjectAttributes)
460 {
461 HANDLE hTimer;
462 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
463 NTSTATUS Status;
464 PAGED_CODE();
465
466 /* Check Parameter Validity */
467 if (PreviousMode != KernelMode)
468 {
469 _SEH2_TRY
470 {
471 /* Make sure the pointer is valid */
472 ProbeForWriteHandle(TimerHandle);
473 }
474 _SEH2_EXCEPT(ExSystemExceptionFilter())
475 {
476 /* Return the exception code */
477 _SEH2_YIELD(return _SEH2_GetExceptionCode());
478 }
479 _SEH2_END;
480 }
481
482 /* Open the Timer */
483 Status = ObOpenObjectByName(ObjectAttributes,
484 ExTimerType,
485 PreviousMode,
486 NULL,
487 DesiredAccess,
488 NULL,
489 &hTimer);
490 if (NT_SUCCESS(Status))
491 {
492 /* Enter SEH */
493 _SEH2_TRY
494 {
495 /* Return the Timer Handle */
496 *TimerHandle = hTimer;
497 }
498 _SEH2_EXCEPT(ExSystemExceptionFilter())
499 {
500
501 }
502 _SEH2_END;
503 }
504
505 /* Return to Caller */
506 return Status;
507 }
508
509 NTSTATUS
510 NTAPI
511 NtQueryTimer(IN HANDLE TimerHandle,
512 IN TIMER_INFORMATION_CLASS TimerInformationClass,
513 OUT PVOID TimerInformation,
514 IN ULONG TimerInformationLength,
515 OUT PULONG ReturnLength OPTIONAL)
516 {
517 PETIMER Timer;
518 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
519 NTSTATUS Status;
520 PTIMER_BASIC_INFORMATION BasicInfo = TimerInformation;
521 PAGED_CODE();
522
523 /* Check Validity */
524 Status = DefaultQueryInfoBufferCheck(TimerInformationClass,
525 ExTimerInfoClass,
526 sizeof(ExTimerInfoClass) /
527 sizeof(ExTimerInfoClass[0]),
528 TimerInformation,
529 TimerInformationLength,
530 ReturnLength,
531 NULL,
532 PreviousMode);
533 if (!NT_SUCCESS(Status)) return Status;
534
535 /* Get the Timer Object */
536 Status = ObReferenceObjectByHandle(TimerHandle,
537 TIMER_QUERY_STATE,
538 ExTimerType,
539 PreviousMode,
540 (PVOID*)&Timer,
541 NULL);
542 if (NT_SUCCESS(Status))
543 {
544 /* Return the Basic Information */
545 _SEH2_TRY
546 {
547 /* Return the remaining time, corrected */
548 BasicInfo->TimeRemaining.QuadPart = Timer->
549 KeTimer.DueTime.QuadPart -
550 KeQueryInterruptTime();
551
552 /* Return the current state */
553 BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer);
554
555 /* Return the buffer length if requested */
556 if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
557 }
558 _SEH2_EXCEPT(ExSystemExceptionFilter())
559 {
560 /* Get the exception code */
561 Status = _SEH2_GetExceptionCode();
562 }
563 _SEH2_END;
564
565 /* Dereference Object */
566 ObDereferenceObject(Timer);
567 }
568
569 /* Return Status */
570 return Status;
571 }
572
573 NTSTATUS
574 NTAPI
575 NtSetTimer(IN HANDLE TimerHandle,
576 IN PLARGE_INTEGER DueTime,
577 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
578 IN PVOID TimerContext OPTIONAL,
579 IN BOOLEAN WakeTimer,
580 IN LONG Period OPTIONAL,
581 OUT PBOOLEAN PreviousState OPTIONAL)
582 {
583 PETIMER Timer;
584 KIRQL OldIrql;
585 BOOLEAN State;
586 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
587 PETHREAD Thread = PsGetCurrentThread();
588 LARGE_INTEGER TimerDueTime;
589 PETHREAD TimerThread;
590 ULONG DerefsToDo = 1;
591 NTSTATUS Status = STATUS_SUCCESS;
592 PAGED_CODE();
593
594 /* Check for a valid Period */
595 if (Period < 0) return STATUS_INVALID_PARAMETER_6;
596
597 /* Check if we need to probe */
598 if (PreviousMode != KernelMode)
599 {
600 _SEH2_TRY
601 {
602 /* Probe and capture the due time */
603 TimerDueTime = ProbeForReadLargeInteger(DueTime);
604
605 /* Probe the state pointer if one was passed */
606 if (PreviousState) ProbeForWriteBoolean(PreviousState);
607 }
608 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
609 {
610 /* Return the exception code */
611 _SEH2_YIELD(return _SEH2_GetExceptionCode());
612 }
613 _SEH2_END;
614 }
615 else
616 {
617 /* Capture the time directly */
618 TimerDueTime = *DueTime;
619 }
620
621 /* Get the Timer Object */
622 Status = ObReferenceObjectByHandle(TimerHandle,
623 TIMER_MODIFY_STATE,
624 ExTimerType,
625 PreviousMode,
626 (PVOID*)&Timer,
627 NULL);
628
629 /*
630 * Tell the user we don't support Wake Timers...
631 * when we have the ability to use/detect the Power Management
632 * functionality required to support them, make this check dependent
633 * on the actual PM capabilities
634 */
635 if (WakeTimer) Status = STATUS_TIMER_RESUME_IGNORED;
636
637 /* Check status */
638 if (NT_SUCCESS(Status))
639 {
640 /* Lock the Timer */
641 KeAcquireSpinLock(&Timer->Lock, &OldIrql);
642
643 /* Cancel Running Timer */
644 if (Timer->ApcAssociated)
645 {
646 /* Get the Thread. */
647 TimerThread = CONTAINING_RECORD(Timer->TimerApc.Thread,
648 ETHREAD,
649 Tcb);
650
651 /* Lock its active list */
652 KeAcquireSpinLockAtDpcLevel(&TimerThread->ActiveTimerListLock);
653
654 /* Remove it */
655 RemoveEntryList(&TimerThread->ActiveTimerListHead);
656 Timer->ApcAssociated = FALSE;
657
658 /* Unlock the list */
659 KeReleaseSpinLockFromDpcLevel(&TimerThread->ActiveTimerListLock);
660
661 /* Cancel the Timer */
662 KeCancelTimer(&Timer->KeTimer);
663 KeRemoveQueueDpc(&Timer->TimerDpc);
664 if (KeRemoveQueueApc(&Timer->TimerApc)) DerefsToDo++;
665 DerefsToDo++;
666 }
667 else
668 {
669 /* If timer was disabled, we still need to cancel it */
670 KeCancelTimer(&Timer->KeTimer);
671 }
672
673 /* Read the State */
674 State = KeReadStateTimer(&Timer->KeTimer);
675
676 /* Handle Wake Timers */
677 Timer->WakeTimer = WakeTimer;
678 KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock);
679 if ((WakeTimer) && !(Timer->WakeTimerListEntry.Flink))
680 {
681 /* Insert it into the list */
682 InsertTailList(&ExpWakeList, &Timer->WakeTimerListEntry);
683 }
684 else if (!(WakeTimer) && (Timer->WakeTimerListEntry.Flink))
685 {
686 /* Remove it from the list */
687 RemoveEntryList(&Timer->WakeTimerListEntry);
688 Timer->WakeTimerListEntry.Flink = NULL;
689 }
690 KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock);
691
692 /* Set up the APC Routine if specified */
693 Timer->Period = Period;
694 if (TimerApcRoutine)
695 {
696 /* Initialize the APC */
697 KeInitializeApc(&Timer->TimerApc,
698 &Thread->Tcb,
699 CurrentApcEnvironment,
700 ExpTimerApcKernelRoutine,
701 (PKRUNDOWN_ROUTINE)NULL,
702 (PKNORMAL_ROUTINE)TimerApcRoutine,
703 PreviousMode,
704 TimerContext);
705
706 /* Lock the Thread's Active List and Insert */
707 KeAcquireSpinLockAtDpcLevel(&Thread->ActiveTimerListLock);
708 InsertTailList(&Thread->ActiveTimerListHead,
709 &Timer->ActiveTimerListEntry);
710 Timer->ApcAssociated = TRUE;
711 KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock);
712
713 /* One less dereference to do */
714 DerefsToDo--;
715 }
716
717 /* Enable and Set the Timer */
718 KeSetTimerEx(&Timer->KeTimer,
719 TimerDueTime,
720 Period,
721 TimerApcRoutine ? &Timer->TimerDpc : NULL);
722
723 /* Unlock the Timer */
724 KeReleaseSpinLock(&Timer->Lock, OldIrql);
725
726 /* Dereference if it was previously enabled */
727 if (DerefsToDo) ObDereferenceObjectEx(Timer, DerefsToDo);
728
729 /* Check if we need to return the State */
730 if (PreviousState)
731 {
732 /* Enter SEH */
733 _SEH2_TRY
734 {
735 /* Return the Timer State */
736 *PreviousState = State;
737 }
738 _SEH2_EXCEPT(ExSystemExceptionFilter())
739 {
740
741 }
742 _SEH2_END;
743 }
744 }
745
746 /* Return to Caller */
747 return Status;
748 }