301 lines
6.9 KiB
C
301 lines
6.9 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1997 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
sleep.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This handles sleep requests on the part of the interpreter
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Stephane Plante (splante)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
NT Kernel Mode Driver only
|
|||
|
|
|||
|
NB: Win9x can run this code also, but they will choose not do so.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pch.h"
|
|||
|
|
|||
|
VOID
|
|||
|
SleepQueueDpc(
|
|||
|
PKDPC Dpc,
|
|||
|
PVOID Context,
|
|||
|
PVOID Argument1,
|
|||
|
PVOID Argument2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is fired when a timer event occurs
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - The DPC that was fired
|
|||
|
Context - Not used
|
|||
|
Argument1 - Time.LowPart -- Not used
|
|||
|
Argument2 - Time.HighPart -- Not used
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
--*/
|
|||
|
{
|
|||
|
LARGE_INTEGER currentTime;
|
|||
|
LARGE_INTEGER dueTime;
|
|||
|
LIST_ENTRY localList;
|
|||
|
PLIST_ENTRY listEntry;
|
|||
|
PSLEEP sleepItem;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( Dpc );
|
|||
|
UNREFERENCED_PARAMETER( Context );
|
|||
|
UNREFERENCED_PARAMETER( Argument1 );
|
|||
|
UNREFERENCED_PARAMETER( Argument2 );
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the local list. Contrary to what the docs say, this code
|
|||
|
// can be called from any IRQL (as long as the mem is resident)
|
|||
|
//
|
|||
|
InitializeListHead(&localList);
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the lock, since we must remove the things from the list
|
|||
|
// under some kind of protection.
|
|||
|
//
|
|||
|
AcquireMutex(&gmutSleep);
|
|||
|
|
|||
|
//
|
|||
|
// Find the correct time. This *must* be done after we are acquire the
|
|||
|
// lock because there might be a long delay between trying to acquire
|
|||
|
// the lock and actually getting it
|
|||
|
//
|
|||
|
currentTime.QuadPart = KeQueryInterruptTime();
|
|||
|
|
|||
|
//
|
|||
|
// Loop until we are done
|
|||
|
//
|
|||
|
while (!IsListEmpty(&SleepQueue)) {
|
|||
|
|
|||
|
//
|
|||
|
// Obtain the first element in the global list again
|
|||
|
//
|
|||
|
sleepItem = CONTAINING_RECORD(SleepQueue.Flink, SLEEP, ListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// Should the current item be removed?
|
|||
|
//
|
|||
|
if (sleepItem->SleepTime.QuadPart > currentTime.QuadPart) {
|
|||
|
|
|||
|
//
|
|||
|
// No, so we need to set the timer to take care of this request
|
|||
|
//
|
|||
|
dueTime.QuadPart = currentTime.QuadPart -
|
|||
|
sleepItem->SleepTime.QuadPart;
|
|||
|
KeSetTimer(
|
|||
|
&SleepTimer,
|
|||
|
dueTime,
|
|||
|
&SleepDpc
|
|||
|
);
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Yes, so remove it
|
|||
|
//
|
|||
|
listEntry = RemoveHeadList(&SleepQueue);
|
|||
|
|
|||
|
//
|
|||
|
// Now, add the entry to the next queue
|
|||
|
//
|
|||
|
InsertTailList(&localList, listEntry);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done with lock. This may cause another DPC to process more elements
|
|||
|
//
|
|||
|
ReleaseMutex(&gmutSleep);
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we are free to remove items from the local list and
|
|||
|
// try to do work on them.
|
|||
|
//
|
|||
|
while (!IsListEmpty(&localList)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the first element from the local list
|
|||
|
//
|
|||
|
listEntry = RemoveHeadList(&localList);
|
|||
|
sleepItem = CONTAINING_RECORD(listEntry, SLEEP, ListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// Force the interpreter to run
|
|||
|
//
|
|||
|
|
|||
|
RestartContext(sleepItem->Context,
|
|||
|
(BOOLEAN)((sleepItem->Context->dwfCtxt & CTXTF_ASYNC_EVAL)
|
|||
|
== 0));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef LOCKABLE_PRAGMA
|
|||
|
#pragma ACPI_LOCKABLE_DATA
|
|||
|
#pragma ACPI_LOCKABLE_CODE
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
LOCAL
|
|||
|
SleepQueueRequest(
|
|||
|
IN PCTXT Context,
|
|||
|
IN ULONG SleepTime
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is responsible for adding the sleep request to the
|
|||
|
system queue for pending sleep requests
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - The current execution context
|
|||
|
SleepTime - The amount of sleep time, in MilliSeconds
|
|||
|
|
|||
|
Rreturn Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
TRACENAME("SLEEPQUEUEREQUEST")
|
|||
|
BOOLEAN timerSet = FALSE;
|
|||
|
NTSTATUS status;
|
|||
|
PLIST_ENTRY listEntry;
|
|||
|
PSLEEP currentSleep;
|
|||
|
PSLEEP listSleep;
|
|||
|
ULONGLONG currentTime;
|
|||
|
LARGE_INTEGER dueTime;
|
|||
|
|
|||
|
ENTER(2, ("SleepQueueRequest(Context=%x,SleepTime=%d)\n",
|
|||
|
Context, SleepTime) );
|
|||
|
|
|||
|
status = PushFrame(Context,
|
|||
|
SIG_SLEEP,
|
|||
|
sizeof(SLEEP),
|
|||
|
ProcessSleep,
|
|||
|
¤tSleep);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// The first step is acquire the timer lock, since we must protect it
|
|||
|
//
|
|||
|
AcquireMutex(&gmutSleep);
|
|||
|
|
|||
|
//
|
|||
|
// Next step is to determine time at which we should wake up this
|
|||
|
// context
|
|||
|
//
|
|||
|
currentTime = KeQueryInterruptTime();
|
|||
|
currentSleep->SleepTime.QuadPart = currentTime +
|
|||
|
((ULONGLONG)SleepTime*10000);
|
|||
|
currentSleep->Context = Context;
|
|||
|
|
|||
|
//
|
|||
|
// At this point, it becomes easier to walk the list backwards
|
|||
|
//
|
|||
|
listEntry = &SleepQueue;
|
|||
|
while (listEntry->Blink != &SleepQueue) {
|
|||
|
|
|||
|
listSleep = CONTAINING_RECORD(listEntry->Blink, SLEEP, ListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// Do we have to add the new element after the current one?
|
|||
|
//
|
|||
|
if (currentSleep->SleepTime.QuadPart >=
|
|||
|
listSleep->SleepTime.QuadPart) {
|
|||
|
|
|||
|
//
|
|||
|
// Yes
|
|||
|
//
|
|||
|
InsertHeadList(
|
|||
|
&(listSleep->ListEntry),
|
|||
|
&(currentSleep->ListEntry)
|
|||
|
);
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Next entry
|
|||
|
//
|
|||
|
listEntry = listEntry->Blink;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Look to see if we got to the head
|
|||
|
//
|
|||
|
if (listEntry->Blink == &SleepQueue) {
|
|||
|
|
|||
|
//
|
|||
|
// If we get to this point, it is because we have
|
|||
|
// gone all the around the list. If we add to the
|
|||
|
// front of the list, we must set the timer
|
|||
|
//
|
|||
|
InsertHeadList(&SleepQueue, ¤tSleep->ListEntry);
|
|||
|
dueTime.QuadPart = currentTime - currentSleep->SleepTime.QuadPart;
|
|||
|
timerSet = KeSetTimer(
|
|||
|
&SleepTimer,
|
|||
|
dueTime,
|
|||
|
&SleepDpc
|
|||
|
);
|
|||
|
}
|
|||
|
//
|
|||
|
// Done with the lock
|
|||
|
//
|
|||
|
ReleaseMutex(&gmutSleep);
|
|||
|
}
|
|||
|
|
|||
|
EXIT(2, ("SleepQueueReqest=%x (currentSleep=%x timerSet=%x)\n",
|
|||
|
status, currentSleep, timerSet) );
|
|||
|
return status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/***LP ProcessSleep - post processing of sleep
|
|||
|
*
|
|||
|
* ENTRY
|
|||
|
* pctxt -> CTXT
|
|||
|
* psleep -> SLEEP
|
|||
|
* rc - status code
|
|||
|
*
|
|||
|
* EXIT-SUCCESS
|
|||
|
* returns STATUS_SUCCESS
|
|||
|
* EXIT-FAILURE
|
|||
|
* returns AMLIERR_ code
|
|||
|
*/
|
|||
|
|
|||
|
NTSTATUS LOCAL ProcessSleep(PCTXT pctxt, PSLEEP psleep, NTSTATUS rc)
|
|||
|
{
|
|||
|
TRACENAME("PROCESSSLEEP")
|
|||
|
|
|||
|
ENTER(2, ("ProcessSleep(pctxt=%x,pbOp=%x,psleep=%x,rc=%x)\n",
|
|||
|
pctxt, pctxt->pbOp, psleep, rc));
|
|||
|
|
|||
|
ASSERT(psleep->FrameHdr.dwSig == SIG_SLEEP);
|
|||
|
|
|||
|
PopFrame(pctxt);
|
|||
|
|
|||
|
EXIT(2, ("ProcessSleep=%x\n", rc));
|
|||
|
return rc;
|
|||
|
} //ProcessSleep
|