DIRECTORY ClassInscript, InterminalBackdoor USING [terminal], Intime USING [ EventTime, EventTimeDifference, MsTicks, ReadEventTime ], Loader USING [ MakeProcedureResident, MakeGlobalFrameResident ], PrincOpsUtils USING [ BITAND, BITSHIFT, LongCopy ], Process USING [ EnableAborts, MsecToTicks, SecondsToTicks, Ticks ], Terminal USING [ RegisterNotifier, SwapAction, SwapNotifier, Virtual ], VM USING [ Allocate, Free, Interval, AddressForPageNumber, Pin, Unpin, wordsPerPage ]; InscriptImpl: MONITOR -- invariant: File contents consistent IMPORTS InterminalBackdoor, Intime, Loader, PrincOpsUtils, Process, Terminal, VM EXPORTS ClassInscript SHARES ClassInscript = BEGIN OPEN ClassInscript, Intime; InscriptData: TYPE = REF InscriptObject; InscriptObject: PUBLIC TYPE = InscriptState; InscriptState: TYPE = RECORD [refCount: INT]; inscriptSpace: VM.Interval; earliestPage: InscriptPageNumber _ 0; latestRecordedD: InscriptPageDescBody_nullDescBody; subspaceSize: CARDINAL; -- # of pages of inscr. file mapped as a unit to insc. VM spaceBase: InscriptPage; spaceLimit: InscriptPage; writeDescriptor: PageDescriptor=@writeD; -- all entries into file use this writeD: InscriptPageDescBody_nullDescBody; terminal: Terminal.Virtual ~ InterminalBackdoor.terminal; actualPageMask: WORD; --<<>> subspaceMask: WORD; --<<>> hintPageNumber: ActualIPN; --<<>> hintPage: InscriptPage; minPagesInUse: ActualIPN; --<<]>>; stdInscript: InscriptData ~ NEW[InscriptState _ [refCount: 0]]; InscriptError: PUBLIC ERROR [code: InscriptErrorCode] = CODE; Internal: ERROR[code: CARDINAL] = CODE; pageSize: INTEGER=VM.wordsPerPage; PageIndex: TYPE = [0..pageSize); PageDescriptor: TYPE = LONG POINTER TO InscriptPageDescBody; InscriptPageDescBody: PUBLIC TYPE = RECORD [ pageNumber: InscriptPageNumber, iP: InscriptPage, -- base of current page. index: PageIndex ]; nullDescBody: InscriptPageDescBody =[pageNumber:-1,iP:NIL,index:NULL]; InscriptPage: TYPE = LONG POINTER TO InscriptPageValue; InscriptPageValue: TYPE = MACHINE DEPENDENT RECORD [ seal: CARDINAL, -- and this one -- data: SELECT OVERLAID * FROM entryPage=> [ length: CARDINAL, -- excludes this word -- entries: ARRAY [0..nInscriptPageIndex) OF WORD], hintPage=> [ earliestPage: CARDINAL, latestPage: CARDINAL, subspaceSize: CARDINAL, rest: ARRAY [0..nInscriptPageIndex-2) OF WORD ], ENDCASE ]; nInscriptPageIndex: PageIndex = pageSize - 2; inscriptPageSeal: CARDINAL = 154275B; ActualIPN: TYPE = INTEGER; nullAIPN: ActualIPN=-1; Time0: OrderKey=[0,0,0]; NewStdInscript: PUBLIC PROC [KeyProc: KEyProc, ComProc: COmProc, initializeFile: BOOL _ FALSE, lnFileSize: INTEGER _ 4, lnGroupSize: INTEGER _ 2 ] RETURNS [Inscript] = { ENABLE InscriptError => { IF code=invalidInscriptFile THEN VM.Free[inscriptSpace]; }; Complain: PROC={ IF ~initializeFile THEN ERROR InscriptError[invalidInscriptFile]; }; refCount: INT=stdInscript.refCount+1; fileLength: ActualIPN=TwoToThe[lnFileSize]+2; --1 for leader page, 1 for my trailer IF refCount#1 THEN ERROR Internal[6]; -- not now IF lnGroupSize>=lnFileSize OR lnGroupSize < 2 THEN ERROR InscriptError[invalidInscriptSpecs]; inscriptSpace _ VM.Allocate[count: fileLength-1]; subspaceSize _ TwoToThe[lnGroupSize]; actualPageMask _ PrincOpsUtils.BITSHIFT[177777B, lnFileSize-16]; --// subspaceMask _ PrincOpsUtils.BITSHIFT[177777B, lnGroupSize]; --// minPagesInUse _ fileLength-2-2*subspaceSize; --// hintPageNumber _ fileLength-2; --// spaceBase _ AccessIP[0]; spaceLimit _ hintPage _ AccessIP[hintPageNumber]; earliestPage _ 0; writeD _ nullDescBody; writeD.pageNumber _ 0; IF initializeFile THEN [] _ SetPage[stdInscript, writeDescriptor, 0, TRUE] ELSE { -- file init: old pD: PageDescriptor=@latestRecordedD; -- use as temp here KeyFrom: PROC[pN: InscriptPageNumber] RETURNS [OrderKey] = { IF SetPage[stdInscript, pD, pN] THEN RETURN[KeyProc[stdInscript, pD ! InscriptError=>IF code=invalidPageKey THEN CONTINUE]]; RETURN[Time0]}; latestTime, nextTime: OrderKey; latestPage, maxLP: InscriptPageNumber; IF hintPage.seal#inscriptPageSeal OR hintPage.subspaceSize#subspaceSize THEN Complain[]; earliestPage _ PrincOpsUtils.BITAND[hintPage.earliestPage,actualPageMask]; -- normalize latestPage _ writeD.pageNumber _ hintPage.latestPage-(hintPage.earliestPage-earliestPage); maxLP _ latestPage+fileLength-3; -- search max distance latestTime _ KeyFrom[latestPage]; IF latestTime=Time0 THEN ERROR InscriptError[invalidInscriptFile]; FOR pageNumber: InscriptPageNumber IN [latestPage+1..maxLP) DO nextTime _ KeyFrom[pageNumber]; IF ComProc[latestTime, nextTime] THEN EXIT ELSE { latestTime _ nextTime; latestPage _ pageNumber; }; ENDLOOP; IF ~SetPage[stdInscript, writeDescriptor, latestPage] THEN ERROR Internal[1]; writeD.index _ writeD.iP.length; }; latestRecordedD _ writeD; InscriptNotifier[terminal, coming, NIL]; stdInscript.refCount _ refCount; RETURN[stdInscript]; }; Release: PUBLIC ENTRY PROC [self: InscriptData] RETURNS [nilInscript: Inscript] = { ENABLE UNWIND => NULL; IF(self.refCount _ self.refCount-1)#0 THEN RETURN[NIL[Inscript]]; InscriptNotifier[terminal, going, NIL]; -- <> VM.Free[inscriptSpace]; RETURN[NIL[Inscript]]; }; GetPageLimits: PUBLIC ENTRY PROC [self: InscriptData] RETURNS [earliestPageNo: InscriptPageNumber, latestPageNo: InscriptPageNumber] = { RETURN[earliestPageNo: earliestPage, latestPageNo: writeD.pageNumber]; }; ResetPageDescriptor: PUBLIC ENTRY PROC [self: InscriptData, descriptor: PageDescriptor] = { IF descriptor#NIL THEN descriptor^ _ nullDescBody}; -- now invalid CopyPageDescriptor: PUBLIC ENTRY PROC [ self: InscriptData, dest: PageDescriptor, source: PageDescriptor] = {dest^ _ source^;}; WaitForEntry: PUBLIC ENTRY PROC [ self: InscriptData, waitMode: WaitMode, waitInterval: MsTicks _ waitALongTime, descriptor: PageDescriptor, waitStartTime: LONG POINTER TO EventTime _ NIL] RETURNS [moreEntries: BOOL] = { ENABLE UNWIND => NULL; t0, t1: EventTime; waitTime: MsTicks _ waitInterval; IF waitMode = timed AND waitStartTime = NIL THEN { t0 _ ReadEventTime[]; waitStartTime _ @t0; }; DO --ELSE-- IF descriptor.pageNumber < writeD.pageNumber OR descriptor.index RETURN[FALSE]; forever => waitTime_waitALongTime; timed => { dif: MsTicks; t1 _ ReadEventTime[]; dif _ EventTimeDifference[@t1, waitStartTime]; IF dif >= waitInterval THEN RETURN[FALSE]; waitTime _ waitInterval - dif; }; ENDCASE; Wait[Process.MsecToTicks[waitTime]]; ENDLOOP; }; SetPage: PUBLIC PROC [ self: InscriptData, descriptor: PageDescriptor, pageNumber: InscriptPageNumber, init: BOOL_FALSE] RETURNS [success: BOOL_TRUE] = { IF ~IsValidPd[descriptor] OR descriptor.pageNumber # pageNumber THEN { iP: InscriptPage; IF earliestPage > pageNumber OR writeD.pageNumber < pageNumber THEN RETURN[FALSE]; descriptor.iP_iP_AccessIP[ IPNToActual[ descriptor.pageNumber_pageNumber ] ]; IF init THEN { iP.length_0; iP.seal _ inscriptPageSeal; } ELSE IF iP.seal # inscriptPageSeal THEN success_FALSE; }; descriptor.index _ 0; }; AdvancePage: PUBLIC PROC [self: InscriptData, descriptor: PageDescriptor] RETURNS [success: BOOL] = { pageNumber: InscriptPageNumber=descriptor.pageNumber+1; IF ~IsValidPd[descriptor] THEN ERROR InscriptError[descriptorUninitialized]; IF earliestPage > pageNumber OR writeD.pageNumber < pageNumber THEN RETURN[FALSE]; descriptor.pageNumber_pageNumber; descriptor.iP_descriptor.iP+pageSize; IF LC[descriptor.iP]>=LC[spaceLimit] THEN descriptor.iP_spaceBase; descriptor.index_0; RETURN[TRUE]; }; SetWritePage: PUBLIC ENTRY PROC [self: InscriptData] RETURNS [success: BOOL] = { writeD.pageNumber_writeD.pageNumber+1; writeD.iP_writeD.iP+pageSize; IF LC[writeD.iP]>=LC[spaceLimit] THEN writeD.iP_spaceBase; writeD.iP.seal_inscriptPageSeal; writeD.index_writeD.iP.length_0; earliestPage _ PrincOpsUtils.BITAND[MAX[earliestPage, writeD.pageNumber-minPagesInUse], subspaceMask]; NOTIFY pageDone; RETURN[TRUE]; }; ReadEntry: PUBLIC ENTRY PROC [ self: InscriptData, descriptor: PageDescriptor, destination: LONG POINTER TO UNSPECIFIED, LengthProc: LengthProcType] RETURNS [success: BOOL] = { ENABLE UNWIND => NULL; index: PageIndex _ descriptor.index; len: CARDINAL; entry: LONG POINTER TO UNSPECIFIED; pageNumber: InscriptPageNumber = descriptor.pageNumber; inscriptPage: InscriptPage=descriptor.iP; IF ~IsValidPd[descriptor] THEN ERROR InscriptError[descriptorUninitialized]; IF earliestPage > pageNumber -- OR pageNumber > writeD.pageNumber -- THEN ERROR InscriptError[entryOutOfBounds]; IF index >= inscriptPage.length THEN RETURN[FALSE]; entry _ @(inscriptPage.entries[index]); PrincOpsUtils.LongCopy[from: entry, to: destination, nwords: (len_LengthProc[entry])]; descriptor.index _ index + len; RETURN[TRUE]; }; WriteEntry: PUBLIC ENTRY PROC [ self: InscriptData, entry: LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [success: BOOL] = { ENABLE UNWIND => NULL; newIndex: CARDINAL = writeD.index+LENGTH[entry]; IF newIndex > nInscriptPageIndex THEN RETURN[FALSE]; PrincOpsUtils.LongCopy[BASE[entry], LENGTH[entry], @writeD.iP.entries[writeD.index]]; writeD.index _ writeD.iP.length _ newIndex; maintDue_FALSE; -- non-preemptive notify for page maintainer BROADCAST entriesWaiting; RETURN[TRUE]; }; dirtyThreshhold: INTEGER=2; MaintainPage: ENTRY PROC[forceDirty: BOOL] RETURNS [{dirty, aborted}] = { DO ENABLE ABORTED => EXIT; IF ~forceDirty THEN { dif: INTEGER; WAIT pageDone; dif_writeD.pageNumber-latestRecordedD.pageNumber; IF latestRecordedD=writeD OR (dif { IF inscriptCurrent = 0 THEN VM.Pin[inscriptSpace]; inscriptCurrent _ inscriptCurrent + 1; }; going => { inscriptCurrent _ inscriptCurrent - 1; IF inscriptCurrent = 0 THEN { VM.Unpin[inscriptSpace]; []_MaintainPage[TRUE]; }}; here, gone => NULL; ENDCASE=>ERROR; }; Loader.MakeProcedureResident[--e.g.--InscriptNotifier]; Loader.MakeGlobalFrameResident[--e.g.--InscriptNotifier]; pageDone.timeout _ maintTicks; []_Intime.ReadEventTime[]; -- make sure Time starts first. Process.EnableAborts[@pageDone]; Process.EnableAborts[@entriesWaiting]; Terminal.RegisterNotifier[terminal, InscriptNotifier]; END. ΆInscriptImpl.mesa - Manage Inscript file Copyright c 1985 by Xerox Corporation. All rights reserved. Stone June 14, 1982 4:32 pm McGregor, 14-Sep-81 16:38:27 Swinehart, April 29, 1982 5:13 pm Paul Rovner, August 10, 1983 5:46 pm Russ Atkinson, September 23, 1983 10:32 am Birrell, August 23, 1983 9:33 am Doug Wyatt, April 24, 1985 10:12:54 pm PST << This version requires that the Inscript file be resident. Its default size has therefore been made much smaller than previous versions (16 pages, of which at least 8 have predictable contents (steady state)>> << This version also assumes (and ensures) that there is but one inscript per module instance.>> <> << One inscript per instance; state entirely contained in frame. NewStdInscript ups ref count; destroy downs it.>> file information mapping information output page information: maintProcess: PROCESS; background, writes dirty pages occasionally this and that <> Types, specifications for the low-level Ring-buffer inscript file implemented here <> Final init lock inscript space, enable recording. maintProcess _ FORK PageMaintainer[]; Process.Detach[maintProcess]; Process.Abort[maintProcess]; IF descriptor.pageNumber > writeD.pageNumber THEN ERROR Internal[5] -- Cleans up descriptor, if necessary Reads the indicated pages success: page available, has seal; failure: page unavailable, or does not have seal (uninit.) If init is TRUE, assumes page contents is valueless, initializes page Next test useful only during initialization, in FindTimeSeam -- Read the next page, set descriptor. Failure: same as SetPage Raises InscriptError[descriptorUninitialized] if there's no current page. IF index+len > inscriptPage.length THEN RETURN[FALSE]; Put entry in a buffer (indicate if page filled up). See MaintainPage for actual file write Utilities, Cleanup process, mapping functions, machine extensions PageMaintainer: PROC = { Monitored only where required. Says here. For the current page, this function merely keeps the inscript file tracking reality approximately; for pages that are finished, no other process will be changing them again. Says here. DO forever ENABLE Process.Aborted => EXIT; (almost forever) SELECT MaintainPage[FALSE] FROM dirty=> Space.ForceOut[inscriptSpace]; aborted=>EXIT; ENDCASE; ENDLOOP; }; Wait on entriesWaiting for specified time Initialization -- [vt: Virtual, action: SwapAction, clientData: REF ANY] NOTE: Must be locked because of known planned use by other abstractions! -- Κ'˜codešœ*™*Kšœ Οmœ1™K˜Kšžœžœž˜*Kšžœ5˜9Kšžœ˜—Kšžœ4žœžœ ˜MK˜#—Kšœ ™ K˜šœ#žœ˜(Kšœ&™&—K™Kšœ%™%Kšœ™K˜ Kšžœ˜Kšœ˜—K˜š ‘œžœžœžœžœ˜SKšžœžœžœ˜Kšžœ$žœžœžœ ˜AKšœ™Kšœ"žœ ˜;Kšžœ˜Kšžœžœ ˜Kšœ˜K˜—š‘ œžœžœžœ˜5KšžœK˜RšžœC˜IK˜——š‘œžœžœžœ5˜[Kšžœ žœžœ ˜BK˜—š‘œžœžœžœ˜'˜WK˜——š‘ œžœžœžœ˜!K˜NKš œ+žœžœžœ žœ˜KKšžœžœ˜Kšžœžœžœ˜K˜K˜!šžœžœžœžœ˜2K˜-—Kšž˜šœF™Fš œžœ+ž˜8Kšœžœžœžœ˜0—šžœ ž˜Kšœ žœžœ˜K˜"˜ K˜ K˜K˜.Kšžœžœžœžœ˜*K˜!—Kšžœ˜—K˜$Kšžœ˜ ——K™šœ"™"Kšœ™Kšœ]™]KšœE™E—K˜š‘œžœžœ˜K˜/Kš œ&žœžœžœ žœžœ˜Ršžœžœ$žœ˜FK˜Kš žœžœ žœžœžœ˜RK˜OKšžœžœ-˜9Kšœ?™?Kšžœžœžœ žœ˜9—K˜K˜—Kšœ#™#Kšœ™KšœI™Iš‘ œžœžœ1˜IKšžœ žœ˜K˜7Kšžœžœžœ(˜LKš žœžœ žœžœžœ˜RK˜!Kš œ&žœžœžœ žœ˜hK˜Kšžœžœ˜K˜—š ‘ œžœžœžœžœ žœ˜PK˜&Kš œžœžœ žœ žœ˜XK˜ K˜ Kšœžœžœ?˜fKšžœ ˜Kšžœžœ˜K˜—š‘ œžœžœžœ˜K˜/Kš œ žœžœžœž œ˜EKšžœ žœ˜Kšžœžœžœ˜K˜$Kšœžœ˜Kš œžœžœžœž œ˜#K˜7K˜)Kšžœžœžœ(˜Lšžœ 'œžœ˜JKšžœ!˜&—Kšžœžœžœžœ˜3K˜'K˜VKšœ6™6K˜Kšžœžœ˜ šœ˜K˜——Kšœ[™[š‘ œžœžœžœ˜Kš œžœž œžœžœžœžœ˜=Kšžœ žœ˜Kšžœžœžœ˜Kšœ žœžœ˜0Kšžœžœžœžœ˜4Kšœžœ žœ+˜UK˜+Kšœ žœ ,˜