DIRECTORY CPSwapDefs USING [ProcessState], Environment USING [Long, PageCount, PageNumber, wordsPerPage], Frame USING [Alloc, Free, GetReturnFrame, MyLocalFrame], Inline USING [BITAND, BITNOT, BITOR, BITSHIFT, COPY], PrincOps USING [ControlLink, Frame, FrameHandle, FrameSizeIndex, NullFrame, NullLink, StateVector], Process USING [Milliseconds, Priority, Ticks], ProcessInternal USING [DisableInterrupts, EnableInterrupts], ProcessOperations USING [ Broadcast, EnableAndRequeue, Enter, Exit, HandleToIndex, IndexToHandle, Notify, ReadPSB, ReEnter, Requeue, Wait, WritePSB, WriteWDC], ProcessPriorities USING [ priorityClient, priorityClientHigh, priorityClientLow, priorityRealTime], ProcessorFace USING [millisecondsPerTick, reservedNakedNotifyMask], PSB USING [ Condition, ConditionVariable, FaultIndex, InterruptItem, Monitor, NoTimeout, NullPsbHandle, NullStateVectorHandle, PDA, PDABase, Priority, ProcessDataArea, ProcessStateBlock, PsbHandle, PsbIndex, PsbNull, Queue, StartPsb, StateVectorHandle, Ticks, UnlockedEmpty], RuntimeInternal USING [FrameSize], RuntimePrograms USING [], SDDefs USING [SD, sFork, sJoin, sProcessTrap], StartList USING [Base], StoragePrograms USING [tableBase]; Processes: MONITOR LOCKS processLock IMPORTS Frame, Inline, ProcessInternal, ProcessorFace, ProcessOperations, RuntimeInternal, StoragePrograms EXPORTS Process, ProcessInternal, RuntimePrograms = BEGIN OPEN PSB, ProcessOperations; priorityBackground: PUBLIC Process.Priority _ ProcessPriorities.priorityClientLow; priorityForeground: PUBLIC Process.Priority _ ProcessPriorities.priorityClientHigh; priorityInterrupt: PUBLIC Process.Priority _ ProcessPriorities.priorityRealTime; priorityNormal: PUBLIC Process.Priority _ ProcessPriorities.priorityClient; ConditionVariable: TYPE = PSB.ConditionVariable; FrameSizeIndex: TYPE = PrincOps.FrameSizeIndex; --PrincOps.--FsiFrame: TYPE = MACHINE DEPENDENT RECORD [ fsi (0): FrameSizeIndex, -- must be at 3 MOD 4 boundary. frame (1): local PrincOps.Frame]; assert1: [FIRST[Process.Priority]..FIRST[Process.Priority]] = FIRST[PSB.Priority]; assert2: [LAST[Process.Priority]..LAST[Process.Priority]] = LAST[PSB.Priority]; assert3: [FIRST[Process.Ticks]..FIRST[Process.Ticks]] = FIRST[PSB.Ticks]; assert5: [PSB.NoTimeout..PSB.NoTimeout] = 0; -- (see next statement.) collisionTimeout: PSB.Ticks = PSB.NoTimeout+1; -- value to use if client ProcessState: TYPE = CPSwapDefs.ProcessState; ProcState: -- for access to ProcessState field. PROCEDURE [psbh: PsbHandle] RETURNS [ProcessState] = INLINE { RETURN[LOOPHOLE[pda[psbh].flags.available, ProcessState]] }; NakedNotifyLevel: TYPE = [0..16); DyingFrameHandle: TYPE = POINTER TO dying PrincOps.Frame; InvalidProcess: PUBLIC ERROR [process: PsbIndex] = CODE; TooManyProcesses: PUBLIC ERROR = CODE; rebirth: CONDITION; -- ..unused. frameReady: CONDITION; -- ..parents waiting to rejoin their child. frameTaken: CONDITION; -- ..children waiting to rejoin their parent. dead: CONDITION; -- ..parents waiting for their child to clean up. pda: PSB.PDABase = PSB.PDA; processLock: MONITORLOCK; busyLevels: WORD; -- bit-mask of busy naked-notify levels deadFrame: DyingFrameHandle _ NIL; -- the top-level frame of a Bug: PRIVATE ERROR [type: BugType] = CODE; BugType: TYPE = {insufficientMappedPDA, noMoreNakedNotifyLevels, noSuchCondition}; --RuntimePrograms.--InitializeProcesses: PUBLIC PROCEDURE [pagePDA: Environment.PageNumber, countPDA: Environment.PageCount] = BEGIN AlignUp: PROC [unaligned, modulus: UNSPECIFIED] RETURNS [aligned: UNSPECIFIED] = INLINE { RETURN[ ((unaligned + modulus-1) / modulus) * modulus] }; AlignDown: PROC [unaligned, modulus: UNSPECIFIED] RETURNS [aligned: UNSPECIFIED] = INLINE { RETURN[unaligned - unaligned MOD modulus] }; rAlloc: PDABase RELATIVE POINTER TO UNSPECIFIED; -- allocation pointer. DisableTimeout[@dead]; DisableTimeout[@frameReady]; DisableTimeout[@frameTaken]; DisableTimeout[@rebirth]; SDDefs.SD[SDDefs.sProcessTrap] _ ProcessTrap; SDDefs.SD[SDDefs.sFork] _ Fork; SDDefs.SD[SDDefs.sJoin] _ Join; rAlloc _ LOOPHOLE[countPDA*Environment.wordsPerPage]; BEGIN alignmentStateVector: CARDINAL = 4; -- (a D0 requirement) sizeStateVector: CARDINAL = AlignUp[MAX[SIZE[PrincOps.StateVector], StoragePrograms.tableBase.stateVectorSize], alignmentStateVector]; rState: PSB.StateVectorHandle; rState _ AlignDown[rAlloc, alignmentStateVector]; FOR pri: PSB.Priority IN PSB.Priority DO pda.state[pri] _ PSB.NullStateVectorHandle; FOR k: CARDINAL IN [0..StoragePrograms.tableBase.stateVectorCounts[pri]) DO IF LOOPHOLE[rState, CARDINAL]= nUseful THEN {pda.count _ nUseful; EXIT}; -- that many Psbs fit in avail space. REPEAT FINISHED => ERROR Bug[insufficientMappedPDA]; ENDLOOP; IF pda.count <= 1 THEN ERROR Bug[insufficientMappedPDA]; END; FOR psb: PsbIndex IN [StartPsb..StartPsb+pda.count) DO pda[pda.timeout][psb] _ NoTimeout; ENDLOOP; pda.block[StartPsb] _ ProcessStateBlock[ link: [failed: FALSE, priority: FIRST[Priority], next: StartPsb, vector: NULL], flags: [available: LOOPHOLE[ProcessState[state: alive, detached: TRUE]], cleanup: PsbNull, waiting: FALSE, abort: FALSE], context: NULL, mds: NoTimeout ]; WritePSB[IndexToHandle[StartPsb]]; -- tell the processor. pda.ready _ Queue[tail: StartPsb]; FOR root: PrincOps.FrameHandle _ Frame.MyLocalFrame[], root.returnlink.frame DO IF root.returnlink.frame = PrincOps.NullFrame THEN { root.returnlink _ LOOPHOLE[End, procedure PrincOps.ControlLink]; EXIT }; ENDLOOP; BEGIN firstFree: PsbIndex _ StartPsb+1; lastFree: PsbIndex _ StartPsb+pda.count-1; LOOPHOLE[rebirth, ConditionVariable].condition.tail _ PsbNull; FOR psb: PsbIndex IN [firstFree..lastFree] DO pda.block[psb].link.next _ IF psb=firstFree THEN lastFree ELSE psb-1; LOOPHOLE[pda.block[psb].flags.available, ProcessState].state _ dead; pda.block[psb].mds _ NoTimeout; REPEAT FINISHED => LOOPHOLE[rebirth, ConditionVariable].condition.tail _ firstFree; ENDLOOP; END; FOR flt: FaultIndex IN FaultIndex DO pda.fault[flt] _ [queue: [tail: PsbNull], condition: [tail: PsbNull, abortable: FALSE, wakeup: FALSE] ]; ENDLOOP; busyLevels _ ProcessorFace.reservedNakedNotifyMask; ProcessOperations.WriteWDC[0]; -- start interrupts. END; DisableAborts: PUBLIC --EXTERNAL-- PROCEDURE [pCondition: LONG POINTER TO CONDITION] = { LOOPHOLE[pCondition^, ConditionVariable] .condition.abortable _ FALSE }; DisableTimeout: PUBLIC --EXTERNAL-- PROCEDURE [pCondition: LONG POINTER TO CONDITION] = { pCondition.timeout _ NoTimeout }; EnableAborts: PUBLIC --EXTERNAL-- PROCEDURE [pCondition: LONG POINTER TO CONDITION] = { LOOPHOLE[pCondition^, ConditionVariable] .condition.abortable _ TRUE}; GetCurrent: PUBLIC --EXTERNAL-- PROCEDURE RETURNS [psbHandle: PROCESS] = { RETURN[ LOOPHOLE[HandleToIndex[ReadPSB[]], PROCESS] ] }; GetPriority: PUBLIC --EXTERNAL since atomic action-- SAFE PROCEDURE [] RETURNS [priority: Process.Priority] = TRUSTED { RETURN[pda[ReadPSB[]].link.priority]}; InitializeCondition: PUBLIC --EXTERNAL-- PROCEDURE [condition: LONG POINTER TO CONDITION, ticks: Process.Ticks] = { LOOPHOLE[condition^, ConditionVariable] _ [ condition: [tail: PsbNull, abortable: FALSE, wakeup: FALSE], timeout: IF ticks=NoTimeout THEN collisionTimeout ELSE ticks ] }; InitializeMonitor: PUBLIC --EXTERNAL-- PROCEDURE [pMonitor: LONG POINTER TO MONITORLOCK] = { LOOPHOLE[pMonitor^, PSB.Monitor] _ UnlockedEmpty }; MsecToTicks: PUBLIC --EXTERNAL-- SAFE PROCEDURE [ms: Process.Milliseconds] RETURNS [Process.Ticks] = TRUSTED { RETURN[ ( IF ms >= LAST[Process.Milliseconds]- (ProcessorFace.millisecondsPerTick-1) THEN LAST[Process.Milliseconds] -- (avoid overflow) ELSE ms + ProcessorFace.millisecondsPerTick-1 ) / ProcessorFace.millisecondsPerTick ] }; ProcessTrap: --EXTERNAL-- PROCEDURE RETURNS [BOOLEAN] = BEGIN Enter: PROCEDURE [a: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = LOOPHOLE[ProcessOperations.Enter]; LongEnter: PROCEDURE [a,b: --LONG POINTER TO MONITORLOCK--UNSPECIFIED] RETURNS [BOOLEAN] = LOOPHOLE[ProcessOperations.Enter]; abortee: PrincOps.FrameHandle; state: RECORD [filler: UNSPECIFIED, v: PrincOps.StateVector]; state.v _ STATE; UNTIL (IF state.v.stkptr = 4 THEN LongEnter[state.v.stk[0], state.v.stk[1]] ELSE Enter[LOOPHOLE[state.v.stk[0], POINTER TO MONITORLOCK]] ) DO ENDLOOP; abortee _ Frame.GetReturnFrame[]; abortee.pc _ [abortee.pc + 1]; -- step past ME(L) instruction. pda[ReadPSB[]].flags.abort _ FALSE; ERROR ABORTED; END; SetPriority: PUBLIC --EXTERNAL-- PROCEDURE [p: Process.Priority] = BEGIN h: PsbHandle; h _ ReadPSB[]; pda[h].link.priority _ p; ProcessInternal.DisableInterrupts[]; -- (to cancel subsequent enable) EnableAndRequeue[@pda.ready, @pda.ready, h]; END; SecondsToTicks: PUBLIC --EXTERNAL-- SAFE PROCEDURE [sec: CARDINAL] RETURNS [Process.Ticks] = TRUSTED BEGIN ticks: Environment.Long = [lc[ (LONG[sec]*LONG[1000] + ProcessorFace.millisecondsPerTick-1) / ProcessorFace.millisecondsPerTick]]; RETURN[IF ticks.highbits ~= 0 THEN LAST[Ticks] ELSE ticks.lowbits] END; SetTimeout: PUBLIC --EXTERNAL-- PROCEDURE [condition: LONG POINTER TO CONDITION, ticks: Process.Ticks] = {condition.timeout _ IF ticks=NoTimeout THEN collisionTimeout ELSE ticks}; TicksToMsec: PUBLIC --EXTERNAL-- SAFE PROCEDURE [ticks: Ticks] RETURNS [Process.Milliseconds] = TRUSTED {RETURN[ IF ticks > LAST[Process.Milliseconds]/ProcessorFace.millisecondsPerTick THEN LAST[Process.Milliseconds] ELSE ticks*ProcessorFace.millisecondsPerTick ] }; Yield: PUBLIC --EXTERNAL-- SAFE PROCEDURE = TRUSTED { ProcessInternal.DisableInterrupts[]; -- (to cancel subsequent enable) EnableAndRequeue[@pda.ready, @pda.ready, ReadPSB[]] }; Abort: PUBLIC ENTRY PROCEDURE [process: PsbIndex] = BEGIN h: PsbHandle; IF (h _ ValidProcess[process]) = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[process]; ProcessInternal.DisableInterrupts[]; -- (also stops IF ProcState[h].state = alive THEN { pda[h].flags.abort _ TRUE; IF pda[h].flags.waiting THEN -- Wake the victim up..-- { pda[h].flags.waiting _ FALSE; pda[pda.timeout][HandleToIndex[h]] _ NoTimeout; pda[h].--timeout--mds _ NoTimeout; Requeue[NIL, @pda.ready, h] }}; ProcessInternal.EnableInterrupts[]; END; --ProcessInternal.--AllocateNakedCondition: PUBLIC ENTRY PROC [] RETURNS [cv: LONG POINTER TO CONDITION, mask: WORD] = BEGIN level: NakedNotifyLevel; FOR level IN NakedNotifyLevel DO mask _ Inline.BITSHIFT[1, LAST[NakedNotifyLevel] - level]; IF Inline.BITAND[mask, busyLevels] = 0 THEN BEGIN busyLevels _ Inline.BITOR[busyLevels, mask]; cv _ LOOPHOLE[@pda.interrupt[level]]; RETURN; END; ENDLOOP; ERROR Bug[noMoreNakedNotifyLevels]; END; --ProcessInternal.--DeallocateNakedCondition: PUBLIC PROC [cv: LONG POINTER TO CONDITION] = BEGIN level: NakedNotifyLevel; FOR level IN NakedNotifyLevel DO mask: WORD _ Inline.BITSHIFT[1, LAST[NakedNotifyLevel] - level]; IF cv = LOOPHOLE[@pda.interrupt[level], LONG POINTER TO CONDITION] THEN BEGIN busyLevels _ Inline.BITAND[busyLevels, Inline.BITNOT[mask]]; pda.interrupt[level].condition.tail _ PsbNull; RETURN; END; ENDLOOP; ERROR Bug[noSuchCondition]; END; Detach: PUBLIC ENTRY PROCEDURE [process: PROCESS] = BEGIN h: PsbHandle; IF (h _ ValidProcess[process]) = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[LOOPHOLE[process, UNSPECIFIED]]; LOOPHOLE[pda[h].flags.available, ProcessState].detached _ TRUE; BROADCAST frameTaken; -- wake child if waiting to JOIN. END; End: --"ENTRY"-- PROCEDURE = BEGIN sv: RECORD [filler: UNSPECIFIED, results: PrincOps.StateVector]; frame: DyingFrameHandle; h: PsbHandle; sv.results _ STATE; -- save stack containing returned results. WHILE ~Enter[@processLock] DO NULL ENDLOOP; frame _ LOOPHOLE[Frame.MyLocalFrame[]]; frame.state _ alive; h _ ReadPSB[]; LOOPHOLE[pda[h].flags.available, ProcessState].state _ frameReady; pda[h].flags.abort _ FALSE; -- too late for Aborts: they no-op Broadcast[@frameReady]; -- wake any parent process waiting to Join. UNTIL ProcState[h].state = frameTaken OR ProcState[h].detached DO Wait[@processLock, @frameTaken, LOOPHOLE[frameTaken, ConditionVariable].timeout]; WHILE ~ReEnter[@processLock, @frameTaken] DO NULL ENDLOOP; ENDLOOP; IF deadFrame ~= NIL THEN {Frame.Free[deadFrame]; deadFrame _ NIL}; IF ProcState[h].detached THEN deadFrame _ frame; -- If detached, leave our frame for freeing. frame.state _ dead; -- tell Joiner that we're done. LOOPHOLE[pda[h].flags.available, ProcessState].state _ dead; Broadcast[@dead]; -- tell parent our frame has been left for freeing. Wait[@processLock, @rebirth, LOOPHOLE[rebirth, ConditionVariable].timeout]; WHILE ~ReEnter[@processLock, @rebirth] DO NULL ENDLOOP; sv.results.dest _ LOOPHOLE[frame.returnlink, PrincOps.ControlLink]; -- (frame.returnlink was set by Join[] to point to the context of JOIN.) sv.results.source _ PrincOps.NullLink; Exit[@processLock]; RETURN WITH sv.results; END; Fork: --"ENTRY"-- PROCEDURE [--argsForRoot,-- root: PrincOps.ControlLink] RETURNS [childPsb: PsbIndex] = BEGIN PForkFrame: TYPE = POINTER TO FRAME[Fork]; ChildBuilder: PROC [--parent's locals--] RETURNS [--MUST BE NULL!--] = BEGIN pChild: LONG POINTER TO ProcessStateBlock _ @pda.block[childPsb]; parentFrame: PForkFrame = LOOPHOLE[Frame.GetReturnFrame[]]; fsi: FrameSizeIndex = LOOPHOLE[ (parentFrame-1), POINTER TO FsiFrame].fsi; childFrame: PForkFrame = Frame.Alloc[fsi]; Inline.COPY[from: parentFrame-1, to: childFrame-1, nwords: RuntimeInternal.FrameSize[fsi] + SIZE[FrameSizeIndex] ]; childFrame.identity _ child; pChild.link.failed _ FALSE; pChild.link.priority _ pda[ReadPSB[]].link.priority; pChild.flags _ [available: LOOPHOLE[ProcessState[state: alive, detached: FALSE]], cleanup: PsbNull, waiting: FALSE, abort: FALSE]; pChild.context _ [frame[LOOPHOLE[childFrame, PrincOps.FrameHandle]]]; pChild.mds _ NoTimeout; pda[pda.timeout][childPsb] _ NoTimeout; Notify[@rebirth]; -- starts the new process executing. Its PC END; identity: {parent, child}; argsForChild: PrincOps.StateVector; argsForChild _ STATE; -- must be first! identity _ parent; WHILE ~Enter[@processLock] DO NULL ENDLOOP; IF LOOPHOLE[rebirth, ConditionVariable].condition.tail = PsbNull THEN { Exit[@processLock]; ERROR TooManyProcesses }; childPsb _ pda.block[LOOPHOLE[rebirth, ConditionVariable].condition.tail] .link.next; -- walk to tail, then to head. [] _ ChildBuilder[--my local vars--]; SELECT identity FROM parent => { Exit[@processLock]; RETURN[childPsb] }; -- return child handle to FORKing parent. child => BEGIN argsForChild.stk[argsForChild.stkptr] _ root; argsForChild.dest _ root; argsForChild.stk[argsForChild.stkptr+1] _ End; argsForChild.source _ LOOPHOLE[End, PrincOps.ControlLink]; RETURN WITH argsForChild; -- "call" root procedure of child. END; ENDCASE; END; Join: ENTRY PROCEDURE [process: PsbIndex] RETURNS [loadResults: PrincOps.FrameHandle] = BEGIN h: PsbHandle; frame: DyingFrameHandle; self: PrincOps.FrameHandle = Frame.MyLocalFrame[]; IF (h _ ValidProcess[process]) = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[process]; WHILE ProcState[h].state ~= frameReady DO WAIT frameReady ENDLOOP; frame _ LOOPHOLE[pda[h].context.frame, DyingFrameHandle]; LOOPHOLE[pda[h].flags.available, ProcessState].state _ frameTaken; BROADCAST frameTaken; -- tell child process we've got his frame. WHILE frame.state ~= dead DO WAIT dead ENDLOOP; frame.returnlink _ self.returnlink; -- We use the child frame's RETURN[frame]; -- JOINer will next Xfer to "frame", which will reload the END; Pause: PUBLIC ENTRY SAFE PROC [ticks: Process.Ticks] = TRUSTED BEGIN ENABLE ABORTED => GO TO Aborted; c: CONDITION; SetTimeout[@c, ticks]; EnableAborts[@c]; WAIT c; EXITS Aborted => RETURN WITH ERROR ABORTED; END; ValidateProcess: PUBLIC ENTRY PROCEDURE [p: PsbIndex] = { IF ValidProcess[p] = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[p] }; ValidProcess: INTERNAL PROCEDURE [p: --PsbIndex--UNSPECIFIED] RETURNS [h: PsbHandle] = INLINE { RETURN[ IF ~(LOOPHOLE[p, PsbIndex] IN [StartPsb..StartPsb+pda.count)) OR ProcState[h _ IndexToHandle[LOOPHOLE[p, PsbIndex]]].state IN [frameTaken..dead] THEN NullPsbHandle ELSE h ] }; END. LOG (For earlier log entries see Pilot 4.0 archive version.) April 29, 1980 5:53 PM Forrest Drop PSB's crossing pages on floor; move Initialize Timeout machinery to ProcessorHead. May 3, 1980 10:46 AM Forrest Mesa 6.0 Conversion. May 14, 1980 6:19 PM McJones Use OISProcessorFace.reservedNakedNotifyMask; start interrupts at end of initialization (as before). June 23, 1980 5:34 PM McJones OISProcessorFace=>ProcessorFace. August 5, 1980 5:54 PM Sandman New PSB format. September 29, 1980 10:30 AM Johnsson New ProcessTrap. January 21, 1981 9:45 AM Knutsen New PDA layout: state vector pool, timeout vector. New Fork. Fix bugs. InitializeProcesses[]. Use waiting bit in Abort. January 27, 1981 8:17 AM Knutsen Use stateVectorCount from StartList. Forking requires storing controlLinks above the stack. February 4, 1981 1:25 PM Knutsen Export priorities to Process. Enable ABORTED on Pause. PrincOps fields changed names. February 25, 1981 2:03 PM Knutsen Use stateVectorSize from StartList. Pause[0] must not wait forever. March 20, 1981 1:45 PM Fay/Sandman/Knutsen Fix AR 7493 by changing Abort procedure. March 26, 1981 5:39 PM Luniewski/McJones Abort must clear waiting flag when doing requeue. August 3, 1982 4:16 pm Levin Correct all occurrences of ~IN. August 26, 1982 11:22 am Levin Make things SAFE. äMesaRuntime>Processes.mesa (August 26, 1982 11:22 am by Levin) assert4: [LAST[Process.Ticks]..LAST[Process.Ticks]] = LAST[PSB.Ticks]; ++ waiting on AR6834. wants timeout numerically equal to NoTimeout. Queues of processes which are.. (numbered right-to-left) detached process which needs to be freed. ~~~~~~~~~~~~ Initialization ~~~~~~~~~~~~ (pagePDA is not used.) We will start at the end of the PDA and allocate downwards: Allocate StateVector pool: Find number of {PSB, timeout words} that will fit in space remaining: (considering TimeoutVector alignment and unused TimeoutVector positions.) of timeout vector) Initialize TimeoutVector: (Note: TimeoutVector unused portions overlay Psbs) Initialize first PSB to describe self - detached: TEMP until microcode uses timeout vector: mds: Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]] ]; TEMP until microcode takes current Psb off readyList: pda.ready _ Queue[tail: PsbNull]; Finish making self be detached: Note that someone must have previously set the return link of the topmost frame to PrincOps.NullFrame. Put rest of PSBs into free pool: (chained off "rebirth" condition) The free list is set so that each successive new process created will have a lower PsbIndex, thus causing the most-recently-created processes to be listed first by CoPilot. Tell CoPilot that process is not in use: TEMP until microcode uses timeout vector: Initialize FaultVector: Initialize naked-notify allocator: ~~~~~~~~~~ EXTERNAL Procedures ~~~~~~~~~~ (called when an aborted process attempts to reenter its monitor.) Acquire abortee's monitor lock: if ABORTED is made resumable, we should return [monitorEntered: TRUE] as the result of the MonitorEnter instruction that got us here. move to appropriate spot in ready queue: ~~~~~~~~~~~~ ENTRY Procedures ~~~~~~~~~~~~ ProcessTimeoutCounter from ticking.) TEMP until microcode uses timeout vector: When the top context of a process "returns", it Xfers to this procedure with its results on the stack. Wait till this process is Detached or Joined: Free any frame left over from a previous dead detached process: This process is dead. Its PSB sits in the rebirth queue until it is recycled into a new process by Fork. Our current frame however, has one of two fates: (a) If this process was detached, the frame will simply be freed by the next process that finishes ("deadFrame"). (b) if this process is being Joined, the parent process will have acquired a pointer to our frame. The JOIN code will Xfer to our frame and the code below will be executed BY THE PARENT PROCESS. The parent process therefore MUST BE RUNNING IN THE SAME MDS as the child process! Reload returned results into stack, return to parent: TEMP until microcode uses timeout vector: pChild.mds _ Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]]; is set to begin execution at the instruction after the call to ChildBuilder. Its stack is empty. Therefore, ChildBuilder MUST NOT RETURN ANY RESULTS! ("root" is automatically popped off the stack first.) Both parent and child processes will execute the following code: To simulate a procedure call, we must also store dest and source links *above* the stack since ReturnWithState doesn't. (stack pointer is *not* incremented.) Set child's top context to call End when it returns: Wait till process ready to be joined: Guaranteed to be a dying frame by the time we get here. Wait till he has finished cleaning up: At this point, we (the parent process) have acquired responsibility for the child's frame. IT MUST BE IN THE SAME MDS AS THE PARENT. return link as a mailbox to pass to the child's frame the address of the JOINer's frame, which the child should return to. results into the stack and return them to the JOINer. ~~~~~~~~~~~~ INTERNAL Procedures ~~~~~~~~~~~~ returns NullPsbHandle if invalid. Ê•˜Jšœ?™?šÏk ˜ Jšœ œ˜ Jšœ œ-˜>Jšœœ-˜8Jš œœœœœœœ˜5šœ œ˜#J˜?—Jšœœ!˜.Jšœœ'˜<šœœ˜J˜GJ˜=—šœœ˜J˜I—Jšœœ0˜Cšœœ˜ J˜LJšœ&œ˜=J˜8J˜C—Jšœœ ˜"Jšœœ˜Jšœœœ˜.Jšœ œ˜Jšœœ ˜"J˜—šœ ˜Jšœ ˜šœ/˜6J˜3—Jšœ,˜3J˜Jšœœœ˜"J˜Jšœœ8˜RJšœœ9˜SJšœœ7˜PJšœœ5˜KJ˜Jšœœœ˜0Jšœœ˜/J˜š Ïc œ œœ œœ˜8Jšœž˜9J˜!J˜—šœ œœ˜=Jšœœ ˜—šœ œœ˜;Jšœœ ˜—Jš œ œœœœ˜I—Jšœ]™]˜Jšœ œ œž˜Fšœœ œž˜IJšœ-™-J˜—Jšœœ˜-J˜šœ ž$˜0š œœ˜4Jšœœœ-˜EJ˜——Jšœœ ˜!J˜Jšœœœœ˜9J˜Jšœœœœ˜8Jšœœœœ˜&J˜Jšœ™Jšœ œž ˜!Jšœ œž+˜CJšœ œž-˜EJšœ œž1˜CJ˜Jšœœ œœ˜Jšœ œ˜šœ œž'˜9Jšœ™J˜—šœœž˜?Jšœ)™)J˜—Jšœœœœ˜*šœ œ˜J˜BJ˜—Jšœ(™(J˜šžÏnœœ œ˜:J˜DJšœ™Jš˜š Ÿœœ œœ œ˜PJšœœ3˜B—š Ÿ œœ œœ œ˜RJšœœœ ˜6—Jš œœœœ œž˜HJ˜J˜J˜J˜Jšœœ$˜-Jšœœ˜Jšœœ˜J˜Jšœ;™;Jšœ œ$˜5J˜Jšœ™Jš˜Jšœœž˜:šœ˜šœ œœ˜)J˜+J˜——Jšœœ˜J˜1š œœ œœ ˜(Jšœœ˜+šœœ˜Jšœ6˜8šœœ œ˜2Jšœ˜!—J˜"Jšœ&ž˜:J˜Jšœ˜—Jšœ˜—J˜Jšœ˜J˜Jš˜JšœE™EJšœ=™=Jšœ ™ šœœž&˜@Jšœœžœ˜?—Jšœœž˜Ašœœœœ ˜2Jšœœ˜$—Jšœ œ˜J˜šœ ˜Jš œœ)˜6Jšž2œ˜5J˜J˜Bšœœœ ž˜CJšœ™Jšœœ œ˜ Jšœœ!˜+Jšœœž%˜C—š˜Jšœœ˜-—Jšœ˜—Jšœœœ˜8Jšœ˜J˜Jšœ™Jšœ2™2šœœ ˜6J˜"Jšœ˜J˜—Jšœ1™1˜(šœœ œ˜@Jšœœ˜—šœœ&œ˜HJšœœ œ˜0—Jšœ œ˜Jšœ)™)—šœ3™3J˜Jšœ$ž˜:Jšœ5™5——šœ!™!J˜"J˜Jšœ™Jšœf™fšœ3˜6Jšœ˜šœ,˜2˜Jšœ&˜.Jšœ˜——Jšœ˜J˜—JšœC™CJšœ@™@JšœB™BJšœ(™(Jš˜J˜!J˜*Jšœ6˜>šœœ˜-Jšœœœ œ˜EJšœ(™(Jšœ<˜DJšœ)™)J˜š˜šœ˜ Jšœ8˜@——Jšœ˜—Jšœ˜J˜Jšœ™šœœ ˜$˜˜Jšœ&œ œ˜>——Jšœ˜J˜—Jšœ"™"J˜3J˜Jšœ ž˜4Jšœ˜J˜Jšœ)™)J˜šœœž ˜"š œœœœ œ˜3šœœ ˜*Jšœœ˜J˜———šœœž ˜#š œœœœ œ˜3J˜#J˜——šœœž ˜!š œœœœ œ˜3šœœ ˜*Jšœœ˜J˜———š Ÿ œœž œ œœ œ˜HJšœœœœ˜:J˜—š Ÿ œœž œœ œ˜FJšœ œ˜0Jšœ ˜&J˜—šœœž ˜(Jš œ œœœ œ˜Hšœœ!˜+šœ(œ œ˜>Jšœ œœœ ˜AJ˜———šœœž ˜&š œ œœœ œ˜3Jšœœ œ˜5J˜——š Ÿ œœž œœ œ˜JJšœ˜!šœœ˜ šœœœ˜&J˜%Jšœœž˜4Jšœ+˜/—J˜(J˜——š Ÿ œž œ œœœ˜7JšœA™AJš˜J˜šŸœ œœœ œœœ˜@Jšœ˜"J˜—šŸ œ œž œ˜FJšœœ˜Jšœ˜"J˜—J˜Jšœœ œ˜=Jšœ œ˜Jšœ™š˜šœœ˜J˜)Jš œœœœ œ˜>—Jšœœ˜ —J˜!Jšœ ž˜?Jšœœ˜#Jšœœ˜JšœE™EJšœ?™?Jšœ˜J˜—šŸ œœž œ œ˜BJš˜J˜ J˜J˜Jšœ&ž ˜FJšœ(™(J˜,Jšœ˜J˜—šœœž ˜#Jš œ œœœ˜@Jš˜˜Jšœœœ-˜Jšœœœœ˜JJ˜—š Ÿ œœž œœ œ˜>Jšœ˜(šœœ˜Jšœ œ8˜GJšœœ˜Jšœ-˜1J˜——š Ÿœœž œœ œ˜3šœ(ž ˜HJ˜6J˜——Jšœ*™*J˜šŸœœœ œ˜3Jš˜J˜ šœ-˜3Jšœœœ˜*—šœ&ž˜4Jšœ$™$—šœœ˜$Jšœœ˜Jšœœž˜7šœœ˜J˜/Jšœ)™)Jšœž œ˜"Jšœœ˜——J˜#Jšœ˜J˜—š žŸœœœœ˜@Jš œœœœ œœ˜5Jš˜J˜šœœ˜ Jšœœœ˜:šœœ˜+Jš˜Jšœœ˜,Jšœœ˜%Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜#Jšœ˜J˜—šžœ˜-Jš œœœœœ œ˜-Jš˜J˜šœœ˜ Jšœœ œœ˜@š œœœœœ œ˜GJš˜Jšœœœ˜Jšœž+˜DJšœ-™-šœ ˜%Jšœ˜˜Jšœ)˜1—Jšœ%œœœ˜:Jšœ˜—Jšœ?™?Jšœœœ%œ˜Bšœ˜Jšœž,˜@—Jšœž˜4Jšœ4˜™>Jšœ*™*Jšœ0™0Jšœ@™@Jšœ0™0Jšœ<™™>Jšœ0™0Jšœ4™4Jšœ5™5Jšœ"œœœ˜7Jšœœ+žH˜J˜&J˜Jšœ5™5Jšœœ ˜Jšœ˜J˜—šŸœž œ œžœ˜IJšœ˜Jš˜Jš œ œœœœ˜*J˜š Ÿ œœžœœžœ˜FJš˜Jšœœœœ*˜AJšœœ˜;šœœ˜Jšœœœ˜*—J˜*šœœ'˜2Jšœ)œ˜@—J˜Jšœœ˜J˜4˜Jšœ œ&œ˜BJšœœ œ˜0—Jšœœ%˜EJšœ)™)—šœ9™9J˜J˜'šœž,˜?Jšœ;™;Jšœ1™1Jšœ)™)—Jšœ˜J˜J˜J˜#Jšœ5™5Jšœœž˜(J˜Jšœœœœ˜+šœœ6˜EJšœœ˜/—šœœ,˜IJšœ ž˜+—Jšœžœ˜%Jšœ@™@šœ ˜˜ ˜Jšœž)˜>——˜Jš˜Jšœ9™9Jšœ=™=Jšœ%™%J˜-J˜Jšœ4™4J˜.Jšœœ˜:Jšœœž"˜=Jšœ˜—Jšœ˜—Jšœ˜J˜——šŸœœ œ˜)Jšœ&˜-Jš˜J˜ J˜J˜2šœ-˜3Jšœœœ˜*—Jšœ%™%šœ"˜)Jšœ œ˜—Jšœ7™7Jšœœ)˜9Jšœ:˜BJš œž*˜AJšœ&™&Jšœœœœ˜/JšœD™DJšœB™Bšœ$ž˜?JšœA™AJšœ8™8—šœ ž:˜JJšœ5™5—Jšœ˜J˜—š Ÿœœœœœ˜>Jš œœœœœ ˜(Jšœ œ˜ J˜J˜Jšœ˜Jš œ œœœœ˜,Jšœ˜J˜—šŸœœœ œ˜7šœœ!˜)Jšœœœ˜&J˜——Jšœ-™-J˜š Ÿ œœ œž œ˜=Jšœ˜Jšœ!™!š œœœœœ ˜Gšœœ˜