/* File: Sleep.c Contains: Routines which facilitate sleeping processes. Written by: Erich Ringewald Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved. Change History (most recent first): <5> 3/23/92 JSM OSEvents.h is obsolete, use Events.h. <4> 3/16/92 YK (for TSM) In clkint, If a physical event is not for the front app, donŐt switch to the front app. <2> 11/27/90 DFH Removed unneeded parameter from cpu_resched. <0> 3/13/86 ELR New Today. */ #include #include #include #include #include #include #include #include #include #include #include #include "Glue.h" #include "Lomem.h" #include "Data.h" #include "SysMisc.h" #include "Puppet.h" /* Some function prototypes that should be in (yet another) header file */ void RemoveFromStateList(PEntryPtr, PEntryPtr *); void CancelSleep(PEntryPtr); Boolean OSEventAvailForFront(short, EventRecord *); /* Function prototypes internal to this file */ void RoustSleepers(void); void PutOnSleepQueue(PEntryPtr, unsigned long); void RoustSleepers(void); /* PutOnSleepQueue. Put the specified process on the sleep queue with the designated * sleep time. */ void PutOnSleepQueue(PEntryPtr pProc, unsigned long sleepTime) { register PEntryPtr pPrev, pNext; unsigned long thenTicks; if (sleepTime == 0) return; /* Figure out time to wakeup. Pin overflow to highest TICKS value. */ thenTicks = TICKS + sleepTime; if (thenTicks < sleepTime) thenTicks = MAXVALUE; /* reflect process state in PEntry */ pProc->p_state = PRSLEEPING; pProc->p_wakeuptime = thenTicks; /* Place process in the (ordered) sleep state list */ pPrev = nil; pNext = pSleepingStateList; while (pNext != nil) { unsigned long tempTicks; if ( ((tempTicks = pNext->p_wakeuptime) != 0) && (tempTicks > thenTicks) ) break; pPrev = pNext; pNext = pNext->p_NextProcessInState; } /* Have insertion point. Fix up newcomer's links, then insert. */ assert((pProc != pPrev) && (pProc != pNext)); pProc->p_PrevProcessInState = pPrev; pProc->p_NextProcessInState = pNext; if (pPrev == nil) pSleepingStateList = pProc; else pPrev->p_NextProcessInState = pProc; if (pNext != nil) pNext->p_PrevProcessInState = pProc; } /* SleepProcess. put the calling process to sleep. Call resched to arrange for * another process to use the processor. */ pascal void c_SleepProcess(u_long napTime) { PEntryPtr pCurr; u_long olda5; olda5 = ProcessMgrA5SimpleSetup(); /* can't sleep if not running */ pCurr = pCurrentProcess; if ( (pCurr->p_state != PRRUN) && (pCurr->p_state != PRBACKRUN) ) { A5SimpleRestore(olda5); return; } /* can't sleep if coercing */ if (coercionState != CS_DONE) { A5SimpleRestore(olda5); return; } PutOnSleepQueue(pCurr, napTime); /* offer up the machine */ cpu_resched(); A5SimpleRestore(olda5); } /* clkint. This routine simulates processing on a clock interrupt. This is silly, * and for now we call this thing from the idle loop. */ void clkint(void) { EventRecord evtRec; unsigned long olda5; olda5 = ProcessMgrA5SimpleSetup(); /* See if anyone's time is up */ RoustSleepers(); /* Get frontmost app going if there's a physical event queued */ assert(coercionState == CS_DONE); if ( (pFrontProcess->p_state == PRSLEEPING) && OSEventAvailForFront(pFrontProcess->p_eventmask, &evtRec) ) CancelSleep(pFrontProcess); A5Restore(olda5); } /* PushOnStateList. Links process into head of given state list */ void PushOnStateList(register PEntryPtr pProc, PEntryPtr *ppList) { PEntryPtr pFirstProc; pFirstProc = *ppList; pProc->p_NextProcessInState = pFirstProc; pProc->p_PrevProcessInState = nil; pFirstProc->p_PrevProcessInState = pProc; *ppList = pProc; } /* RemoveFromStateList. Unlinks process from given state list */ void RemoveFromStateList(register PEntryPtr pProc, PEntryPtr *ppList) { PEntryPtr pOtherProc; if ((pOtherProc = pProc->p_PrevProcessInState) != nil) pOtherProc->p_NextProcessInState = pProc->p_NextProcessInState; else *ppList = pProc->p_NextProcessInState; if ((pOtherProc = pProc->p_NextProcessInState) != nil) pOtherProc->p_PrevProcessInState = pProc->p_PrevProcessInState; pProc->p_PrevProcessInState = nil; pProc->p_NextProcessInState = nil; } /* RoustSleepers. This fellow is called by the 'clock interrupt'. It dequeues all * members that have expired. The queue is ordered by p_wakeuptime, so normally * we go no farther than a PEntry that should not be woken. The exception to this, * of course, is when napOver == true, since that means that there are processes * that are to be woken up prematurely (their p_wakeuptime == 0). * NOTE: We turn interrupts off to protect this critical region against interference * from WakeupProcess calls by interrupt routines. */ void RoustSleepers(void) { register PEntryPtr pProc, pNextProc; short ps; ps = disable(); pProc = pSleepingStateList; while (pProc != nil) { /* must get this now, since RemoveFromStateList() changes it */ pNextProc = pProc->p_NextProcessInState; /* see if this guy should wake up now */ if (pProc->p_wakeuptime <= (unsigned long) TICKS) { RemoveFromStateList(pProc, &pSleepingStateList); pProc->p_state = PRREADY; } else if (napOver == false) break; /* move on */ pProc = pNextProc; } napOver = false; spl(ps); } /* CancelSleep. Ensures that pProc gets out of its current event call. */ void CancelSleep(PEntryPtr pProc) { /* make sure the process is awake */ if (pProc->p_state == PRSLEEPING) { RemoveFromStateList(pProc, &pSleepingStateList); pProc->p_state = PRREADY; } /* make sure the process stays awake. A zeroed p_wakeuptime means that the app * will not go to sleep on the way out of its current event call. We must do this * even if app is not asleep to begin with. */ pProc->p_wakeuptime = 0; }