123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- utimer.c
- Abstract:
- This module implements user mode timer support.
- Author:
- Evan Green 11-Aug-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include "psp.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define PROCESS_TIMER_ALLOCATION_TAG 0x6D547350 // 'mTsP'
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure defines a user mode timer.
- Members:
- ListEntry - Stores pointers to the next and previous timers in the process
- list.
- ReferenceCount - Stores the reference count on the timer.
- Process - Stores a pointer to the process that owns this timer.
- Thread - Stores an optional pointer to the thread that is to be signaled
- when the timer expires. If NULL, then the process is signaled.
- TimerNumber - Stores the timer's identifying number.
- DueTime - Stores the due time of the timer.
- Interval - Stores the periodic interval of the timer.
- ExpirationCount - Stores the number of timer expirations that have occurred
- since the last work item ran.
- OverflowCount - Stores the number of overflows that have occurred since the
- last time the caller asked.
- Timer - Stores a pointer to the timer backing this user mode timer.
- Dpc - Stores a pointer to the DPC that runs when the timer fires.
- WorkItem - Stores a pointer to the work item that's queued when the DPC
- runs.
- SignalQueueEntry - Stores the signal queue entry that gets queued when the
- timer expires.
- --*/
- typedef struct _PROCESS_TIMER {
- LIST_ENTRY ListEntry;
- ULONG ReferenceCount;
- PKPROCESS Process;
- PKTHREAD Thread;
- LONG TimerNumber;
- ULONGLONG DueTime;
- ULONGLONG Interval;
- ULONG ExpirationCount;
- ULONG OverflowCount;
- PKTIMER Timer;
- PDPC Dpc;
- PWORK_ITEM WorkItem;
- SIGNAL_QUEUE_ENTRY SignalQueueEntry;
- } PROCESS_TIMER, *PPROCESS_TIMER;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- PspCreateTimer (
- PKPROCESS Process,
- PKTHREAD Thread,
- PPROCESS_TIMER *Timer
- );
- KSTATUS
- PspSetTimer (
- PKPROCESS Process,
- PPROCESS_TIMER Timer,
- PULONGLONG DueTime,
- PULONGLONG Period
- );
- KSTATUS
- PspCreateProcessTimer (
- PKPROCESS Process,
- PKTHREAD Thread,
- PPROCESS_TIMER *Timer
- );
- VOID
- PspProcessTimerAddReference (
- PPROCESS_TIMER Timer
- );
- VOID
- PspProcessTimerReleaseReference (
- PPROCESS_TIMER Timer
- );
- VOID
- PspDestroyProcessTimer (
- PPROCESS_TIMER Timer
- );
- VOID
- PspFlushProcessTimer (
- PKPROCESS Process,
- PPROCESS_TIMER Timer
- );
- VOID
- PspProcessTimerDpcRoutine (
- PDPC Dpc
- );
- VOID
- PspProcessTimerWorkRoutine (
- PVOID Parameter
- );
- VOID
- PspProcessTimerSignalCompletion (
- PSIGNAL_QUEUE_ENTRY SignalQueueEntry
- );
- VOID
- PspExpireRuntimeTimer (
- PKTHREAD Thread,
- PRUNTIME_TIMER Timer,
- ULONG Signal,
- ULONGLONG CurrentTime
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- INTN
- PsSysQueryTimeCounter (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine implements the system call for getting the current time
- counter value.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- {
- PSYSTEM_CALL_QUERY_TIME_COUNTER Parameters;
- Parameters = (PSYSTEM_CALL_QUERY_TIME_COUNTER)SystemCallParameter;
- Parameters->Value = HlQueryTimeCounter();
- return STATUS_SUCCESS;
- }
- INTN
- PsSysTimerControl (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine performs timer control operations.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PPROCESS_TIMER CurrentTimer;
- BOOL LockHeld;
- PSYSTEM_CALL_TIMER_CONTROL Parameters;
- PKPROCESS Process;
- KSTATUS Status;
- PKTHREAD Thread;
- PPROCESS_TIMER Timer;
- LockHeld = FALSE;
- Parameters = (PSYSTEM_CALL_TIMER_CONTROL)SystemCallParameter;
- Process = PsGetCurrentProcess();
- ASSERT(Process != PsGetKernelProcess());
- Timer = NULL;
- Thread = NULL;
- //
- // If it's not a create operation, find the timer being referenced.
- //
- if (Parameters->Operation != TimerOperationCreateTimer) {
- KeAcquireQueuedLock(Process->QueuedLock);
- LockHeld = TRUE;
- CurrentEntry = Process->TimerList.Next;
- while (CurrentEntry != &(Process->TimerList)) {
- CurrentTimer = LIST_VALUE(CurrentEntry, PROCESS_TIMER, ListEntry);
- if (CurrentTimer->TimerNumber == Parameters->TimerNumber) {
- Timer = CurrentTimer;
- break;
- }
- CurrentEntry = CurrentEntry->Next;
- }
- if (Timer == NULL) {
- Status = STATUS_INVALID_HANDLE;
- goto SysTimerControlEnd;
- }
- }
- Status = STATUS_SUCCESS;
- switch (Parameters->Operation) {
- //
- // Create a new processs timer and add it to the list.
- //
- case TimerOperationCreateTimer:
- //
- // If a thread is to be signaled, validate that the thread belongs to
- // the current process.
- //
- if ((Parameters->Flags & TIMER_CONTROL_FLAG_SIGNAL_THREAD) != 0) {
- Thread = PspGetThreadById(Process, Parameters->ThreadId);
- if (Thread == NULL) {
- Status = STATUS_INVALID_PARAMETER;
- goto SysTimerControlEnd;
- }
- }
- Status = PspCreateTimer(Process, Thread, &Timer);
- if (!KSUCCESS(Status)) {
- goto SysTimerControlEnd;
- }
- Timer->SignalQueueEntry.Parameters.SignalNumber =
- Parameters->SignalNumber;
- Timer->SignalQueueEntry.Parameters.SignalCode = SIGNAL_CODE_TIMER;
- if ((Parameters->Flags & TIMER_CONTROL_FLAG_USE_TIMER_NUMBER) != 0) {
- Timer->SignalQueueEntry.Parameters.Parameter = Timer->TimerNumber;
- } else {
- Timer->SignalQueueEntry.Parameters.Parameter =
- Parameters->SignalValue;
- }
- Parameters->TimerNumber = Timer->TimerNumber;
- break;
- //
- // Delete an existing process timer.
- //
- case TimerOperationDeleteTimer:
- LIST_REMOVE(&(Timer->ListEntry));
- KeReleaseQueuedLock(Process->QueuedLock);
- LockHeld = FALSE;
- PspFlushProcessTimer(Process, Timer);
- PspProcessTimerReleaseReference(Timer);
- break;
- //
- // Get timer information, including the next due time and overflow count.
- //
- case TimerOperationGetTimer:
- Parameters->TimerInformation.DueTime = KeGetTimerDueTime(Timer->Timer);
- Parameters->TimerInformation.Period = Timer->Interval;
- Parameters->TimerInformation.OverflowCount = Timer->OverflowCount;
- break;
- //
- // Arm or disarm the timer. Save and return the original information.
- //
- case TimerOperationSetTimer:
- Parameters->TimerInformation.OverflowCount = 0;
- Status = PspSetTimer(Process,
- Timer,
- &(Parameters->TimerInformation.DueTime),
- &(Parameters->TimerInformation.Period));
- if (!KSUCCESS(Status)) {
- goto SysTimerControlEnd;
- }
- break;
- default:
- ASSERT(FALSE);
- Status = STATUS_INVALID_PARAMETER;
- goto SysTimerControlEnd;
- }
- SysTimerControlEnd:
- if (LockHeld != FALSE) {
- KeReleaseQueuedLock(Process->QueuedLock);
- }
- if (Thread != NULL) {
- ObReleaseReference(Thread);
- }
- return Status;
- }
- INTN
- PsSysSetITimer (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine performs gets or sets a thread interval timer.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- {
- ULONGLONG CurrentCycles;
- ULONGLONG CurrentTime;
- ULONGLONG DueTime;
- ULONGLONG OldPeriod;
- PKPROCESS Process;
- PPROCESS_TIMER RealTimer;
- PSYSTEM_CALL_SET_ITIMER Request;
- KSTATUS Status;
- PKTHREAD Thread;
- RESOURCE_USAGE Usage;
- PRUNTIME_TIMER UserTimer;
- Thread = KeGetCurrentThread();
- Process = Thread->OwningProcess;
- Request = SystemCallParameter;
- if (Request->Type >= ITimerTypeCount) {
- Status = STATUS_INVALID_PARAMETER;
- goto SysSetITimerEnd;
- }
- if (Request->Set == FALSE) {
- switch (Request->Type) {
- case ITimerReal:
- RealTimer = Thread->RealTimer;
- if (RealTimer == NULL) {
- Request->DueTime = 0;
- Request->Period = 0;
- break;
- }
- Request->DueTime = KeGetTimerDueTime(RealTimer->Timer);
- CurrentTime = HlQueryTimeCounter();
- if (Request->DueTime > CurrentTime) {
- Request->DueTime -= CurrentTime;
- } else {
- Request->DueTime = 0;
- }
- Request->Period = RealTimer->Interval;
- break;
- case ITimerVirtual:
- case ITimerProfile:
- PspGetThreadResourceUsage(Thread, &Usage);
- UserTimer = &(Thread->UserTimer);
- CurrentCycles = Usage.UserCycles;
- if (Request->Type == ITimerProfile) {
- CurrentCycles += Usage.KernelCycles;
- UserTimer = &(Thread->ProfileTimer);
- }
- Request->Period = UserTimer->Period;
- Request->DueTime = UserTimer->DueTime;
- if (Request->DueTime > CurrentCycles) {
- Request->DueTime -= CurrentCycles;
- } else {
- Request->DueTime = 0;
- }
- break;
- default:
- ASSERT(FALSE);
- break;
- }
- Status = STATUS_SUCCESS;
- goto SysSetITimerEnd;
- }
- //
- // This is a set timer request.
- //
- switch (Request->Type) {
- case ITimerReal:
- if (Thread->RealTimer == NULL) {
- Status = PspCreateTimer(Process, NULL, &RealTimer);
- if (!KSUCCESS(Status)) {
- goto SysSetITimerEnd;
- }
- RealTimer->SignalQueueEntry.Parameters.SignalNumber = SIGNAL_TIMER;
- RealTimer->SignalQueueEntry.Parameters.SignalCode =
- SIGNAL_CODE_TIMER;
- Thread->RealTimer = RealTimer;
- }
- //
- // Set the new real timer. The due time in the request is always
- // relative, so convert it to absolute and back.
- //
- CurrentTime = HlQueryTimeCounter();
- KeAcquireQueuedLock(Process->QueuedLock);
- DueTime = Request->DueTime;
- if (DueTime != 0) {
- DueTime += CurrentTime;
- }
- Status = PspSetTimer(Process,
- Thread->RealTimer,
- &DueTime,
- &(Request->Period));
- KeReleaseQueuedLock(Process->QueuedLock);
- if (!KSUCCESS(Status)) {
- goto SysSetITimerEnd;
- }
- if (DueTime > CurrentTime) {
- DueTime -= CurrentTime;
- } else {
- DueTime = 0;
- }
- break;
- case ITimerVirtual:
- case ITimerProfile:
- PspGetThreadResourceUsage(Thread, &Usage);
- UserTimer = &(Thread->UserTimer);
- CurrentCycles = Usage.UserCycles;
- if (Request->Type == ITimerProfile) {
- CurrentCycles += Usage.KernelCycles;
- UserTimer = &(Thread->ProfileTimer);
- }
- DueTime = Request->DueTime;
- if (DueTime != 0) {
- DueTime += CurrentCycles;
- }
- OldPeriod = UserTimer->Period;
- Request->DueTime = UserTimer->Period;
- if (Request->DueTime > CurrentCycles) {
- Request->DueTime -= CurrentCycles;
- } else {
- Request->DueTime = 0;
- }
- UserTimer->DueTime = DueTime;
- UserTimer->Period = Request->Period;
- Request->Period = OldPeriod;
- break;
- default:
- ASSERT(FALSE);
- Status = STATUS_INVALID_PARAMETER;
- goto SysSetITimerEnd;
- }
- Status = STATUS_SUCCESS;
- SysSetITimerEnd:
- return Status;
- }
- VOID
- PsEvaluateRuntimeTimers (
- PKTHREAD Thread
- )
- /*++
- Routine Description:
- This routine checks the runtime timers for expiration on the current thread.
- Arguments:
- Thread - Supplies a pointer to the current thread.
- Return Value:
- None.
- --*/
- {
- ULONGLONG CurrentCycles;
- RESOURCE_USAGE Usage;
- //
- // If they're both zero, return.
- //
- if ((Thread->UserTimer.DueTime | Thread->ProfileTimer.DueTime) == 0) {
- return;
- }
- //
- // Potentially expire the user timer. This read can never tear since user
- // mode can't sneak in and run a bit more.
- //
- if ((Thread->UserTimer.DueTime != 0) &&
- (Thread->ResourceUsage.UserCycles >= Thread->UserTimer.DueTime)) {
- PspExpireRuntimeTimer(Thread,
- &(Thread->UserTimer),
- SIGNAL_EXECUTION_TIMER_EXPIRED,
- Thread->ResourceUsage.UserCycles);
- }
- //
- // Potentially expire the profiling timer. The kernel time might tear, so
- // do a torn read and if it succeeds, do a legit read. If the torn read
- // results in a false negative then the timer will be a little late, but
- // will expire on the next check.
- //
- if ((Thread->ProfileTimer.DueTime != 0) &&
- ((Thread->ResourceUsage.UserCycles +
- Thread->ResourceUsage.KernelCycles) >=
- Thread->ProfileTimer.DueTime)) {
- PspGetThreadResourceUsage(Thread, &Usage);
- CurrentCycles = Usage.UserCycles + Usage.KernelCycles;
- if (CurrentCycles >= Thread->ProfileTimer.DueTime) {
- PspExpireRuntimeTimer(Thread,
- &(Thread->ProfileTimer),
- SIGNAL_PROFILE_TIMER,
- CurrentCycles);
- }
- }
- return;
- }
- VOID
- PspDestroyProcessTimers (
- PKPROCESS Process
- )
- /*++
- Routine Description:
- This routine cleans up any timers a process may have. This routine assumes
- the process lock is already held.
- Arguments:
- Process - Supplies a pointer to the process.
- Return Value:
- None.
- --*/
- {
- PPROCESS_TIMER Timer;
- while (LIST_EMPTY(&(Process->TimerList)) == FALSE) {
- Timer = LIST_VALUE(Process->TimerList.Next, PROCESS_TIMER, ListEntry);
- LIST_REMOVE(&(Timer->ListEntry));
- //
- // Cancel the timer and flush the DPC to ensure that the reference
- // count is up to date. Then release the reference. This will either
- // clean up the object right away or the work item will run on its
- // own time.
- //
- KeCancelTimer(Timer->Timer);
- if (!KSUCCESS(KeCancelDpc(Timer->Dpc))) {
- KeFlushDpc(Timer->Dpc);
- }
- PspProcessTimerReleaseReference(Timer);
- }
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- PspCreateTimer (
- PKPROCESS Process,
- PKTHREAD Thread,
- PPROCESS_TIMER *Timer
- )
- /*++
- Routine Description:
- This routine attempts to create and add a new process timer.
- Arguments:
- Process - Supplies a pointer to the process that owns the timer.
- Thread - Supplies an optional pointer to the thread to be signaled when the
- timer expires.
- Timer - Supplies a pointer where a pointer to the new timer is returned on
- success.
- Return Value:
- Status code.
- --*/
- {
- PPROCESS_TIMER PreviousTimer;
- PPROCESS_TIMER ProcessTimer;
- KSTATUS Status;
- Status = PspCreateProcessTimer(Process, Thread, &ProcessTimer);
- if (!KSUCCESS(Status)) {
- goto CreateTimerEnd;
- }
- ProcessTimer->SignalQueueEntry.Parameters.SignalCode = SIGNAL_CODE_TIMER;
- //
- // Insert this timer in the process. Assign the timer the ID of the
- // last timer in the list plus one.
- //
- KeAcquireQueuedLock(Process->QueuedLock);
- if (LIST_EMPTY(&(Process->TimerList)) != FALSE) {
- ProcessTimer->TimerNumber = 1;
- } else {
- PreviousTimer = LIST_VALUE(Process->TimerList.Previous,
- PROCESS_TIMER,
- ListEntry);
- ProcessTimer->TimerNumber = PreviousTimer->TimerNumber + 1;
- }
- INSERT_BEFORE(&(ProcessTimer->ListEntry), &(Process->TimerList));
- KeReleaseQueuedLock(Process->QueuedLock);
- CreateTimerEnd:
- *Timer = ProcessTimer;
- return Status;
- }
- KSTATUS
- PspSetTimer (
- PKPROCESS Process,
- PPROCESS_TIMER Timer,
- PULONGLONG DueTime,
- PULONGLONG Period
- )
- /*++
- Routine Description:
- This routine attempts to arm a process timer. This routien assumes the
- process lock is already held.
- Arguments:
- Process - Supplies a pointer to the process that owns the timer.
- Timer - Supplies a pointer where a pointer to the new timer is returned on
- success.
- DueTime - Supplies the new due time in time counter ticks. Returns the
- previous due time.
- Period - Supplies the new interval in time counter ticks. Returns the
- previous interval.
- Return Value:
- Status code.
- --*/
- {
- ULONGLONG OriginalDueTime;
- ULONGLONG OriginalPeriod;
- KSTATUS Status;
- ASSERT(KeIsQueuedLockHeld(Process->QueuedLock) != FALSE);
- OriginalDueTime = KeGetTimerDueTime(Timer->Timer);
- OriginalPeriod = Timer->Interval;
- if (Timer->DueTime != 0) {
- KeCancelTimer(Timer->Timer);
- }
- Timer->DueTime = *DueTime;
- Timer->Interval = *Period;
- if (Timer->DueTime != 0) {
- Status = KeQueueTimer(Timer->Timer,
- TimerQueueSoftWake,
- Timer->DueTime,
- Timer->Interval,
- 0,
- Timer->Dpc);
- if (!KSUCCESS(Status)) {
- goto SetTimerEnd;
- }
- }
- Status = STATUS_SUCCESS;
- SetTimerEnd:
- *DueTime = OriginalDueTime;
- *Period = OriginalPeriod;
- return Status;
- }
- KSTATUS
- PspCreateProcessTimer (
- PKPROCESS Process,
- PKTHREAD Thread,
- PPROCESS_TIMER *Timer
- )
- /*++
- Routine Description:
- This routine attempts to create a new process timer.
- Arguments:
- Process - Supplies a pointer to the process that owns the timer.
- Thread - Supplies an optional pointer to the thread to be signaled when the
- timer expires.
- Timer - Supplies a pointer where a pointer to the new timer is returned on
- success.
- Return Value:
- STATUS_SUCCESS always.
- --*/
- {
- PPROCESS_TIMER NewTimer;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- NewTimer = MmAllocateNonPagedPool(sizeof(PROCESS_TIMER),
- PROCESS_TIMER_ALLOCATION_TAG);
- if (NewTimer == NULL) {
- goto CreateProcessTimerEnd;
- }
- RtlZeroMemory(NewTimer, sizeof(PROCESS_TIMER));
- NewTimer->Process = Process;
- NewTimer->Thread = Thread;
- NewTimer->ReferenceCount = 1;
- NewTimer->Timer = KeCreateTimer(PROCESS_TIMER_ALLOCATION_TAG);
- if (NewTimer->Timer == NULL) {
- goto CreateProcessTimerEnd;
- }
- NewTimer->Dpc = KeCreateDpc(PspProcessTimerDpcRoutine, NewTimer);
- if (NewTimer->Dpc == NULL) {
- goto CreateProcessTimerEnd;
- }
- NewTimer->WorkItem = KeCreateWorkItem(NULL,
- WorkPriorityNormal,
- PspProcessTimerWorkRoutine,
- NewTimer,
- PROCESS_TIMER_ALLOCATION_TAG);
- if (NewTimer->WorkItem == NULL) {
- goto CreateProcessTimerEnd;
- }
- NewTimer->SignalQueueEntry.CompletionRoutine =
- PspProcessTimerSignalCompletion;
- //
- // Take a reference on the process to avoid a situation where the
- // process is destroyed before the work item gets around to running. Do the
- // same for the thread if it is present.
- //
- ObAddReference(Process);
- if (Thread != NULL) {
- ObAddReference(Thread);
- }
- Status = STATUS_SUCCESS;
- CreateProcessTimerEnd:
- if (!KSUCCESS(Status)) {
- if (NewTimer != NULL) {
- if (NewTimer->Timer != NULL) {
- KeDestroyTimer(NewTimer->Timer);
- }
- if (NewTimer->Dpc != NULL) {
- KeDestroyDpc(NewTimer->Dpc);
- }
- if (NewTimer->WorkItem != NULL) {
- KeDestroyWorkItem(NewTimer->WorkItem);
- }
- MmFreeNonPagedPool(NewTimer);
- NewTimer = NULL;
- }
- }
- *Timer = NewTimer;
- return Status;
- }
- VOID
- PspProcessTimerAddReference (
- PPROCESS_TIMER Timer
- )
- /*++
- Routine Description:
- This routine adds a reference to a process timer.
- Arguments:
- Timer - Supplies a pointer to the timer.
- Return Value:
- None.
- --*/
- {
- RtlAtomicAdd32(&(Timer->ReferenceCount), 1);
- return;
- }
- VOID
- PspProcessTimerReleaseReference (
- PPROCESS_TIMER Timer
- )
- /*++
- Routine Description:
- This routine releases a reference on a process timer.
- Arguments:
- Timer - Supplies a pointer to the timer.
- Return Value:
- None.
- --*/
- {
- if (RtlAtomicAdd32(&(Timer->ReferenceCount), -1) == 1) {
- PspDestroyProcessTimer(Timer);
- }
- return;
- }
- VOID
- PspDestroyProcessTimer (
- PPROCESS_TIMER Timer
- )
- /*++
- Routine Description:
- This routine destroys a process timer.
- Arguments:
- Timer - Supplies a pointer to the timer to destroy.
- Return Value:
- None.
- --*/
- {
- KeDestroyTimer(Timer->Timer);
- KeDestroyDpc(Timer->Dpc);
- KeDestroyWorkItem(Timer->WorkItem);
- ObReleaseReference(Timer->Process);
- if (Timer->Thread != NULL) {
- ObReleaseReference(Timer->Thread);
- }
- MmFreeNonPagedPool(Timer);
- return;
- }
- VOID
- PspFlushProcessTimer (
- PKPROCESS Process,
- PPROCESS_TIMER Timer
- )
- /*++
- Routine Description:
- This routine flushes a process timer to the point where the reference
- count is prepared for anyone about to release a reference, and the signal
- is either queued or cancelled.
- Arguments:
- Process - Supplies a pointer to the process that owns the timer.
- Timer - Supplies a pointer to the timer to cancel/flush.
- Return Value:
- None.
- --*/
- {
- //
- // After the timer's cancelled, the DPC is queued or it isn't going to be.
- //
- KeCancelTimer(Timer->Timer);
- //
- // Cancelling or flushing the DPC means that either the work item is queued
- // or isn't going to be.
- //
- if (!KSUCCESS(KeCancelDpc(Timer->Dpc))) {
- KeFlushDpc(Timer->Dpc);
- }
- //
- // After the work queue's flushed, either the signal is queued or it isn't
- // going to be.
- //
- KeFlushWorkQueue(NULL);
- //
- // Attempt to cancel the signal to prevent signals from coming in way
- // after the timer was deleted.
- //
- PspCancelQueuedSignal(Process, &(Timer->SignalQueueEntry));
- return;
- }
- VOID
- PspProcessTimerDpcRoutine (
- PDPC Dpc
- )
- /*++
- Routine Description:
- This routine implements the DPC routine that fires when a process timer
- expires. It queues the work item.
- Arguments:
- Dpc - Supplies a pointer to the DPC that is running.
- Return Value:
- None.
- --*/
- {
- KSTATUS Status;
- PPROCESS_TIMER Timer;
- //
- // Increment the number of expirations, and queue the work item if this was
- // the first one.
- //
- Timer = (PPROCESS_TIMER)(Dpc->UserData);
- if (RtlAtomicAdd32(&(Timer->ExpirationCount), 1) == 0) {
- //
- // Increment the reference count to ensure this structure doesn't go
- // away while the signal is queued. Anybody trying to make the structure
- // go away needs to flush the DPC before decrementing their referecne
- // to ensure this gets a chance to run.
- //
- PspProcessTimerAddReference(Timer);
- Status = KeQueueWorkItem(Timer->WorkItem);
- ASSERT(KSUCCESS(Status));
- }
- return;
- }
- VOID
- PspProcessTimerWorkRoutine (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine implements the process timer expiration work routine.
- Arguments:
- Parameter - Supplies a pointer to the process timer.
- Return Value:
- None.
- --*/
- {
- ULONG ExpirationCount;
- PPROCESS_TIMER Timer;
- Timer = (PPROCESS_TIMER)Parameter;
- //
- // Read the current expiration count to determine how to set the overflow
- // count.
- //
- ExpirationCount = RtlAtomicOr32(&(Timer->ExpirationCount), 0);
- ASSERT(ExpirationCount != 0);
- Timer->OverflowCount = ExpirationCount - 1;
- Timer->SignalQueueEntry.Parameters.FromU.OverflowCount =
- Timer->OverflowCount;
- if (Timer->Thread != NULL) {
- PsSignalThread(Timer->Thread,
- Timer->SignalQueueEntry.Parameters.SignalNumber,
- &(Timer->SignalQueueEntry),
- FALSE);
- } else {
- PsSignalProcess(Timer->Process,
- Timer->SignalQueueEntry.Parameters.SignalNumber,
- &(Timer->SignalQueueEntry));
- }
- return;
- }
- VOID
- PspProcessTimerSignalCompletion (
- PSIGNAL_QUEUE_ENTRY SignalQueueEntry
- )
- /*++
- Routine Description:
- This routine is called when a process timer's signal was successfully
- completed in usermode.
- Arguments:
- SignalQueueEntry - Supplies a pointer to the signal queue entry that was
- successfully sent to user mode.
- Return Value:
- None.
- --*/
- {
- ULONG ExpirationCount;
- ULONG OverflowCount;
- PPROCESS_TIMER Timer;
- Timer = PARENT_STRUCTURE(SignalQueueEntry, PROCESS_TIMER, SignalQueueEntry);
- //
- // Slam a zero into the overflow count.
- //
- OverflowCount = Timer->OverflowCount;
- Timer->OverflowCount = 0;
- //
- // Subtract off the overflow count (plus one for the original non-overflow
- // expiration) from the expiration count.
- //
- OverflowCount += 1;
- ExpirationCount = RtlAtomicAdd32(&(Timer->ExpirationCount), -OverflowCount);
- ASSERT(ExpirationCount >= OverflowCount);
- //
- // If new intervals came in already, re-queue the work item immediately,
- // as the DPC is never going to.
- //
- if (ExpirationCount - OverflowCount != 0) {
- KeQueueWorkItem(Timer->WorkItem);
- //
- // Release the reference, until the next DPC runs all parties are done
- // touching this memory.
- //
- } else {
- PspProcessTimerReleaseReference(Timer);
- }
- return;
- }
- VOID
- PspExpireRuntimeTimer (
- PKTHREAD Thread,
- PRUNTIME_TIMER Timer,
- ULONG Signal,
- ULONGLONG CurrentTime
- )
- /*++
- Routine Description:
- This routine is called when a runtime timer expires.
- Arguments:
- Thread - Supplies a pointer to the current thread.
- Timer - Supplies a pointer to the thread's runtime timer that expired.
- Signal - Supplies the signal to send the current process.
- CurrentTime - Supplies the current user or user/kernel time, for rearming
- of periodic timers.
- Return Value:
- None.
- --*/
- {
- ULONGLONG NextTime;
- //
- // Fire off a signal to the process as a whole.
- //
- PsSignalProcess(Thread->OwningProcess, Signal, NULL);
- //
- // Rearm the timer if it's periodic.
- //
- if (Timer->Period != 0) {
- NextTime = Timer->DueTime + Timer->Period;
- while ((NextTime > Timer->DueTime) && (NextTime <= CurrentTime)) {
- NextTime += Timer->Period;
- }
- if (NextTime <= Timer->DueTime) {
- NextTime = 0;
- }
- Timer->DueTime = NextTime;
- //
- // This was a one-shot timer. Disable it now.
- //
- } else {
- Timer->DueTime = 0;
- }
- return;
- }
|