-- Copyright (C) 1986 by Xerox Corporation. All rights reserved. -- CRuntimeA.mesa -- NFS 5-Mar-86 17:37:14 -- MEW 14-Apr-86 13:25:52 DIRECTORY BcdOps USING [BcdBase], AtomVariants USING [ATRecord], BucketAlloc USING [BucketInfo, Initialize], CAbort USING [], CBasics USING [ArraySpaceHandle, CommonRecPtr], CRuntime USING [abortOutcome, FilePtr], CRuntimeInternal USING [ ArraySpace, ConfigEntry, ConfigHandle, DeleteAndLogHeap, DeleteAndLogStream, GFEntry, GFIndex, GFList, GFTable, gfTableSize, lock, LogCleanUp, ProcessEntry, ProcessIndex, ProcessList, ProcessTable, processTableSize, SHEntry, SHIndex, SHList, SHTable, shTableSize, StreamEntry, StreamList], Environment USING [PageCount, Word, wordsPerPage], File USING [Create, Delete, Error, File, GetSize], FileTypes USING [tUntypedFile], Frame USING [GetReturnFrame, ReadGlobalLink], Heap USING [Create, Delete, Flush, MakeNode], Inline USING [LongCOPY, LowHalf], LoadState USING [ GetModuleInfo, LockBcdInfo, LPBcdInfoTable, ModuleInfoSequenceHandle, ModuleInfosOfBcd, ModuleInfoRange, UnlockBcdInfo], LoadStateFormat USING [ModuleInfo], PrincOps USING [GlobalFrameHandle], Process USING [GetCurrent], Space USING [Kill, MakeReadOnly, Map, SwapUnitSize, Unmap], SpecialCRuntime USING [], Stream USING [Handle], Table USING [Base], Volume USING [InsufficientSpace, systemID]; CRuntimeA: MONITOR LOCKS CRuntimeInternal.lock IMPORTS BucketAlloc, CRuntimeInternal, File, Heap, Inline, Frame, LoadState, Process, Space, Volume EXPORTS CAbort, CBasics, CRuntime, CRuntimeInternal, SpecialCRuntime = { OPEN CRuntime, CRuntimeInternal; GlobalFrameHandle: TYPE = PrincOps.GlobalFrameHandle; ConfigEntry: PUBLIC TYPE = CRuntimeInternal.ConfigEntry; ConfigHandle: TYPE = LONG POINTER TO ConfigEntry; CEList: PUBLIC ConfigHandle ← NIL; z: PUBLIC UNCOUNTED ZONE; arrayZone: PUBLIC UNCOUNTED ZONE; lock: PUBLIC MONITORLOCK; gfTable: PUBLIC LONG POINTER TO GFTable; shTable: PUBLIC LONG POINTER TO SHTable; processTable: PUBLIC LONG POINTER TO ProcessTable; ArraySpace: PUBLIC TYPE = CRuntimeInternal.ArraySpace; GFHash: PROCEDURE [gf: GlobalFrameHandle] RETURNS [GFIndex] = INLINE { RETURN[(LOOPHOLE[gf, CARDINAL] / 4) MOD gfTableSize]; }; ModuleNotRegistered: SIGNAL [gfh: GlobalFrameHandle] = CODE; ProcessNotRegistered: PUBLIC ERROR = CODE; TooMuchGlobalArraySpace: PUBLIC ERROR = CODE; arraySpaceThreshold: PUBLIC Environment.PageCount ← 5; heapSize: PUBLIC Environment.PageCount ← 10; checkUnusedSpace: BOOLEAN ← FALSE; dataSwapUnitSize: CARDINAL ← 4; codeSwapUnitSize: CARDINAL ← 2; GetBcdInfo: PUBLIC PROC RETURNS [numArgs: CARDINAL, arraySize: LONG CARDINAL, commonInfo: CBasics.CommonRecPtr] = { bcdInfo: LoadState.LPBcdInfoTable; frame: PrincOps.GlobalFrameHandle; modinfo: LoadStateFormat.ModuleInfo; atb: LONG POINTER TO cInfo AtomVariants.ATRecord; bcd: BcdOps.BcdBase; [, bcdInfo] ← LoadState.LockBcdInfo[]; frame ← Frame.ReadGlobalLink[Frame.GetReturnFrame[]]; modinfo ← LoadState.GetModuleInfo[frame]; bcd ← bcdInfo[modinfo.index].base; LoadState.UnlockBcdInfo[]; IF bcd.atLimit = LOOPHOLE[0] THEN RETURN[0,0,NIL]; atb ← ATBaseFromBcd[bcd]; numArgs ← atb.numMainArgs; arraySize ← atb.arraySpaceSize[0]; IF bcd.atLimit = LOOPHOLE[SIZE[AtomVariants.ATRecord] + SIZE[LONG CARDINAL]] THEN RETURN[numArgs, arraySize, NIL]; commonInfo ← atb + SIZE[AtomVariants.ATRecord] + SIZE[LONG CARDINAL]; RETURN[numArgs, arraySize, commonInfo]}; -- ATBaseFromBcd: analogous to BcdOpsExtras.MTBaseFromBcd ATBaseFromBcd: PROC [bcd: BcdOps.BcdBase] RETURNS [Table.Base] = INLINE { RETURN[LOOPHOLE[bcd + bcd.atOffset]]}; -- GetCommons called from the driver module, performs the allocation of common space and initializes pointers in the global frame of the driver module to point to the various commons. GetCommons: PUBLIC PROCEDURE [comm: CBasics.CommonRecPtr, comSpace: CBasics.ArraySpaceHandle ] = { ptrptr: POINTER TO LONG POINTER ← NIL; driversGF: PrincOps.GlobalFrameHandle = Frame.ReadGlobalLink[ Frame.GetReturnFrame[]]; ptrptr ← LOOPHOLE[driversGF + comm.commPtrOffset]; FOR i: CARDINAL IN [0..comm.twiceNumCommons / 2) DO ptrptr↑ ← NextArray[comSpace, comm.items[i]]; ptrptr ← ptrptr + SIZE[LONG POINTER]; ENDLOOP}; -- GetCommons -- GetTotalSize computes the total ammount of space required for all the commons which are declared in this config. GetCommonSize: PUBLIC PROCEDURE [comm: CBasics.CommonRecPtr] RETURNS [totSize: LONG CARDINAL ← 0] = { FOR i: CARDINAL IN [0..comm.twiceNumCommons / 2) DO totSize ← totSize + comm.items[i]; ENDLOOP; RETURN[totSize]}; -- GetTotalSize ConfigHandleForGF: INTERNAL PROCEDURE [gf: GlobalFrameHandle] RETURNS [ConfigHandle] = { hashValue: GFIndex = GFHash[gf]; searcher: GFList ← gfTable[hashValue]; IF searcher = NIL THEN SIGNAL ModuleNotRegistered[gf]; UNTIL searcher.gf = gf DO searcher ← searcher.link; IF searcher = NIL THEN SIGNAL ModuleNotRegistered[gf]; ENDLOOP; RETURN[searcher.config]; }; RegisterConfig: PUBLIC ENTRY PROCEDURE [wordsNeeded: LONG CARDINAL] = { ENABLE UNWIND => NULL; gf: GlobalFrameHandle = Frame.ReadGlobalLink[Frame.GetReturnFrame[]]; ch: ConfigHandle ← AssociateProcessWithGF[gf]; IF ch.data = NIL THEN ch.data ← MakeArraySpace[wordsNeeded]; IF ch.data # NIL THEN { ch.data.nextArray ← ch.data.space + ch.data.firstArrayOffset; ZeroFill[ch.data.nextArray, ch.data.words]; }; }; RegisterFrame: PUBLIC ENTRY PROCEDURE [wordsNeeded: LONG CARDINAL] RETURNS [CBasics.ArraySpaceHandle] = { ENABLE UNWIND => NULL; gf: GlobalFrameHandle = Frame.ReadGlobalLink[Frame.GetReturnFrame[]]; gfi: GFIndex; ge: GFList; ch: ConfigHandle ← AssociateProcessWithGF[gf]; gfi ← GFHash[gf]; ge ← gfTable[gfi]; WHILE ge # NIL DO IF ge.gf = gf THEN { IF ge.data # NIL THEN { ge.data.nextArray ← ge.data.space + ge.data.firstArrayOffset; ZeroFill[ge.data.nextArray, ge.data.words]; }; RETURN[IF ch.data = NIL THEN ge.data ELSE ch.data]; }; ge ← ge.link; ENDLOOP; gfTable[gfi] ← MakeGFEntry[gfi, gf, wordsNeeded, ch.data = NIL]; RETURN[IF ch.data = NIL THEN gfTable[gfi].data ELSE ch.data]; }; MakeGFEntry: INTERNAL PROCEDURE [ gfi: GFIndex, gf: GlobalFrameHandle, wordsNeeded: LONG CARDINAL, localData: BOOLEAN] RETURNS [gfl: GFList] = { ch: ConfigHandle ← NIL; moduleInfo: LoadStateFormat.ModuleInfo = LoadState.GetModuleInfo[gf]; ch ← ConfigHandleForConfig[moduleInfo.index, gf]; IF ch.data = NIL AND ~localData THEN ch.data ← MakeArraySpace[wordsNeeded]; IF localData THEN gfl ← z.NEW[ GFEntry ← [gf, ch, gfTable[gfi], MakeArraySpace[wordsNeeded]]] ELSE gfl ← z.NEW[GFEntry ← [gf, ch, gfTable[gfi], NIL]]; }; MakeArraySpace: PROCEDURE [wordsNeeded: LONG CARDINAL] RETURNS [ah: LONG POINTER TO ArraySpace] = { IF wordsNeeded > 0 THEN { pages: Environment.PageCount ← PagesForWords[wordsNeeded]; IF pages > arraySpaceThreshold THEN { file: File.File; ah ← z.NEW[large ArraySpace]; IF checkUnusedSpace THEN { swapUnits: ARRAY [1..2] OF Space.SwapUnitSize ← [CARDINAL[pages], 1]; ah.firstArrayOffset ← CARDINAL[ (pages * Environment.wordsPerPage) - wordsNeeded]; pages ← pages + 1; file ← File.Create[ volume: Volume.systemID, initialSize: pages, type: FileTypes.tUntypedFile ! File.Error, Volume.InsufficientSpace => ERROR TooMuchGlobalArraySpace]; ah.space ← Space.Map[ window: [file: file, base: 0, count: pages], life: dead, swapUnits: [irregular[DESCRIPTOR[swapUnits]]]].pointer; Space.MakeReadOnly[ interval: [ pointer: ah.space + ((pages - 1) * Environment.wordsPerPage), count: 1]]; ah.nextArray ← ah.space + ah.firstArrayOffset; } ELSE { -- no unused space check file ← File.Create[ volume: Volume.systemID, initialSize: pages, type: FileTypes.tUntypedFile ! File.Error, Volume.InsufficientSpace => ERROR TooMuchGlobalArraySpace]; ah.space ← ah.nextArray ← Space.Map[ window: [file: file, base: 0, count: pages], life: dead, swapUnits: [uniform[size: ]]].pointer; ah.firstArrayOffset ← 0; }; ah.filePart ← large[file]; } ELSE { ah ← z.NEW[small ArraySpace]; ah.space ← ah.nextArray ← Heap.MakeNode[ arrayZone, CARDINAL[wordsNeeded]]; ah.firstArrayOffset ← 0; ah.filePart ← small[]; }; ah.words ← wordsNeeded; ZeroFill[ah.nextArray, ah.words]; } ELSE ah ← NIL}; PagesForWords: PROCEDURE [nWords: LONG CARDINAL] RETURNS [Environment.PageCount] = INLINE { RETURN[(nWords + Environment.wordsPerPage - 1) / Environment.wordsPerPage]; }; NextArray: PUBLIC PROCEDURE [ heap: LONG POINTER TO ArraySpace, nWords: LONG CARDINAL] RETURNS [next: LONG POINTER] = { next ← heap.nextArray; heap.nextArray ← heap.nextArray + nWords; }; ZeroFill: PROCEDURE [space: LONG POINTER, words: LONG CARDINAL] = { -- Initializes global array space to all zeroes. largeSpace: BOOLEAN = words > LONG[CARDINAL.LAST]; words1: CARDINAL = IF largeSpace THEN CARDINAL.LAST ELSE CARDINAL[words]; IF words = 0 THEN RETURN; LOOPHOLE[space, LONG POINTER TO Environment.Word]↑ ← 0; Inline.LongCOPY[from: space, nwords: words1 - 1, to: space + 1]; IF largeSpace THEN { words2: CARDINAL = CARDINAL[words - LONG[CARDINAL.LAST]]; ptr: LONG POINTER = space + CARDINAL.LAST; LOOPHOLE[ptr, LONG POINTER TO Environment.Word]↑ ← 0; Inline.LongCOPY[from: ptr, nwords: words2 - 1, to: ptr + 1]; }; }; ConfigHandleForConfig: INTERNAL PROCEDURE [ config: CARDINAL, gf: GlobalFrameHandle] RETURNS [cH: ConfigHandle] = { searcher: ConfigHandle ← CEList; [] ← LoadState.LockBcdInfo[]; WHILE searcher # NIL DO Validate[searcher]; IF searcher.config = config THEN {cH ← searcher; GOTO Unlock; }; searcher ← searcher.link; ENDLOOP; CEList ← NewConfigEntry[config, gf, NIL, NIL, NIL]; cH ← CEList; GOTO Unlock; EXITS Unlock => LoadState.UnlockBcdInfo[]; }; NewConfigEntry: INTERNAL PROCEDURE [ config: CARDINAL, gf: GlobalFrameHandle, stdin, stdout, stderr: Stream.Handle] RETURNS [ConfigHandle] = INLINE { RETURN[ z.NEW[ ConfigEntry ← [ config: config, gf: gf, heap: NIL, openStreams: NIL, stdin: stdin, stdout: stdout, stderr: stderr, link: CEList, data: NIL]]]; }; SetConfig: PUBLIC ENTRY PROCEDURE [ gf: GlobalFrameHandle, stdin, stdout, stderr: Stream.Handle] = { ENABLE UNWIND => NULL; moduleInfo: LoadStateFormat.ModuleInfo = LoadState.GetModuleInfo[gf]; CEList ← NewConfigEntry[moduleInfo.index, gf, stdin, stdout, stderr]; EnterStreamGloballyInternal[stdin]; EnterStreamGloballyInternal[stdout]; EnterStreamGloballyInternal[stderr]; }; ResetConfig: PUBLIC ENTRY PROCEDURE [ gf: GlobalFrameHandle, stdin, stdout, stderr: Stream.Handle] = { ENABLE UNWIND => NULL; cH: ConfigHandle; moduleInfo: LoadStateFormat.ModuleInfo = LoadState.GetModuleInfo[gf]; cH ← ConfigHandleForConfig[moduleInfo.index, gf]; IF cH.openStreams # NIL THEN FreeOpenStreamList[cH]; IF cH.heap # NIL THEN Heap.Flush[cH.heap]; cH.stdin ← stdin; cH.stdout ← stdout; cH.stderr ← stderr; EnterStreamGloballyInternal[stdin]; EnterStreamGloballyInternal[stdout]; EnterStreamGloballyInternal[stderr]; cH.openStreams ← NIL; }; RegisterProcess: PUBLIC ENTRY PROCEDURE = { ENABLE UNWIND => NULL; [] ← AssociateProcessWithGF[Frame.ReadGlobalLink[Frame.GetReturnFrame[]]]; }; AssociateProcessWithGF: INTERNAL PROCEDURE [gf: GlobalFrameHandle] RETURNS [cH: ConfigHandle] = { cH ← ConfigHandleForConfig[LoadState.GetModuleInfo[gf].index, gf]; AssociateProcessWithConfig[cH]; }; AssociateProcessWithConfig: INTERNAL PROCEDURE [cH: ConfigHandle] = { p: PROCESS = Process.GetCurrent[]; pti: ProcessIndex = PHash[p]; pl: ProcessList ← processTable[pti]; <<Check if process already there, and reuse the process entry if it is.>> WHILE pl # NIL DO IF pl.p = p THEN {pl.config ← cH; RETURN; }; pl ← pl.link; ENDLOOP; pl ← z.NEW[ProcessEntry ← [p: p, config: cH, link: processTable[pti]]]; processTable[pti] ← pl; }; ConfigHandleForProcess: INTERNAL PROCEDURE RETURNS [ConfigHandle] = { p: PROCESS = Process.GetCurrent[]; pl: ProcessList ← processTable[PHash[p]]; WHILE pl # NIL DO IF pl.p = p THEN RETURN[pl.config]; pl ← pl.link; ENDLOOP; ERROR ProcessNotRegistered; }; GetConfigHandle: PUBLIC ENTRY PROCEDURE RETURNS [ConfigHandle] = { RETURN[ConfigHandleForProcess[]]; }; SetConfigHandle: PUBLIC ENTRY PROCEDURE [cH: ConfigHandle] = { AssociateProcessWithConfig[cH]; }; PHash: PROCEDURE [p: PROCESS] RETURNS [ProcessIndex] = { RETURN[LOOPHOLE[p, CARDINAL] MOD processTableSize]; }; FreeOpenStreamList: INTERNAL PROCEDURE [cH: ConfigHandle] = { s1, s2: StreamList; s1 ← cH.openStreams; WHILE s1 # NIL DO s2 ← s1.link; z.FREE[@s1]; s1 ← s2; ENDLOOP; }; RemoveConfig: PUBLIC ENTRY PROC [gf: GlobalFrameHandle] = { ENABLE UNWIND => NULL; cH: ConfigHandle; moduleSeq: LoadState.ModuleInfoSequenceHandle; searcher, follower: ConfigHandle; cH ← ConfigHandleForGF[gf ! ModuleNotRegistered => GOTO NotRegisterd]; IF cH.heap # NIL THEN Heap.Delete[z: cH.heap, checkEmpty: FALSE]; [] ← LoadState.LockBcdInfo[]; Validate[cH]; moduleSeq ← LoadState.ModuleInfosOfBcd[cH.config, z]; -- Remove each gf from gfTable and unmap global array space. FOR i: LoadState.ModuleInfoRange IN [0..moduleSeq.length) DO RemoveGF[moduleSeq[i].gf]; ENDLOOP; -- Remove cH from list of config entries. searcher ← CEList; follower ← NIL; UNTIL searcher = cH DO follower ← searcher; searcher ← searcher.link; ENDLOOP; IF follower = NIL THEN CEList ← searcher.link ELSE follower.link ← searcher.link; IF searcher.data # NIL THEN { -- if space was mapped WITH a: searcher.data↑ SELECT FROM large => {[] ← Space.Unmap[pointer: a.space]; File.Delete[a.file]; }; small => arrayZone.FREE[@a.space]; ENDCASE; z.FREE[@searcher.data]; }; z.FREE[@searcher]; z.FREE[@moduleSeq]; LoadState.UnlockBcdInfo[]; EXITS NotRegisterd => NULL; }; RemoveGF: INTERNAL PROCEDURE [gf: GlobalFrameHandle] = { gfi: GFIndex = GFHash[gf]; searcher, follower: GFList; searcher ← gfTable[gfi]; follower ← NIL; UNTIL searcher = NIL OR searcher.gf = gf DO follower ← searcher; searcher ← searcher.link; ENDLOOP; IF searcher = NIL THEN RETURN; IF searcher.data # NIL THEN { -- if space was mapped WITH a: searcher.data↑ SELECT FROM large => {[] ← Space.Unmap[pointer: a.space]; File.Delete[a.file]; }; small => arrayZone.FREE[@a.space]; ENDCASE; z.FREE[@searcher.data]; }; IF follower = NIL THEN gfTable[gfi] ← searcher.link ELSE follower.link ← searcher.link; z.FREE[@searcher]; }; Validate: PROCEDURE [cH: ConfigHandle] = { cH.config ← LoadState.GetModuleInfo[cH.gf].index; }; GetHeap: PUBLIC ENTRY PROCEDURE RETURNS [UNCOUNTED ZONE] = { ENABLE UNWIND => NULL; RETURN[ConfigHandleForProcess[]↑.heap]; }; SetHeap: PUBLIC ENTRY PROCEDURE [h: UNCOUNTED ZONE] = { ENABLE UNWIND => NULL; ConfigHandleForProcess[]↑.heap ← h; }; SHHash: PROCEDURE [sh: Stream.Handle] RETURNS [SHIndex] = INLINE { RETURN[(LOOPHOLE[Inline.LowHalf[sh], CARDINAL] / 4) MOD shTableSize]; }; EnterStream: PUBLIC ENTRY PROCEDURE [sH: Stream.Handle] RETURNS [FilePtr] = { ENABLE UNWIND => NULL; cH: ConfigHandle = ConfigHandleForProcess[]; newStream: StreamList = z.NEW[StreamEntry ← [sH, cH.openStreams]]; cH.openStreams ← newStream; EnterStreamGloballyInternal[sH]; RETURN[@newStream.sH]; }; EnterStreamGlobally: PUBLIC ENTRY PROC [sH: Stream.Handle] = { ENABLE UNWIND => NULL; EnterStreamGloballyInternal[sH]; }; EnterStreamGloballyInternal: INTERNAL PROCEDURE [sH: Stream.Handle] = { shi: SHIndex = SHHash[sH]; shl: SHList; shl ← shTable[shi]; WHILE shl # NIL DO IF shl.sh = sH THEN {shl.refCount ← shl.refCount.SUCC; RETURN; }; shl ← shl.link; ENDLOOP; shTable[shi] ← z.NEW[SHEntry ← [sh: sH, refCount: 1, link: shTable[shi]]]; }; RemoveStream: PUBLIC ENTRY PROCEDURE [sH: Stream.Handle] RETURNS [canDeleteStream: BOOLEAN] = { ENABLE UNWIND => NULL; cH: ConfigHandle; -- OK if sH not in list . Might not have been opened with fopen. searcher: StreamList; IF sH = NIL THEN RETURN [FALSE]; cH ← ConfigHandleForProcess[]; searcher ← cH.openStreams; UNTIL searcher = NIL OR searcher.sH = sH DO searcher ← searcher.link; ENDLOOP; IF searcher # NIL THEN searcher.sH ← NIL; -- check if sH is a standard stream SELECT sH FROM cH.stdin => cH.stdin ← NIL; cH.stdout => cH.stdout ← NIL; cH.stderr => cH.stderr ← NIL; ENDCASE; canDeleteStream ← RemoveStreamGlobally[sH]; --called even if not in list }; RemoveStreamGlobally: INTERNAL PROCEDURE [sH: Stream.Handle] RETURNS [canDeleteStream: BOOLEAN] = { -- decrements ref. count and removes entry if ref. count becomes 0. searcher, follower: SHList; shi: SHIndex ← SHHash[sH]; searcher ← shTable[shi]; follower ← NIL; UNTIL searcher = NIL OR searcher.sh = sH DO follower ← searcher; searcher ← searcher.link; ENDLOOP; IF searcher = NIL THEN RETURN[TRUE]; searcher.refCount ← searcher.refCount.PRED; IF searcher.refCount = 0 THEN { canDeleteStream ← TRUE; IF follower = NIL THEN shTable[shi] ← searcher.link ELSE follower.link ← searcher.link; z.FREE[@searcher]; } ELSE canDeleteStream ← FALSE; }; CanDelete: PUBLIC ENTRY PROC [sh: Stream.Handle] RETURNS [BOOLEAN] = { -- Returns true if sh not in global stream table. ENABLE UNWIND => NULL; shi: SHIndex = SHHash[sh]; shl: SHList; shl ← shTable[shi]; WHILE shl # NIL DO IF shl.sh = sh THEN RETURN[FALSE]; shl ← shl.link; ENDLOOP; RETURN[TRUE]; }; ProgramExited: PUBLIC ERROR [status: INTEGER] = CODE; exit: PUBLIC PROCEDURE [status: INTEGER] RETURNS [INTEGER ← 0] = { CleanUp[]; ERROR ProgramExited[status]; }; abort: PUBLIC PROCEDURE RETURNS [INTEGER ← 0] = { CleanUp[]; ERROR ProgramExited[abortOutcome]; }; CleanUp: PUBLIC ENTRY PROCEDURE = { -- Deletes heap and closes open streams. ENABLE UNWIND => NULL; CleanUpConfig[ConfigHandleForProcess[]]; }; CleanUpConfig: INTERNAL PROCEDURE [cH: ConfigHandle] = { sl: StreamList; IF ~LogCleanUp[cH].cleanUpNeeded THEN RETURN; -- delete heap IF cH.heap # NIL THEN {DeleteAndLogHeap[z: cH.heap]; cH.heap ← NIL; }; -- close standard streams IF cH.stdin # NIL AND RemoveStreamGlobally[cH.stdin].canDeleteStream THEN DeleteAndLogStream[cH.stdin]; IF cH.stdout # NIL AND RemoveStreamGlobally[cH.stdout].canDeleteStream THEN DeleteAndLogStream[cH.stdout]; IF cH.stderr # NIL AND RemoveStreamGlobally[cH.stderr].canDeleteStream THEN DeleteAndLogStream[cH.stderr]; -- close other open files sl ← cH.openStreams; WHILE sl # NIL DO nextsl: StreamList ← sl.link; IF sl.sH # cH.stdin AND sl.sH # cH.stdout AND sl.sH # cH.stderr AND sl.sH # NIL THEN { IF RemoveStreamGlobally[sl.sH].canDeleteStream THEN DeleteAndLogStream[sl.sH]; }; z.FREE[@sl]; sl ← nextsl; ENDLOOP; cH.openStreams ← NIL; cH.stdin ← cH.stdout ← cH.stderr ← NIL; IF cH.data # NIL THEN WITH ah: cH.data SELECT FROM large => Space.Kill[ interval: [pointer: cH.data.space, count: File.GetSize[ah.file]]]; ENDCASE; }; GetStdin: PUBLIC ENTRY PROCEDURE RETURNS [FilePtr] = { ENABLE UNWIND => NULL; RETURN[@(ConfigHandleForProcess[].stdin)]; }; GetStdout: PUBLIC ENTRY PROCEDURE RETURNS [FilePtr] = { ENABLE UNWIND => NULL; RETURN[@(ConfigHandleForProcess[].stdout)]; }; GetStderr: PUBLIC ENTRY PROCEDURE RETURNS [FilePtr] = { ENABLE UNWIND => NULL; RETURN[@(ConfigHandleForProcess[].stderr)]; }; SetStdin: PUBLIC ENTRY PROCEDURE [fp: FilePtr] = { ENABLE UNWIND => NULL; cH: ConfigHandle = ConfigHandleForProcess[]; cH.stdin ← IF fp = NIL THEN NIL ELSE fp↑; }; SetStdout: PUBLIC ENTRY PROCEDURE [fp: FilePtr] = { ENABLE UNWIND => NULL; cH: ConfigHandle = ConfigHandleForProcess[]; cH.stdout ← IF fp = NIL THEN NIL ELSE fp↑; }; SetStderr: PUBLIC ENTRY PROCEDURE [fp: FilePtr] = { ENABLE UNWIND => NULL; cH: ConfigHandle = ConfigHandleForProcess[]; cH.stderr ← IF fp = NIL THEN NIL ELSE fp↑; }; SetUnusedSpaceCheck: PUBLIC ENTRY PROCEDURE [checkSpace: BOOLEAN] RETURNS [oldCheckSpace: BOOLEAN] = { oldCheckSpace ← checkUnusedSpace; checkUnusedSpace ← checkSpace; }; GetUnusedSpaceCheck: PUBLIC ENTRY PROCEDURE RETURNS [checkSpace: BOOLEAN] = { checkSpace ← checkUnusedSpace; }; SetDataSwapUnitSize:PUBLIC ENTRY PROCEDURE[size:CARDINAL] RETURNS [oldSize:CARDINAL] = { oldSize ← dataSwapUnitSize; dataSwapUnitSize ← size; }; SetCodeSwapUnitSize:PUBLIC ENTRY PROCEDURE[size:CARDINAL] RETURNS [oldSize:CARDINAL] = { oldSize ← codeSwapUnitSize; codeSwapUnitSize ← size; }; GetDataSwapUnitSize:PUBLIC ENTRY PROCEDURE RETURNS [size:CARDINAL] = { size ← dataSwapUnitSize; }; GetCodeSwapUnitSize:PUBLIC ENTRY PROCEDURE RETURNS [size:CARDINAL] = { size ← codeSwapUnitSize; }; Init: PROCEDURE = { buckets: ARRAY [0..8) OF BucketAlloc.BucketInfo ← [ [6, 4, 2], [10, 4, 2], [12, 4, 1], [15, 4, 2], -- SIZE[CIOLib.ClientDataObject] [20, 3, 0], [25, 2, 0], [32, 2, 0], [45, 2, 2] -- SIZE[CFormatIO$StringStreamObject] ]; z ← Heap.Create[ initial: 15, increment: 10, largeNodeThreshold: Environment.wordsPerPage * CARDINAL[arraySpaceThreshold]]; arrayZone ← Heap.Create[ initial: 25, increment: 50, largeNodeThreshold: Environment.wordsPerPage * CARDINAL[arraySpaceThreshold]]; gfTable ← z.NEW[GFTable]; shTable ← z.NEW[SHTable]; processTable ← z.NEW[ProcessTable]; BucketAlloc.Initialize[z, DESCRIPTOR[buckets]]; }; Init[]; }.