<Processes.mesa (August 26, 1982 11:22 am by Levin)>> 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 <<(numbered right-to-left)>> deadFrame: DyingFrameHandle _ NIL; -- the top-level frame of a <> Bug: PRIVATE ERROR [type: BugType] = CODE; BugType: TYPE = {insufficientMappedPDA, noMoreNakedNotifyLevels, noSuchCondition}; <<~~~~~~~~~~~~ Initialization ~~~~~~~~~~~~>> --RuntimePrograms.--InitializeProcesses: PUBLIC PROCEDURE [pagePDA: Environment.PageNumber, countPDA: Environment.PageCount] = <<(pagePDA is not used.)>> 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]> <<(considering TimeoutVector alignment and unused TimeoutVector>> <> sizePSBTimeout: CARDINAL -- (does not include alignment filler) = SIZE[ProcessStateBlock] + --interruptVectorWord--SIZE[Ticks]; alignmentTimeoutVector: CARDINAL = 16; -- (PrincOps requirement) rStartPsb: PDABase RELATIVE POINTER TO UNSPECIFIED = LOOPHOLE[IndexToHandle[StartPsb]]; totalPsbs: CARDINAL; pda.count _ 0; FOR nUseful: CARDINAL DECREASING IN [0..(rAlloc - rStartPsb)/sizePSBTimeout] --UNTIL nUseful Psbs will fit in available space-- DO totalPsbs _ StartPsb + nUseful; pda.timeout _ AlignDown[rAlloc-totalPsbs, alignmentTimeoutVector]; IF (LOOPHOLE[pda.timeout, CARDINAL]+StartPsb -- (first used word <> - LOOPHOLE[rStartPsb, CARDINAL]) / SIZE[ProcessStateBlock] >= 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; <> <<(Note: TimeoutVector unused portions overlay Psbs)>> 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; <<~~~~~~~~~~ EXTERNAL Procedures ~~~~~~~~~~>> 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] = <<(called when an aborted process attempts to reenter its monitor.)>> 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[]] }; <<~~~~~~~~~~~~ ENTRY Procedures ~~~~~~~~~~~~>> 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]; <> <> <> <<(a) If this process was detached, the frame will simply be freed>> <> <<(b) if this process is being Joined, the parent process will>> <> <> <> <> 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; <<("root" is automatically popped off the stack first.)>> 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 <> <> <<(stack pointer is *not* incremented.)>> 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] }; <<~~~~~~~~~~~~ INTERNAL Procedures ~~~~~~~~~~~~>> 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.