DIRECTORY Basics USING [BITSHIFT, LongNumber], MesaRuntimeInit USING [StateVectorCounts], PrincOps USING [Condition, ConditionVariable, ControlLink, FaultIndex, Frame, FrameHandle, FrameSizeIndex, InterruptItem, NoTimeout, NullFrame, NullLink, NullPsbHandle, NullStateVectorHandle, PDA, PDABase, Priority, ProcessDataArea, ProcessState, ProcessStateBlock, PsbHandle, PsbIndex, PsbLink, PsbNull, Queue, SD, sFork, sJoin, sProcessTrap, StartPsb, StateVector, StateVectorHandle, Ticks], Process USING [DisableTimeout, EnableAborts, Milliseconds, Priority, priorityFaultHandlers, SetPriority, SetTimeout, Ticks], ProcessBackdoor USING [ParentChildCallback, SingleCallback], PrincOpsUtils USING [Alloc, Broadcast, ConditionPointer, DisableInterrupts, EnableAndRequeue, EnableInterrupts, Enter, Exit, FrameSize, Free, GetReturnFrame, LongCopy, MyLocalFrame, Notify, PsbHandleToIndex, PsbIndexToHandle, ReadPSB, ReEnter, Requeue, Wait, WritePSB, WriteWDC]; ProcessImpl: MONITOR [pdaWords: CARDINAL, svCounts: MesaRuntimeInit.StateVectorCounts, wordsPerSV: CARDINAL, reservedNakedNotifyMask: WORD, millisecondsPerTick: CARDINAL] LOCKS processLock IMPORTS Basics, PrincOpsUtils, Process EXPORTS MesaRuntimeInit, PrincOpsUtils, Process, ProcessBackdoor = { OPEN PrincOps; ConditionPointer: TYPE = PrincOpsUtils.ConditionPointer; FsiFrame: TYPE = MACHINE DEPENDENT RECORD [ fsi (0): FrameSizeIndex, -- must be at 3 MOD 4 boundary. frame (1): local Frame]; NakedNotifyLevel: TYPE = [0..--VM.bitsPerWord--16); DyingFrameHandle: TYPE = POINTER TO dying Frame; ParentChildCallback: TYPE = ProcessBackdoor.ParentChildCallback; SingleCallback: TYPE = ProcessBackdoor.SingleCallback; processLock: MONITORLOCK; rebirth: CONDITION; -- queue of unused processes. frameReady: CONDITION; -- queue of parents waiting to rejoin their child. frameTaken: CONDITION; -- queue of children waiting to rejoin their parent. dead: CONDITION; -- queue of parents waiting for their child to clean up. deadFrame: DyingFrameHandle _ NIL; -- the top-level frame of a detached process which needs to be freed. busyLevels: PACKED ARRAY NakedNotifyLevel OF BOOL; ForkProc: ParentChildCallback _ NIL; JoinProc: ParentChildCallback _ NIL; DetachProc: SingleCallback _ NIL; EndProc: SingleCallback _ NIL ; MsecToTicks: PUBLIC SAFE PROC [ms: Process.Milliseconds] RETURNS [Process.Ticks] = TRUSTED { RETURN[ (IF ms >= Process.Milliseconds.LAST-(millisecondsPerTick-1) THEN Process.Milliseconds.LAST -- (avoid overflow) ELSE ms + millisecondsPerTick-1) / millisecondsPerTick ] }; SecondsToTicks: PUBLIC SAFE PROC [sec: CARDINAL] RETURNS [Process.Ticks] = TRUSTED { ticks: Basics.LongNumber = [lc[(sec.LONG*1000 + millisecondsPerTick-1) / millisecondsPerTick]]; RETURN[IF ticks.highbits ~= 0 THEN Ticks.LAST ELSE ticks.lowbits] }; TicksToMsec: PUBLIC SAFE PROC [ticks: Ticks] RETURNS [Process.Milliseconds] = TRUSTED { RETURN[ IF ticks > Process.Milliseconds.LAST/millisecondsPerTick THEN Process.Milliseconds.LAST ELSE ticks*millisecondsPerTick ] }; Detach: PUBLIC ENTRY PROC [process: PROCESS] = { h: PsbHandle; px: PsbIndex = LOOPHOLE[process]; IF (h _ ValidProcess[px]) = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[process]; IF DetachProc # NIL THEN DetachProc[LOOPHOLE[process]]; PDA[h].flags.processState.detached _ TRUE; BROADCAST frameTaken; -- wake child if waiting to JOIN. }; Abort: PUBLIC ENTRY PROC [process: PROCESS] = { h: PsbHandle; px: PsbIndex = LOOPHOLE[process]; IF (h _ ValidProcess[px]) = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[process]; IF h = PrincOpsUtils.ReadPSB[] THEN { PDA[h].flags.abort _ FALSE; RETURN WITH ERROR ABORTED } ELSE { PrincOpsUtils.DisableInterrupts[]; -- (also stops ProcessTimeoutCounter from ticking.) IF ProcState[h].state = alive THEN { PDA[h].flags.abort _ TRUE; IF PDA[h].flags.waiting THEN { PDA[h].flags.waiting _ FALSE; PDA[PDA.timeout][PrincOpsUtils.PsbHandleToIndex[h]] _ NoTimeout; PDA[h].--timeout--mds _ NoTimeout; PrincOpsUtils.Requeue[NIL, @PDA.ready, h]; }; }; PrincOpsUtils.EnableInterrupts[]; }; }; CheckForAbort: PUBLIC ENTRY SAFE PROC = TRUSTED { h: PsbHandle = PrincOpsUtils.ReadPSB[]; IF PDA[h].flags.abort THEN { PDA[h].flags.abort _ FALSE; RETURN WITH ERROR ABORTED }; }; Pause: PUBLIC ENTRY SAFE PROC [ticks: Process.Ticks] = TRUSTED { ENABLE ABORTED => GO TO Aborted; c: CONDITION; Process.SetTimeout[@c, ticks]; Process.EnableAborts[@c]; WAIT c; EXITS Aborted => RETURN WITH ERROR ABORTED; }; SetTimeSlice: PUBLIC ENTRY SAFE PROC [ticks: Process.Ticks] = TRUSTED { IF pSchedulingInterval = NIL THEN RETURN; pSchedulingInterval^.timeout _ ticks; NOTIFY pSchedulingInterval^; }; CountFreeProcesses: PUBLIC ENTRY SAFE PROC RETURNS [freeProcesses: CARDINAL _ 0] = TRUSTED { pbsIndex: PrincOps.PsbIndex; firstPBSIndex: PrincOps.PsbIndex ; pbsIndex _ LOOPHOLE[rebirth, ConditionVariable].condition.tail; firstPBSIndex _ pbsIndex; IF pbsIndex # PrincOps.PsbNull THEN DO freeProcesses _ freeProcesses + 1; pbsIndex _ PrincOps.PDA.block[pbsIndex].link.next; IF pbsIndex = firstPBSIndex OR pbsIndex = PrincOps.PsbNull THEN EXIT; ENDLOOP; }; RegisterEventProcs: PUBLIC SAFE PROC [forkProc: ParentChildCallback, joinProc: ParentChildCallback, endProc: SingleCallback, detachProc: SingleCallback] RETURNS [oldForkProc: ParentChildCallback, oldJoinProc: ParentChildCallback, oldEndProc: SingleCallback, oldDetachProc: SingleCallback] = CHECKED { oldForkProc _ ForkProc; ForkProc _ forkProc; oldJoinProc _ JoinProc; JoinProc _ joinProc; oldEndProc _ EndProc ; EndProc _ endProc ; oldDetachProc _ DetachProc; DetachProc _ detachProc; }; ValidateProcess: PUBLIC ENTRY PROC [p: PROCESS] = { IF ValidProcess[LOOPHOLE[p]] = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[p]; }; InvalidProcess: PUBLIC ERROR [process: PROCESS] = CODE; AllocateNakedCondition: PUBLIC ENTRY PROC RETURNS [cv: ConditionPointer, mask: WORD] = { FOR level: NakedNotifyLevel IN NakedNotifyLevel DO IF ~busyLevels[level] THEN { busyLevels[level] _ TRUE; RETURN[ cv: LOOPHOLE[@PDA.interrupt[level]], mask: Basics.BITSHIFT[1, NakedNotifyLevel.LAST - level] ]; }; ENDLOOP; ERROR }; DeallocateNakedCondition: PUBLIC ENTRY PROC [cv: ConditionPointer] = { FOR level: NakedNotifyLevel IN NakedNotifyLevel DO IF busyLevels[level] AND cv = LOOPHOLE[@PDA.interrupt[level], ConditionPointer] THEN { busyLevels[level] _ FALSE; PDA.interrupt[level].condition.tail _ PsbNull; RETURN; }; ENDLOOP; ERROR; }; ProcState: PROC [psbh: PsbHandle] RETURNS [ProcessState] = INLINE { RETURN[PDA[psbh].flags.processState] }; ProcessTrap: PROC RETURNS [BOOL] = { LongEnter: PROC [a,b: --LONG POINTER TO MONITORLOCK--WORD] RETURNS [BOOL] = LOOPHOLE[PrincOpsUtils.Enter]; abortee: FrameHandle; state: RECORD [filler: WORD, v: StateVector]; state.v _ STATE; UNTIL (IF state.v.stkptr = 4 THEN LongEnter[state.v.stk[0], state.v.stk[1]] ELSE PrincOpsUtils.Enter[LOOPHOLE[state.v.stk[0], POINTER TO MONITORLOCK]]) DO ENDLOOP; abortee _ PrincOpsUtils.GetReturnFrame[]; abortee.pc _ [abortee.pc + 1]; -- step past ME(L) instruction. PDA[PrincOpsUtils.ReadPSB[]].flags.abort _ FALSE; ERROR ABORTED; }; ValidProcess: INTERNAL PROC [p: PsbIndex] RETURNS [h: PsbHandle] = INLINE { IF p < StartPsb OR p > LOOPHOLE[PDA.count+StartPsb, PsbIndex] THEN RETURN [NullPsbHandle]; h _ PrincOpsUtils.PsbIndexToHandle[p]; IF ProcState[h].state IN [frameTaken..dead] THEN RETURN [NullPsbHandle]; }; Fork: PROC [--argsForRoot,-- root: ControlLink] RETURNS [childPsb: PsbIndex] = { PForkFrame: TYPE = POINTER TO FRAME[Fork]; ChildBuilder: PROC [--parent's locals--] RETURNS [--MUST BE NULL!--] = { pChild: LONG POINTER TO ProcessStateBlock _ @PDA.block[childPsb]; parentFrame: PForkFrame = LOOPHOLE[PrincOpsUtils.GetReturnFrame[]]; fsi: FrameSizeIndex = LOOPHOLE[parentFrame-1, POINTER TO FsiFrame].fsi; childFrame: PForkFrame = PrincOpsUtils.Alloc[fsi]; PrincOpsUtils.LongCopy[ from: parentFrame, to: childFrame, nwords: PrincOpsUtils.FrameSize[fsi]]; childFrame.identity _ child; pChild.link.failed _ FALSE; pChild.link.priority _ PDA[PrincOpsUtils.ReadPSB[]].link.priority; pChild.flags _ [ processState: [state: alive, detached: FALSE], cleanup: PsbNull, waiting: FALSE, abort: FALSE ]; pChild.context _ [frame[LOOPHOLE[childFrame, FrameHandle]]]; pChild.mds _ NoTimeout; PDA[PDA.timeout][childPsb] _ NoTimeout; PrincOpsUtils.Notify[@rebirth]; }; identity: {parent, child}; argsForChild: StateVector; argsForChild _ STATE; -- must be first! ("root" is automatically popped off the stack first.) identity _ parent; UNTIL PrincOpsUtils.Enter[@processLock] DO NULL ENDLOOP; WHILE LOOPHOLE[rebirth, ConditionVariable].condition.tail = PsbNull DO PrincOpsUtils.Wait[@processLock, @dead, LOOPHOLE[dead, ConditionVariable].timeout]; UNTIL PrincOpsUtils.ReEnter[@processLock, @dead] DO NULL ENDLOOP; ENDLOOP; childPsb _ PDA.block[LOOPHOLE[rebirth, ConditionVariable].condition.tail].link.next; -- walk to tail, then to head. [] _ ChildBuilder[--my local vars--]; SELECT identity FROM parent => { IF ForkProc # NIL THEN ForkProc[PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]], childPsb]; PrincOpsUtils.Exit[@processLock]; RETURN[childPsb]}; -- return child handle to FORKing parent. child => { argsForChild.stk[argsForChild.stkptr] _ LOOPHOLE[root]; argsForChild.dest _ root; argsForChild.stk[argsForChild.stkptr+1] _ LOOPHOLE[End, WORD]; argsForChild.source _ LOOPHOLE[End, ControlLink]; IF ForkProc # NIL THEN { -- insure ForkProc was called in parent before child returns UNTIL PrincOpsUtils.Enter[@processLock] DO NULL ENDLOOP; PrincOpsUtils.Exit[@processLock]; }; RETURN WITH argsForChild; -- "call" root procedure of child. }; ENDCASE; }; End: PROC = { sv: RECORD [filler: WORD, results: StateVector]; frame: DyingFrameHandle; h: PsbHandle; sv.results _ STATE; -- save stack containing returned results. h _ PrincOpsUtils.ReadPSB[]; IF EndProc # NIL THEN EndProc[Psb: PrincOpsUtils.PsbHandleToIndex[h]]; UNTIL PrincOpsUtils.Enter[@processLock] DO NULL ENDLOOP; frame _ LOOPHOLE[PrincOpsUtils.MyLocalFrame[]]; frame.state _ alive; PDA[h].flags.processState.state _ frameReady; PDA[h].flags.abort _ FALSE; -- too late for Aborts: they no-op PrincOpsUtils.Broadcast[@frameReady]; -- wake any parent process waiting to Join. UNTIL ProcState[h].state = frameTaken OR ProcState[h].detached DO PrincOpsUtils.Wait[@processLock, @frameTaken, LOOPHOLE[frameTaken, ConditionVariable].timeout]; WHILE ~PrincOpsUtils.ReEnter[@processLock, @frameTaken] DO ENDLOOP; ENDLOOP; IF deadFrame ~= NIL THEN {PrincOpsUtils.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. PDA[h].flags.processState.state _ dead; PrincOpsUtils.Broadcast[@dead]; -- tell parent our frame has been left for freeing. PrincOpsUtils.Wait[@processLock, @rebirth, LOOPHOLE[rebirth, ConditionVariable].timeout]; UNTIL PrincOpsUtils.ReEnter[@processLock, @rebirth] DO ENDLOOP; sv.results.dest _ LOOPHOLE[frame.returnlink, ControlLink]; -- (frame.returnlink was set by Join[] to point to the context of JOIN.) sv.results.source _ NullLink; PrincOpsUtils.Exit[@processLock]; RETURN WITH sv.results }; Join: ENTRY PROC [process: PROCESS] RETURNS [loadResults: FrameHandle] = { h: PsbHandle; px: PsbIndex = LOOPHOLE[process]; frame: DyingFrameHandle; self: FrameHandle = PrincOpsUtils.MyLocalFrame[]; IF (h _ ValidProcess[px]) = NullPsbHandle THEN RETURN WITH ERROR InvalidProcess[process]; IF JoinProc # NIL THEN JoinProc[PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]], px]; WHILE ProcState[h].state ~= frameReady DO WAIT frameReady ENDLOOP; frame _ LOOPHOLE[PDA[h].context.frame, DyingFrameHandle]; PDA[h].flags.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; RETURN[frame] -- JOINer will next Xfer to "frame", which will reload the results into the stack and return them to the JOINer. }; defaultInterval: Process.Milliseconds = 100; schedulerPriority: Process.Priority = Process.priorityFaultHandlers; pSchedulingInterval: ConditionPointer _ NIL; Schedule: ENTRY PROC = { QueueToPsbHandle: PROC [queue: Queue] RETURNS [PsbHandle] = INLINE {RETURN[PrincOpsUtils.PsbIndexToHandle[queue.tail]]}; schedulingLevels: PACKED ARRAY Process.Priority OF BOOL _ [FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE]; schedulingInterval: CONDITION _ [timeout: MsecToTicks[defaultInterval]]; pSchedulingInterval _ @schedulingInterval; -- enable use of SetTimeSlice Process.SetPriority[schedulerPriority]; DO WAIT schedulingInterval; FOR priority: Process.Priority DECREASING IN [FIRST[Process.Priority]..schedulerPriority) DO IF schedulingLevels[priority] THEN { psb: PsbHandle; PrincOpsUtils.DisableInterrupts[]; psb _ PrincOpsUtils.PsbIndexToHandle[PDA[QueueToPsbHandle[PDA.ready]].link.next]; DO SELECT PDA[psb].link.priority FROM = priority => {PrincOpsUtils.Requeue[@PDA.ready, @PDA.ready, psb]; EXIT}; < priority => EXIT; ENDCASE => IF psb = QueueToPsbHandle[PDA.ready] THEN EXIT; psb _ PrincOpsUtils.PsbIndexToHandle[PDA[psb].link.next]; ENDLOOP; PrincOpsUtils.EnableInterrupts[]; }; ENDLOOP; ENDLOOP; }; Initialize: PROC = { AlignUp: PROC [unaligned, modulus: WORD] RETURNS [aligned: WORD] = INLINE {RETURN[ ((unaligned + modulus-1) / modulus) * modulus]}; AlignDown: PROC [unaligned, modulus: WORD] RETURNS [aligned: WORD] = INLINE {RETURN[unaligned - unaligned MOD modulus]}; rAlloc: PDABase RELATIVE POINTER TO WORD _ LOOPHOLE[pdaWords]; -- allocation pointer. Process.DisableTimeout[@dead]; Process.DisableTimeout[@frameReady]; Process.DisableTimeout[@frameTaken]; Process.DisableTimeout[@rebirth]; SD[sProcessTrap] _ LOOPHOLE[ProcessTrap]; SD[sFork] _ LOOPHOLE[Fork]; SD[--sCedarFork-- 153B] _ LOOPHOLE[Fork]; -- temporary until Compiler is fixed SD[sJoin] _ LOOPHOLE[Join]; { alignmentStateVector: CARDINAL = 4; -- (a D0 requirement) sizeStateVector: CARDINAL = AlignUp[MAX[SIZE[StateVector], wordsPerSV], alignmentStateVector]; rState: StateVectorHandle _ LOOPHOLE[AlignDown[LOOPHOLE[rAlloc], alignmentStateVector]]; FOR pri: Priority IN Priority DO PDA.state[pri] _ NullStateVectorHandle; FOR k: CARDINAL IN [0..svCounts[pri]) DO IF LOOPHOLE[rState, CARDINAL] < sizeStateVector THEN ERROR; rState _ rState - sizeStateVector; PDA[rState].stk[0] _ LOOPHOLE[PDA.state[pri]]; -- chain onto list. PDA.state[pri] _ LOOPHOLE[rState]; ENDLOOP; ENDLOOP; rAlloc _ LOOPHOLE[rState]; }; { sizePSBTimeout: CARDINAL = SIZE[ProcessStateBlock] + SIZE[Ticks]; alignmentTimeoutVector: CARDINAL = 16; -- (PrincOps requirement) rStartPsb: PDABase RELATIVE POINTER TO WORD = LOOPHOLE[PrincOpsUtils.PsbIndexToHandle[StartPsb]]; totalPsbs: CARDINAL; PDA.count _ 0; FOR nUseful: CARDINAL DECREASING IN [0..(rAlloc - rStartPsb)/sizePSBTimeout] DO totalPsbs _ StartPsb + nUseful; PDA.timeout _ LOOPHOLE[AlignDown[LOOPHOLE[rAlloc - totalPsbs], alignmentTimeoutVector]]; IF (LOOPHOLE[PDA.timeout, CARDINAL] + StartPsb -- (first used word of timeout vector) - LOOPHOLE[rStartPsb, CARDINAL]) / SIZE[ProcessStateBlock] >= nUseful THEN {PDA.count _ nUseful; EXIT}; -- that many Psbs fit in available space. REPEAT FINISHED => ERROR; ENDLOOP; IF PDA.count <= 1 THEN ERROR; }; 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: [ processState: [state: alive, detached: TRUE], cleanup: PsbNull, waiting: FALSE, abort: FALSE], context: NULL, mds: NoTimeout ]; FOR root: FrameHandle _ PrincOpsUtils.MyLocalFrame[], root.returnlink.frame DO IF root.returnlink.frame = NullFrame THEN { root.returnlink _ LOOPHOLE[End, procedure ControlLink]; EXIT}; ENDLOOP; PrincOpsUtils.WritePSB[PrincOpsUtils.PsbIndexToHandle[StartPsb]]; -- tell the processor. PDA.ready _ Queue[tail: StartPsb]; { 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; PDA.block[psb].flags.processState.state _ dead; PDA.block[psb].mds _ NoTimeout; REPEAT FINISHED => LOOPHOLE[rebirth, ConditionVariable].condition.tail _ firstFree; ENDLOOP; }; FOR fi: FaultIndex IN FaultIndex DO PDA.fault[fi] _ [queue: [tail: PsbNull], condition: [tail: PsbNull, abortable: FALSE, wakeup: FALSE]]; ENDLOOP; busyLevels _ LOOPHOLE[reservedNakedNotifyMask]; PrincOpsUtils.WriteWDC[0]; -- start interrupts. Detach[FORK Schedule[]]; }; Initialize[]; }. ^ProcessImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Levin on September 28, 1983 12:03 pm Birrell on August 25, 1983 4:29 pm Russ Atkinson (RRA) February 19, 1985 1:08:25 pm PST Bob Hagmann May 3, 1985 11:15:51 am PDT Types and Related Declarations Global Variables (protected by processLock) This really should be protected by a completely separate monitor from the above, but it isn't worth the trouble. procedures to call during fork, join, detach and end Exported to Process Initializing monitors and condition variables Detaching processes Aborting a process Wake the victim up. TEMP until microcode uses timeout vector: Control of Scheduling Process Monitoring Process validation Exported to PrincOpsUtils Internal Procedures This procedure is invoked by microcode 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. This procedure returns NullPsbHandle if "p" is invalid. Implementation of Language Constructs (FORK and JOIN) TEMP until microcode uses timeout vector: pChild.mds _ Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]]; The following starts the new process executing. Its PC is set to begin execution at the instruction after the call to ChildBuilder. Its stack is empty. Therefore, ChildBuilder MUST NOT RETURN ANY RESULTS! 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: 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: 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. We use the child frame's 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. Time Slicer The following is intended to be altered only by the debugger. Note: this procedure only touches resident structures and does not call any procedures. It therefore can never consume a state vector and consequently does not need a reserved one, even though it is executing at the reserved priority level. Note that readyList can't be empty, since we are running! Initialization This procedure is implicitly parameterized by the module parameters. Initialize monitored variables and SD Allocate StateVector pool: Find number of {PSB, timeout words} that will fit in space remaining, considering TimeoutVector alignment and unused TimeoutVector positions. Initialize TimeoutVector: (Note: TimeoutVector unused portions overlay Psbs) Set up the executing context as a legitimate process (detached) in the first Psb: TEMP until microcode uses timeout vector: mds: Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]] ]; Note: the following assumes that someone has previously set the return link of the topmost frame to NullFrame. Initialize the ready list Put rest of PSBs into free pool: (chained off "rebirth" condition) TEMP until microcode uses timeout vector: Initialize FaultVector: Initialize naked-notify allocator: Start the time slicer Bob Hagmann January 30, 1985 3:29:28 pm PST add ProcessBackdoor changes to: ParentChildCallback, SingleCallback, Detach, CountFreeProcesses, RegisterEventProcs, Fork, End, Join, DIRECTORY, ParentChildCallback, SingleCallback Κ™– "Cedar" style˜codešœ™Kšœ Οmœ1™Kšœžœ˜1šžœ žœžœŸ<˜UKšžœ#žœžœžœ˜8Kšœ!˜!K˜—KšžœžœŸ"˜=Kšœ˜—Kšžœ˜—Kšœ˜—K˜š‘œžœ˜ Kšœf™fKšœžœ žœ˜0K˜K˜ Kšœ žœŸ*˜?Kšœ˜Jšžœ žœžœ1˜FKšžœ#žœžœžœ˜8Kšœžœ˜/K˜Kšžœ*˜-KšžœžœŸ"˜>Kšœ'Ÿ+˜RKšœ-™-šžœ!žœž˜AKšœ.žœ)˜_Kšžœ3žœžœ˜CKšžœ˜—Kšœ?™?Kšžœžœžœ-žœ˜JKšžœžœŸ,˜^KšœŸ˜4Kšžœ$˜'Kšœ!Ÿ3˜TKšœ+žœ&˜Yšœ›™›Kšœq™qKšœ—™——Kšžœ/žœžœ˜?Kšœžœ"ŸH˜„K˜Kšœ!˜!Kšœ5™5Kšžœžœ ˜Kšœ˜—K˜š ‘œžœžœ žœžœ˜JK˜ Kšœžœ ˜!K˜Kšœ1˜1šžœ(ž˜.Kšžœžœžœ˜*—Kšžœ žœžœG˜]Kšœ%™%Kšžœ"žœžœ žœ˜BKšœ7™7Kšœžœžœ%˜9Kšžœ*˜-Kšž œŸ*˜AKšœ&™&Kšžœžœžœžœ˜/KšœˆŸœ{™›Kšœ#˜#Kšžœ Ÿp˜Kšœ˜—K™—K™ K˜K˜,K˜DK˜Kšœ(žœ˜,K˜š‘œžœžœ˜š‘œžœžœž˜BKšœžœ.˜5—Kšœ=™=š œžœžœžœžœ˜9Kšœžœžœžœžœžœžœžœžœ˜5—Kšœž œ+˜HKšœ,Ÿ˜IKšœρ™ρK˜'šž˜Kšžœ˜š žœž œžœžœ'ž˜\šžœžœ˜$K˜K˜"Kšœ9™9Kšœ%žœžœ˜Qšž˜šžœžœž˜"Kšœ&žœ žœžœ˜IKšœžœ˜Kš žœžœžœžœžœ˜:—Kšœ%žœ˜9Kšžœ˜—K˜!K˜—Kšžœ˜—Kšžœ˜—K˜K˜K˜—Kšœ™˜š‘ œžœ˜KšœD™Dš ‘œžœžœžœ žœ˜BKšžœžœ2˜@—š ‘ œžœžœžœ žœ˜DKšžœžœžœ ˜4—Kš œžœžœžœžœžœ Ÿ˜VK™Kšœ%™%K˜K˜$K˜$K˜!Kšžœžœ˜)Kšžœ žœ˜KšžœŸœ žœ Ÿ$˜OKšžœ žœ˜K˜Kšœ™šœ˜KšœžœŸ˜:šœžœ˜Kšœžœžœ2˜B—Kšœžœ žœ!˜Xšžœžœ ž˜ Kšžœ$˜'šžœžœžœž˜(Kš žœžœ žœžœžœ˜;K˜"KšžœžœžœŸ˜CKšžœžœ ˜"Kšžœ˜—Kšžœ˜—Kšœ žœ ˜Kšœ˜—K˜šœ˜Kšœ™Kšœžœžœžœ˜AKšœžœŸ˜Aš œžœžœžœžœ˜-Kšžœ+˜3—Kšœ žœ˜Kšžœ ˜š žœ žœž œžœ*ž˜OK˜Kšžœ žœ žœ/˜Xš žœžœžœ žœŸ&˜VKšœžœ žœžœ!ž˜LKšœžœžœŸ)˜G—šž˜Kšžœžœ˜—Kšžœ˜—Kšžœžœ žœžœ˜Kšœ˜—K˜KšœL™Lšžœžœžœž˜6Kšžœžœ˜"Kšžœ˜K˜—KšœQ™Qšžœ%˜(Kšœžœ žœ$žœ˜Ošœ˜Kšœ'žœ˜-Kšœžœ žœ˜0—Kšœ žœ˜Kšœ)™)Kšœ3™3K˜K˜—Kšœn™nšžœIž˜Nšžœ#žœ˜+Kšœžœ˜7Kšžœ˜—Kšžœ˜—KšœCŸ˜YK™Kšœ™Kšžœ˜"K˜KšœC™Cšœ˜K˜!Kšœžœ ˜*Kšžœ6˜>šžœžœž˜-Kšžœžœžœ žœ˜EKšžœ,˜/Kšœ)™)Kšžœ˜šž˜Kšžœžœ8˜L—Kšžœ˜—Kšœ˜—K˜Kšœ™šžœžœ ž˜#KšžœLžœ žœ˜fKšžœ˜K˜—Kšœ"™"Kšœ žœ˜/K˜KšœŸ˜0K™K™Kšœžœ ˜Kšœ˜—K˜K˜ K˜—šœ˜K™šœ+™+K™Kšœ Οr”™ —K™K™K™K™Kš’™——…—ARhI