DIRECTORY ClassInscript, Interminal 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 ], VM USING [ Allocate, Free, Interval, AddressForPageNumber, Pin, Unpin, wordsPerPage ] ; InscriptImpl: MONITOR -- invariant: File contents consistent IMPORTS Interminal, Intime, Loader, PrincOpsUtils, Process, Terminal, VM EXPORTS ClassInscript SHARES ClassInscript = BEGIN OPEN ClassInscript, Intime; InscriptData: TYPE = LONG POINTER TO InscriptState; 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; actualPageMask: WORD; --<<>> subspaceMask: WORD; --<<>> hintPageNumber: ActualIPN; --<<>> hintPage: InscriptPage; minPagesInUse: ActualIPN; --<<]>>; stdInscriptState: InscriptState; stdInscript: InscriptData=@stdInscriptState; 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: BOOLEAN _ 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[-1,lnFileSize-16]; --// subspaceMask_PrincOpsUtils.BITSHIFT[-1,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[Interminal.terminal, coming, NIL]; -- lock inscript space, enable recording. 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[Interminal.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: BOOLEAN] = { ENABLE UNWIND => NULL; t0, t1: EventTime; waitTime: MsTicks _ waitInterval; IF waitMode = timed AND waitStartTime = NIL THEN { t0 _ ReadEventTime[]; waitStartTime _ @t0; }; DO -- IF descriptor.pageNumber > 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: BOOLEAN_FALSE] RETURNS [success: BOOLEAN_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; } -- Next test useful only during initialization, in FindTimeSeam -- ELSE IF iP.seal # inscriptPageSeal THEN success_FALSE; }; descriptor.index _ 0; }; AdvancePage: PUBLIC PROC [self: InscriptData, descriptor: PageDescriptor] RETURNS [success: BOOLEAN] = { 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: BOOLEAN] = { 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: BOOLEAN] = { 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: BOOLEAN] = { 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: BOOLEAN] 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; -- NOTE: Must be locked because of known planned use by other abstractions! -- []_Intime.ReadEventTime[]; -- make sure Time starts first. Process.EnableAborts[@pageDone]; Process.EnableAborts[@entriesWaiting]; Terminal.RegisterNotifier[Interminal.terminal, InscriptNotifier]; END. @InscriptImpl.mesa - Manage Inscript file 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 << 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 maintProcess _ FORK PageMaintainer[]; Process.Detach[maintProcess]; Process.Abort[maintProcess]; 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 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 [vt: Virtual, action: SwapAction, clientData: REF ANY] Ê ÿ˜šœ*™*Jšœ™Jšœ™Jšœ!™!Jšœ$™$J™*Jšœ ™ J™—JšœÔ™ÔJšœa™aJ˜šÏk ˜ J˜Jšœ œ˜Jšœœ<˜HJšœœ4˜@Jšœœœœ ˜3Jšœœ6˜CJšœ œ0˜>JšœœM˜UJ˜—J™JšœM™MJ˜JšœœÏc&˜<˜š˜Jšœ>˜@—Jšœ˜Jšœ˜Jšœœ˜!J˜Jš œœœœœ˜3Jšœœœ˜,J˜—Jšœœœ œ˜-šœ@™@Jšœ1™1šœ™Jšœœ ˜J˜—šœ™J˜%J˜3Jšœœž9˜QJ˜J˜J˜—šœ™Jšœ)ž!˜JJ˜*——JšœC™CJ˜šœ ™ Jšœœž˜Jšœœž˜Jšœž˜!J˜Jšœž˜$J˜Jšœl™lJ˜ J˜,J˜Jšœœœœ˜=—˜Jšœ<™J˜Jšœœ˜*Jšœ1˜5Jšœ˜—Jšœ4œœ ˜MJ˜#—Jšœ ™ J˜Jšœ.œž)˜]—Jšœ%™%šœ™J˜Jšœ˜—J˜š Ÿœœœœœ˜SJšœœœ˜Jšœ"œœœ ˜?Jšœ™Jšœ-œž˜FJšœ˜Jšœœ ˜Jšœ˜J˜—šŸ œœœœ˜5JšœK˜RšœC˜IJ˜——šŸœœœœ5˜[Jšœ œœž˜@J˜—šŸœœœœ˜'˜EJ˜J˜——šŸ œœœœ˜!J˜NJš œ+œœœ œ˜KJšœœ˜"Jšœœœ˜J˜J˜!šœœœœ˜2J˜-—Jš˜šžK˜Kšžœœ+˜8Jšœœœœ˜0—šœ ˜Jšœ œœ˜J˜"˜ J˜ J˜J˜.Jšœœœœ˜*J˜!—Jšœ˜—J˜$Jšœ˜ ——J™šœ"™"Jšœ™Jšœ]™]JšœE™E—J˜šŸœœœ˜J˜/Jš œ&œœœ œœ˜Xšœœ$œ˜FJ˜Jš œœ œœœ˜RJ˜OJšœœ-˜9JšžB˜BJšœœœ œ˜9—J˜J˜—Jšœ#™#Jšœ™JšœI™IšŸ œœœ1˜IJšœ œ˜J˜7Jšœœœ(˜LJš œœ œœœ˜RJ˜!Jš œ&œœœ œ˜hJ˜Jšœœ˜J˜—š Ÿ œœœœœ œ˜SJ˜&Jš œœœ œ œ˜XJ˜ J˜ Jšœœœ?˜fJšœ ˜Jšœœ˜J˜—šŸ œœœœ˜J˜/Jš œ œœœ œ˜EJšœ œ˜Jšœœœ˜J˜$Jšœœ˜Jš œœœœ œ˜#J˜7J˜)Jšœœœ(˜Lšœž'œœ˜JJšœ!˜&—Jšœœœœ˜3J˜'J˜Všœ6™6J˜Jšœœ˜J˜——Jšœ[™[šŸ œœœœ˜Jš œœ œœœœœ˜=Jšœ œ˜Jšœœœ˜Jšœ œœ˜0Jšœœœœ˜4Jšœœ œ+˜UJ˜+Jšœ œž,˜Jšœœœ˜J˜—šœœœœœœœœ˜LJ˜—Jšœ)™)J˜š Ÿœœœœœ˜,Jšœœ˜/J˜—š Ÿœœœœœœ˜:šœœ ˜'J˜—Jšœ œ˜Jšœ œ˜J˜4Jšœ œœ˜J˜Jšž˜J˜—Jšœœ˜J˜šœ*œ˜3Jšœ6™6šœ˜˜ Jšœœœ˜3J˜)—˜ J˜&šœœ˜Jšœ˜Jšœœ˜J˜——Jšœœ˜Jšœœ˜Jšœ˜J˜—Jšœžœ˜7Jšœžœ˜9J˜J˜JšžN˜NJšœž˜:J˜ J˜&J˜A—J˜Jšœ˜J˜J˜—…—,üD;