|
|
/****************************** 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); }
|