<> <> <> <> <> <> <> <> <> <<>> <<<< 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.>> >> 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]; <<<< One inscript per instance; state entirely contained in frame.>> <>>> <> 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 < writeD.pageNumber THEN ERROR Internal[5] -->> --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])]; < inscriptPage.length THEN RETURN[FALSE];>> 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]; }; <> <> <> <> <> <> <> < EXIT; (almost forever)>> <