Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

714 lines
22 KiB

/****************************** Module Header ******************************\
* Module Name: taskman.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* This module contains the core functions of the input sub-system
*
* History:
* 02-27-91 MikeHar Created.
* 02-23-92 MattFe rewrote sleeptask
* 09-07-93 DaveHart Per-process nonpreemptive scheduler for
* multiple WOW VDM support.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* WakeWowTask
*
* If needed the wowtask is woken by setting its event. It is assumed that if
* any wow task is currently scheduled that it is a waste of time to wake the
* specified wow task since rescheduling will occur when the currently
* scheduled wow task enters xxxSleepTask.
*
* History:
* ????
\***************************************************************************/
VOID
WakeWowTask(
PTHREADINFO pti)
{
PWOWPROCESSINFO pwpi;
pwpi = pti->ppi->pwpi;
if (pwpi && !pwpi->ptiScheduled) {
KeSetEvent(pti->pEventQueueServer, EVENT_INCREMENT, FALSE);
}
}
/***************************************************************************\
* InsertTask
*
* This function removes a task from its old location and inserts
* in the proper prioritized location
*
* Find a place for this task such that it must be inserted
* after any task with greater or equal priority and must be
* before any task with higher priorty. The higher the priority
* the less urgent the task.
*
* History:
* 19-Nov-1993 mikeke Created
\***************************************************************************/
VOID InsertTask(
PPROCESSINFO ppi,
PTDB ptdbNew)
{
PTDB *pptdb;
PTDB ptdb;
int nPriority;
PWOWPROCESSINFO pwpi = ppi->pwpi;
CheckCritIn();
UserAssert(pwpi != NULL);
pptdb = &pwpi->ptdbHead;
nPriority = ptdbNew->nPriority;
while ((ptdb = *pptdb) != NULL) {
/*
* Remove it from it's old location
*/
if (ptdb == ptdbNew) {
*pptdb = ptdbNew->ptdbNext;
/*
* continue to search for the place to insert it
*/
while ((ptdb = *pptdb) != NULL) {
if (nPriority < ptdb->nPriority) {
break;
}
pptdb = &(ptdb->ptdbNext);
}
break;
}
/*
* if this is the place to insert continue to search for the
* place to delete it from
*/
if (nPriority < ptdb->nPriority) {
do {
if (ptdb->ptdbNext == ptdbNew) {
ptdb->ptdbNext = ptdbNew->ptdbNext;
break;
}
ptdb = ptdb->ptdbNext;
} while (ptdb != NULL);
break;
}
pptdb = &(ptdb->ptdbNext);
}
/*
* insert the new task
*/
ptdbNew->ptdbNext = *pptdb;
*pptdb = ptdbNew;
}
/***************************************************************************\
* DestroyTask()
*
* History:
* 02-27-91 MikeHar Created.
\***************************************************************************/
VOID DestroyTask(
PPROCESSINFO ppi,
PTHREADINFO ptiToRemove)
{
PTDB ptdbToRemove = ptiToRemove->ptdb;
PTDB ptdb;
PTDB* pptdb;
PWOWPROCESSINFO pwpi = ppi->pwpi;
// try to catch #150446
CheckCritIn();
BEGINATOMICCHECK();
UserAssert(pwpi != NULL);
if (ptdbToRemove != NULL) {
if (ptdbToRemove->TDB_Flags & TDBF_SETUP) {
/*
* This means that the WoW app was a setup app (checked in
* SetAppCompatFlags). If so, the shell needs to be notified so
* it can clean up potential problems caused by bad calls to
* DDE, etc.
*/
PDESKTOPINFO pdeskinfo = GETDESKINFO(ptiToRemove);
if (pdeskinfo->spwndShell) {
_PostMessage(pdeskinfo->spwndShell, DTM_SETUPAPPRAN, 0, 0);
}
}
/*
* Remove the WOW per thread info.
*/
if (ptdbToRemove->pwti) {
PWOWTHREADINFO *ppwti = &gpwtiFirst;
while (*ppwti != ptdbToRemove->pwti && (*ppwti)->pwtiNext != NULL) {
ppwti = &((*ppwti)->pwtiNext);
}
if (*ppwti == ptdbToRemove->pwti) {
*ppwti = ptdbToRemove->pwti->pwtiNext;
}
CLOSE_PSEUDO_EVENT(&ptdbToRemove->pwti->pIdleEvent);
UserFreePool(ptdbToRemove->pwti);
}
gpsi->nEvents -= ptdbToRemove->nEvents;
/*
* remove it from any lists
*/
pptdb = &pwpi->ptdbHead;
while ((ptdb = *pptdb) != NULL) {
/*
* Remove it from it's old location
*/
if (ptdb == ptdbToRemove) {
*pptdb = ptdb->ptdbNext;
UserAssert(ptiToRemove->ptdb == ptdbToRemove);
UserFreePool(ptdbToRemove);
ptiToRemove->ptdb = NULL;
break;
}
pptdb = &(ptdb->ptdbNext);
}
UserAssert(ptdb == ptdbToRemove);
}
ENDATOMICCHECK();
/*
* If the task being destroyed is the active task, make nobody active.
* We will go through this code path for 32-bit threads that die while
* Win16 threads are waiting for a SendMessage reply from them.
*/
if (pwpi->ptiScheduled == ptiToRemove) {
pwpi->ptiScheduled = NULL;
ExitWowCritSect(ptiToRemove, pwpi);
/*
* Wake next task with events, or wowexec to run the scheduler
*/
if (pwpi->ptdbHead != NULL) {
for (ptdb = pwpi->ptdbHead; ptdb; ptdb = ptdb->ptdbNext) {
if (ptdb->nEvents > 0) {
KeSetEvent(ptdb->pti->pEventQueueServer,
EVENT_INCREMENT, FALSE);
break;
}
}
if (!ptdb) {
KeSetEvent(pwpi->pEventWowExec, EVENT_INCREMENT, FALSE);
}
}
}
UserAssert(ptiToRemove != pwpi->CSOwningThread);
}
/***************************************************************************\
* xxxSleepTask
*
* This function puts this task to sleep and wakes the next (if any)
* deserving task.
*
* BOOL fInputIdle - app is going idle, may do idle hooks
* HANDLE hEvent - if nonzero, WowExec's event (client side) for
* virtual HW Interrupt HotPath.
* History:
* 02-27-91 MikeHar Created.
* 02-23-91 MattFe rewrote
* 12-17-93 Jonle add wowexec hotpath for VirtualInterrupts
\***************************************************************************/
BOOL xxxSleepTask(
BOOL fInputIdle,
HANDLE hEvent)
{
PTDB ptdb;
PTHREADINFO pti;
PPROCESSINFO ppi;
PWOWPROCESSINFO pwpi;
PSMS psms;
NTSTATUS Status;
int nHandles;
BOOLEAN bWaitedAtLeastOnce;
/*
* !!!
* ClearSendMessages assumes that this function does NOT leave the
* critical section when called with fInputIdle==FALSE and from a
* 32bit thread!
*/
CheckCritIn();
pti = PtiCurrent();
ppi = pti->ppi;
pwpi = ppi->pwpi;
/*
* If this task has received a message from outside of the current
* wow scheduler and hasn't yet replied to the message, the scheduler
* will deadlock because the send\receive lock counts are updated
* in ReplyMessage and not in receive message. Check for this
* condition and do the DirectedSchedukeTask that normally occurs
* in ReplyMessage. 16-Feb-1995 Jonle
*/
psms = pti->psmsCurrent;
if (psms && psms->ptiReceiver == pti &&
psms->ptiSender && !(psms->flags & SMF_REPLY) &&
psms->flags & (SMF_RECEIVERBUSY | SMF_RECEIVEDMESSAGE) &&
psms->ptiSender->TIF_flags & TIF_16BIT &&
(pwpi != psms->ptiSender->ppi->pwpi || !(pti->TIF_flags & TIF_16BIT)) ) {
DirectedScheduleTask(psms->ptiReceiver, psms->ptiSender, FALSE, psms);
}
/*
* Return immediately if we are not 16 bit (don't have a pwpi).
*/
if (!(pti->TIF_flags & TIF_16BIT)) {
return FALSE;
}
/*
* Deschedule the current task.
*/
if (pti == pwpi->ptiScheduled) {
ExitWowCritSect(pti, pwpi);
pwpi->ptiScheduled = NULL;
}
UserAssert(pti != pwpi->CSOwningThread);
/*
* If this is wowexec calling on WowWaitForMsgAndEvent
* set up the WakeMask for all messages , and check for wake
* bits set since the last time. Reinsert wowexec, at the end
* of the list so other 16 bit tasks will be scheduled first.
*/
if (pwpi->hEventWowExecClient == hEvent) {
InsertTask(ppi, pti->ptdb);
pti->pcti->fsWakeMask = QS_ALLINPUT | QS_EVENT;
if (pti->pcti->fsChangeBits & pti->pcti->fsWakeMask) {
pti->ptdb->nEvents++;
gpsi->nEvents++;
}
}
bWaitedAtLeastOnce = FALSE;
do {
/*
* If nobody is Active look for the highest priority task with
* some events pending. if MsgWaitForMultiple call don't
* reschedule self
*/
if (pwpi->ptiScheduled == NULL) {
rescan:
if (pwpi->nRecvLock >= pwpi->nSendLock) {
for (ptdb = pwpi->ptdbHead; ptdb; ptdb = ptdb->ptdbNext) {
if (ptdb->nEvents > 0 &&
!(hEvent == HEVENT_REMOVEME && ptdb->pti == pti)) {
pwpi->ptiScheduled = ptdb->pti;
break;
}
}
if (bWaitedAtLeastOnce) {
//
// If not first entry into sleep task avoid waiting
// more than needed, if the curr task is now scheduled.
//
if (pwpi->ptiScheduled == pti) {
break;
}
} else {
//
// On the first entry into sleep task input is going
// idle if no tasks are ready to run. Call the idle
// hook if there is one.
//
if (fInputIdle &&
pwpi->ptiScheduled == NULL &&
IsHooked(pti, WHF_FOREGROUNDIDLE)) {
/*
* Make this the active task so that no other
* task will become active while we're calling
* the hook.
*/
pwpi->ptiScheduled = pti;
xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
/*
* Reset state so that no tasks are active. We
* then need to rescan the task list to see if
* a task was scheduled during the call to the
* hook. Clear the input idle flag to ensure
* that the hook won't be called again if there
* are no tasks ready to run.
*/
pwpi->ptiScheduled = NULL;
fInputIdle = FALSE;
goto rescan;
}
}
}
/*
* If there is a task ready, wake it up.
*/
if (pwpi->ptiScheduled != NULL) {
KeSetEvent(pwpi->ptiScheduled->pEventQueueServer,
EVENT_INCREMENT,
FALSE
);
/*
* There is no one to wake up, but we may have to wake
* wowexec to service virtual hardware interrupts
*/
} else if (ppi->W32PF_Flags & W32PF_WAKEWOWEXEC) {
if (pwpi->hEventWowExecClient == hEvent) {
pwpi->ptiScheduled = pti;
ppi->W32PF_Flags &= ~W32PF_WAKEWOWEXEC;
InsertTask(ppi, pti->ptdb);
EnterWowCritSect(pti, pwpi);
UserAssert(pti == pwpi->ptiScheduled);
return TRUE;
} else {
KeSetEvent(pwpi->pEventWowExec, EVENT_INCREMENT, FALSE);
}
} else if ((pti->TIF_flags & TIF_SHAREDWOW) && !bWaitedAtLeastOnce) {
if (pwpi->hEventWowExecClient == hEvent) {
/*
* We have to call zzzWakeInputIdle only if this will
* awake WowExec's thread and not other thread. Bug 44060.
*/
zzzWakeInputIdle(pti); // need to DeferWinEventNotify() ?? IANJA ??
}
}
}
/*
* Return if we are a 32 bit thread, or if we were called by
* MsgWaitForMultiple to exit the wow scheduler.
*/
if (!(pti->TIF_flags & TIF_16BIT)) {
return FALSE;
} else if (hEvent == HEVENT_REMOVEME) {
InsertTask(ppi, pti->ptdb);
KeClearEvent(pti->pEventQueueServer);
return FALSE;
}
if (pti->apEvent == NULL) {
pti->apEvent = UserAllocPoolNonPaged(POLL_EVENT_CNT * sizeof(PKEVENT), TAG_EVENT);
if (pti->apEvent == NULL)
return FALSE;
}
/*
* Wait for input to this thread.
*/
pti->apEvent[IEV_TASK] = pti->pEventQueueServer;
/*
* Add the WowExec, handle for virtual hw interrupts
*/
if (pwpi->hEventWowExecClient == hEvent) {
pti->apEvent[IEV_WOWEXEC] = pwpi->pEventWowExec;
nHandles = 2;
} else {
nHandles = 1;
}
if (pti->TIF_flags & TIF_MEOW) {
xxxClientWOWTask16SchedNotify(WOWSCHNOTIFY_WAIT, 0);
}
LeaveCrit();
Status = KeWaitForMultipleObjects(nHandles,
&pti->apEvent[IEV_TASK],
WaitAny,
WrUserRequest,
UserMode,
TRUE,
NULL,
NULL);
EnterCrit();
if (pti->TIF_flags & TIF_MEOW) {
xxxClientWOWTask16SchedNotify(WOWSCHNOTIFY_RUN, 0);
}
bWaitedAtLeastOnce = TRUE;
// remember if we woke up for wowexec
if (Status == STATUS_WAIT_1) {
ppi->W32PF_Flags |= W32PF_WAKEWOWEXEC;
} else if (Status == STATUS_USER_APC) {
/*
* ClientDeliverUserApc() delivers User-mode APCs by calling back
* to the client and immediately returning without doing anything:
* KeUserModeCallback will automatically deliver any pending APCs.
*/
ClientDeliverUserApc();
}
} while (pwpi->ptiScheduled != pti);
/*
* We are the Active Task, reduce number of Events
* Place ourselves at the far end of tasks in the same priority
* so that next time we sleep someone else will run.
*/
pti->ptdb->nEvents--;
gpsi->nEvents--;
UserAssert(gpsi->nEvents >= 0);
InsertTask(ppi, pti->ptdb);
ppi->W32PF_Flags &= ~W32PF_WAKEWOWEXEC;
EnterWowCritSect(pti, pwpi);
UserAssert(pti == pwpi->ptiScheduled);
return FALSE;
}
/***************************************************************************\
* xxxUserYield
*
* Does exactly what Win3.1 UserYield does.
*
* History:
* 10-19-92 Scottlu Created.
\***************************************************************************/
BOOL xxxUserYield(
PTHREADINFO pti)
{
PPROCESSINFO ppi = pti->ppi;
/*
* Deal with any pending messages. Only call it this first time if
* this is the current running 16 bit app. In the case when starting
* up a 16 bit app, the starter calls UserYield() to yield to the new
* task, but at this time ppi->ptiScheduled is set to the new task.
* Receiving messages at this point would be bad!
*/
if (pti->TIF_flags & TIF_16BIT) {
if (pti == ppi->pwpi->ptiScheduled) {
xxxReceiveMessages(pti);
}
} else {
xxxReceiveMessages(pti);
}
/*
* If we are a 16 bit task
* Mark our task so it comes back some time. Also, remove it and
* re-add it to the list so that we are the last task of our priority
* to run.
*/
if ((pti->TIF_flags & TIF_16BIT) && (pti->ptdb != NULL)) {
if (pti->ptdb->nEvents == 0) {
pti->ptdb->nEvents++;
gpsi->nEvents++;
}
InsertTask(ppi, pti->ptdb);
/*
* Sleep. Return right away if there are no higher priority tasks
* in need of running.
*/
xxxSleepTask(TRUE, NULL);
/*
* Deal with any that arrived since we weren't executing.
*/
xxxReceiveMessages(pti);
}
return TRUE;
}
/***************************************************************************\
* DirectedScheduleTask
*
* History:
* 25-Jun-1992 mikeke Created.
\***************************************************************************/
VOID DirectedScheduleTask(
PTHREADINFO ptiOld,
PTHREADINFO ptiNew,
BOOL bSendMsg,
PSMS psms)
{
PWOWPROCESSINFO pwpiOld;
PWOWPROCESSINFO pwpiNew;
CheckCritIn();
pwpiOld = ptiOld->ppi->pwpi;
pwpiNew = ptiNew->ppi->pwpi;
/*
* If old task is 16 bit, reinsert the task in its wow scheduler list
* so that it is lowest in priority. Note that ptiOld is always the
* same as pwpiOld->ptiScheduled except when called from ReceiverDied.
*/
if (ptiOld->TIF_flags & TIF_16BIT) {
if (pwpiOld->ptiScheduled == ptiOld) {
ptiOld->ptdb->nEvents++;
gpsi->nEvents++;
InsertTask(ptiOld->ppi, ptiOld->ptdb);
}
// Update the Send\Recv counts for interprocess scheduling in SleepTask
if (pwpiOld != pwpiNew || !(ptiNew->TIF_flags & TIF_16BIT)) {
if (bSendMsg) {
pwpiOld->nSendLock++;
psms->flags |= SMF_WOWSEND;
}
else if (pwpiOld->nRecvLock && psms->flags & SMF_WOWRECEIVE) {
pwpiOld->nRecvLock--;
psms->flags &= ~SMF_WOWRECEIVE;
}
}
}
/*
* If the new task is 16 bit, reinsert into the wow scheduler list
* so that it will run, if its a sendmsg raise priority of the receiver.
* If its a reply and the sender is waiting for this psms or the sender
* has a message to reply to raise priority of the sender.
*/
if (ptiNew->TIF_flags & TIF_16BIT) {
BOOL bRaisePriority;
ptiNew->ptdb->nEvents++;
gpsi->nEvents++;
bRaisePriority = bSendMsg || psms == ptiNew->psmsSent;
if (bRaisePriority) {
ptiNew->ptdb->nPriority--;
}
InsertTask(ptiNew->ppi, ptiNew->ptdb);
if (bRaisePriority) {
ptiNew->ptdb->nPriority++;
WakeWowTask(ptiNew);
}
// Update the Send\Recv counts for interprocess scheduling in SleepTask
if (pwpiOld != pwpiNew || !(ptiOld->TIF_flags & TIF_16BIT)) {
if (bSendMsg) {
pwpiNew->nRecvLock++;
psms->flags |= SMF_WOWRECEIVE;
}
else if (pwpiNew->nSendLock && psms->flags & SMF_WOWSEND) {
pwpiNew->nSendLock--;
psms->flags &= ~SMF_WOWSEND;
}
}
}
}
/***************************************************************************\
* xxxDirectedYield
*
* History:
* 09-17-92 JimA Created.
\***************************************************************************/
VOID xxxDirectedYield(
DWORD dwThreadId)
{
PTHREADINFO ptiOld;
PTHREADINFO ptiNew;
CheckCritIn();
ptiOld = PtiCurrent();
if (!(ptiOld->TIF_flags & TIF_16BIT) || !ptiOld->ppi->pwpi) {
RIPMSG0(RIP_ERROR, "DirectedYield called from 32 bit thread!");
return;
}
/*
* If the old task is 16 bit, reinsert the task in its wow
* scheduler list so that it is lowest in priority.
*/
ptiOld->ptdb->nEvents++;
gpsi->nEvents++;
InsertTask(ptiOld->ppi, ptiOld->ptdb);
/*
* -1 supports Win 3.1 OldYield mechanics
*/
if (dwThreadId != DY_OLDYIELD) {
ptiNew = PtiFromThreadId(dwThreadId);
if ((ptiNew == NULL) || (ptiNew->ppi != ptiOld->ppi)) {
RIPMSG0(RIP_ERROR, "DirectedYield called from different process or invalid thread id!");
return;
}
if (ptiNew->TIF_flags & TIF_16BIT) {
ptiNew->ptdb->nEvents++;
gpsi->nEvents++;
ptiNew->ptdb->nPriority--;
InsertTask(ptiNew->ppi, ptiNew->ptdb);
ptiNew->ptdb->nPriority++;
}
}
xxxSleepTask(TRUE, NULL);
}