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