DIRECTORY FS USING [Close, Create, Error, GetInfo, nullOpenFile, OpenFile, Read, SetPageCount, Write], Loader USING [MakeGlobalFrameResident, MakeGlobalFrameSwappable, MakeProcedureResident, MakeProcedureSwappable], PrincOps USING [BytePC, FrameHandle, GlobalFrameHandle, PsbHandle, wordsPerPage], PrincOpsUtils USING [GetClockPulses, GetReturnFrame, LongCopy, ReadPSB], Process USING [Detach, GetCurrent, GetPriority, MsecToTicks, Pause, Priority, priorityForeground, SetPriority, Ticks], Rope USING [ROPE], SpyLog, VM USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, PageNumber, PageNumberForAddress, Pin, Unpin]; SpyLogImpl: MONITOR IMPORTS FS, Loader, PrincOpsUtils, Process, VM EXPORTS SpyLog = { OPEN SpyLog; active: PUBLIC BOOL _ FALSE; -- true iff we are writing to the log. Error: PUBLIC ERROR = CODE; bufferReady: CONDITION; Log: TYPE = REF LogDesc; -- want it pinned in the global frame LogDesc: TYPE = RECORD [ base: EntryBasePointer _ NIL, index: EntryPointer _ FIRST[EntryPointer], file: FS.OpenFile _ FS.nullOpenFile, fileName: Rope.ROPE _ NIL, filePage: INT _ 0, pages: INT _ 0, doneWriting: BOOL _ FALSE, buffer: VM.Interval _ VM.nullInterval, next: VM.Interval _ VM.nullInterval, bufferProcess: PROCESS _ NIL, bufferProcessDone: BOOL _ FALSE, extendProcess: PROCESS _ NIL]; EntryBasePointer: TYPE = LONG ORDERED BASE POINTER; EntryPointer: TYPE = EntryBasePointer RELATIVE POINTER [0..177777B] TO Entry; bufferPages: CARDINAL = 40; buffersAhead: INT = 8; limit: EntryPointer = LOOPHOLE[bufferPages*PrincOps.wordsPerPage]; log1: Log = NEW[LogDesc _ [fileName: "///Spy/Spy.data"]]; log2: Log = NEW[LogDesc _ [fileName: "///Spy/SpyingOnSpy.data"]]; Init: PROC = { Pin: PROC [ref: REF, length: CARDINAL] = { page1: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[LOOPHOLE[ref, CARD]-2]]; page2: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[LOOPHOLE[ref, CARD]+length-1]]; VM.Pin[[page1, page2-page1+1]]; }; Pin[log1, LogDesc.SIZE]; Pin[log2, LogDesc.SIZE]; OpenLog[log1]; }; InvisibleProcesses: PUBLIC ENTRY PROC RETURNS [PROCESS, PROCESS] = { RETURN [IF writeLog = NIL THEN NIL ELSE writeLog.bufferProcess, IF writeLog = NIL THEN NIL ELSE writeLog.extendProcess]; }; OpenForRead: PUBLIC ENTRY PROC = { IF readLog # NIL THEN RETURN; -- already have a log IF writeLog # NIL THEN { -- read from the write log FinalizeWriteLog[writeLog]; readLog _ writeLog; writeLog _ NIL}; IF readLog = NIL AND log1.file # FS.nullOpenFile THEN readLog _ log1; -- use the last log IF readLog = NIL THEN { -- create a dummy log (the one on the disk isn't valid) InitializeWriteLog[readLog _ log1]; FinalizeWriteLog[readLog]}; MakeSwappable[]; readLog.filePage _ 0; readLog.index _ limit; }; OpenForWrite: PUBLIC ENTRY PROC [spyOnSpyLog: BOOL] = { IF ~spyOnSpyLog THEN writeLog _ log1 ELSE { readLog _ log1; readLog.filePage _ 0; readLog.index _ limit; writeLog _ log2}; InitializeWriteLog[writeLog]; MakeResident[]; }; Close: PUBLIC ENTRY PROC = { IF readLog # NIL THEN readLog _ NIL; IF writeLog = log1 THEN {FinalizeWriteLog[writeLog]; writeLog _ NIL}; IF writeLog = NIL THEN MakeSwappable[]; }; InitializeWriteLog: PROC [log: Log] = { OpenLog[log]; VM.Pin[log.buffer]; VM.Pin[log.next]; log.base _ LOOPHOLE[VM.AddressForPageNumber[log.buffer.page]]; log.index _ FIRST[EntryPointer]; log.filePage _ 0; }; FinalizeWriteLog: INTERNAL PROC [log: Log] = { endWords: NAT = SIZE[endOfLog SpyLog.Entry]; end: SpyLog.Entry _ [endOfLog[0]]; IF log.bufferProcess # NIL THEN { WHILE ~log.bufferProcessDone DO WAIT bufferReady; -- drop monitor to avoid deadlock ENDLOOP; JOIN log.bufferProcess; log.bufferProcess _ NIL; log.bufferProcessDone _ FALSE; }; PrincOpsUtils.LongCopy[from: @end, nwords: endWords, to: @log.base[log.index]]; log.doneWriting _ TRUE; Process.Pause[11]; FS.SetPageCount[log.file, log.filePage + bufferPages]; FS.Write[log.file, log.filePage, bufferPages, log.base]; VM.Unpin[log.buffer]; VM.Unpin[log.next]; }; OpenLog: PROC [log: Log] = { IF log.file = FS.nullOpenFile THEN { log.file _ FS.Create[name: log.fileName, pages: 4*bufferPages]; log.buffer _ VM.Allocate[bufferPages]; log.next _ VM.Allocate[bufferPages]; } ELSE { FS.SetPageCount[file: log.file, pages: 4*bufferPages]; }; log.pages _ 4*bufferPages; log.doneWriting _ FALSE; TRUSTED {Process.Detach[log.extendProcess _ FORK LogExtender[log]];}; }; CloseLog: PROC [log: Log] = { IF log.file = FS.nullOpenFile THEN RETURN; FS.Close[log.file]; log.file _ FS.nullOpenFile; VM.Free[log.buffer]; log.buffer _ VM.nullInterval; VM.Free[log.next]; log.next _ VM.nullInterval; }; MakeResident: PROC = { IF ~active THEN { Loader.MakeGlobalFrameResident[WriteData]; Loader.MakeProcedureResident[WriteData]; Loader.MakeProcedureResident[WriteTrace]; Loader.MakeProcedureResident[NextWriteBuffer]; active _ TRUE; }; }; MakeSwappable: PROC = { IF active THEN { active _ FALSE; Loader.MakeGlobalFrameSwappable[WriteData]; Loader.MakeProcedureSwappable[WriteData]; Loader.MakeProcedureSwappable[WriteTrace]; Loader.MakeProcedureSwappable[NextWriteBuffer]; }; }; readLog, writeLog: Log _ NIL; NextEntry: PUBLIC ENTRY PROC RETURNS [entry: LONG POINTER TO Entry] = { IF readLog = NIL OR readLog.base = NIL THEN RETURN [NIL]; IF LOOPHOLE[readLog.index, CARDINAL] >= LOOPHOLE[limit, CARDINAL] THEN { FS.Read[readLog.file, readLog.filePage, bufferPages, readLog.base]; readLog.filePage _ readLog.filePage + bufferPages; readLog.index _ FIRST[EntryPointer]; IF readLog.base[readLog.index].type = nullEntry THEN ERROR}; entry _ @readLog.base[readLog.index]; WITH entry: entry SELECT FROM endOfLog => NULL; trace => readLog.index _ readLog.index + SIZE[trace Entry]; data => readLog.index _ readLog.index + SIZE[data Entry] + entry.size; nullEntry => readLog.index _ limit; ENDCASE => ERROR; RETURN [entry]; }; WriteData: PUBLIC ENTRY PROC [data: LONG POINTER, size: [0..8000], type: CARDINAL] = { entrySizeWithoutData: CARDINAL = SIZE[data Entry]; entrySizeWithData: CARDINAL = entrySizeWithoutData + size; IF writeLog = NIL THEN RETURN; IF writeLog.bufferProcess # NIL AND writeLog.bufferProcess = Process.GetCurrent[] THEN RETURN; -- deadlock avoidance  FilePackage can do allocates during buffer write IF LOOPHOLE[writeLog.index+entrySizeWithData, CARDINAL] >= LOOPHOLE[limit, CARDINAL] THEN { NextWriteBuffer[writeLog]; writeLog.index _ FIRST[EntryPointer]; }; { ent: SpyLog.Entry _ [data[size: size, rttype: type, timestamp: PrincOpsUtils.GetClockPulses[], data: NULL]]; PrincOpsUtils.LongCopy[from: @ent, nwords: entrySizeWithoutData, to: @writeLog.base[writeLog.index]]; WITH e: writeLog.base[writeLog.index] SELECT FROM data => PrincOpsUtils.LongCopy[from: data, nwords: size, to: @e.data]; ENDCASE => ERROR; writeLog.index _ writeLog.index + entrySizeWithData; }; }; WriteTrace: PUBLIC ENTRY PROC [gfh: PrincOps.GlobalFrameHandle, pc: PrincOps.BytePC] = { entrySize: CARDINAL = SIZE[trace SpyLog.Entry]; IF writeLog = NIL THEN RETURN; IF LOOPHOLE[writeLog.index+SIZE[trace Entry], CARDINAL] >= LOOPHOLE[limit, CARDINAL] THEN { NextWriteBuffer[writeLog]; writeLog.index _ FIRST[EntryPointer]; }; IF gfh = NIL AND pc = 0 THEN { frame: PrincOps.FrameHandle = PrincOpsUtils.GetReturnFrame[]; gfh _ frame.accesslink; pc _ frame.pc; }; { ent: SpyLog.Entry _ [trace[fill: 0, gfh: gfh, pc: pc, process: PrincOpsUtils.ReadPSB[], timestamp: PrincOpsUtils.GetClockPulses[]]]; PrincOpsUtils.LongCopy[from: @ent, nwords: entrySize, to: @writeLog.base[writeLog.index]]; writeLog.index _ writeLog.index + entrySize; }; }; NextWriteBuffer: INTERNAL PROCEDURE [log: Log] = { entrySize: CARDINAL = SIZE[nullEntry SpyLog.Entry]; temp: VM.Interval; ent: SpyLog.Entry _ [nullEntry[limit-log.index]]; PrincOpsUtils.LongCopy[from: @ent, nwords: entrySize, to: @log.base[log.index]]; IF log.bufferProcess # NIL THEN { WHILE ~log.bufferProcessDone DO WAIT bufferReady; -- drop monitor to avoid deadlock ENDLOOP; JOIN log.bufferProcess; log.bufferProcess _ NIL; log.bufferProcessDone _ FALSE; }; log.bufferProcess _ FORK WriteBuffer[log, log.filePage, log.base]; temp _ log.buffer; log.buffer _ log.next; log.next _ temp; log.base _ LOOPHOLE[VM.AddressForPageNumber[log.buffer.page]]; log.filePage _ log.filePage + bufferPages; }; WriteBuffer: PROC [log: Log, filePage: INT, base: EntryBasePointer] = { writeTries: INT _ 0; writeDone: ENTRY PROC = { log.bufferProcessDone _ TRUE; BROADCAST bufferReady; }; priority: Process.Priority _ Process.GetPriority[]; IF priority > Process.priorityForeground THEN Process.SetPriority[Process.priorityForeground]; WHILE MIN[log.pages, FS.GetInfo[log.file].pages] < filePage + bufferPages DO Process.Pause[1]; -- wait for room ENDLOOP; FS.Write[log.file, filePage, bufferPages, base ! FS.Error => { IF error.code = $unknownPage AND writeTries < 10 THEN { nowPages: INT; hundredMilliseconds: Process.Ticks = Process.MsecToTicks[100]; writeTries _ writeTries + 1; FOR i:INT IN [0..99) DO nowPages _ FS.GetInfo[log.file].pages; IF nowPages >= filePage + bufferPages THEN EXIT; Process.Pause[hundredMilliseconds]; ENDLOOP; RETRY; }; }; ]; writeDone[]; }; LogExtender: PROC [log: Log] = { WHILE ~log.doneWriting AND (log.extendProcess = NIL OR log.extendProcess = Process.GetCurrent[]) DO newPages: INT; newPages _ log.filePage + (buffersAhead * bufferPages); IF (log.pages - log.filePage)/bufferPages < buffersAhead THEN { FS.SetPageCount[log.file, MAX[log.pages, newPages]]; log.pages _ FS.GetInfo[log.file].pages; }; Process.Pause[5]; ENDLOOP; }; Init[]; }. . . ζSpyLogImpl.mesa Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. John Maxwell November 28, 1983 2:49 pm Bob Hagmann March 26, 1987 10:12:31 am PST Russ Atkinson (RRA) February 16, 1987 12:38:47 pm PST Mike Spreitzer June 26, 1986 5:59:41 pm PDT control don't close the alternate log initialize the log for writing reading and writing move the index to the next entry are we at the end of the buffer? write out the entry. are we at the end of the buffer? write out the entry. wait until next buffer is written, then fork a write IF log.bufferProcess # NIL THEN {JOIN log.bufferProcess; log.bufferProcess _ NIL}; update the indices FS.SetPageCount[log.file, filePage + bufferPages]; The MIN with GetInfo is an attempt to fix a bug in writing past EOF. It shouldn't be needed, but ... This is another attempt to fix a bug in writing past EOF. It (also) shouldn't be neededed, but ... The LogExtender process is a temporary way to get the FS.SetPageCount call out of WriteBuffer. The idea is to keep enough pages in the file so the FS.Write succeeds. Letting WriteBuffer do the SetPageCount can cause a deadlock in the BTree package. Bob Hagmann June 3, 1985 4:15:39 pm PDT fix off by one in Pin - if unlucky you would pin an extra page changes to: Pin (local of Init) Russ Atkinson (RRA) June 6, 1985 5:47:30 pm PDT added error recovery if the log cannot be opened Bob Hagmann October 24, 1986 10:31:52 am PDT Fixed to avoid deadlock while using the labelless file system Κ έ˜codešœ™KšœH™HKšœ'™'K™*K™5K™+K™šΟk ˜ KšœœT˜\Kšœœd˜pKšœ œC˜QKšœœ5˜HKšœœi˜vKšœœœ˜Kšœ˜Kšœœn˜v——K˜head2šœ ˜Kšœœ"˜.Kšœ ˜Kšœ˜ —K˜KšœœœœΟc&˜CKšœœœœ˜K˜šœ  œ˜K˜Kšœœœ ž%˜?šœ œœ˜Kšœœ˜Kšœœ˜*Kšœœ œ˜%Kšœœœ˜Kšœ œ˜Kšœœ˜Kšœ œœ˜Kšœœ œ˜&Kšœœ œ˜$Kšœœœ˜Kšœœœ˜ Kšœœœ˜—Kš œœœœœœ˜3Kš œœœœœ˜MK˜—Kšž™™Kšœ œ˜Kšœœ˜Kšœœ$˜BKšœ œ+˜:Kšœ œ2˜AK˜šΟnœœ˜šŸœœœ œ˜*Kš œœœœœœ˜PKš œœœœœœ ˜WKšœ˜Kšœ˜—Kšœœ˜Kšœœ˜K˜Kšœ˜—K˜š Ÿœœ œœœ˜DKšœœ œœœœœ œœœœ˜xKšœ˜—K˜šŸ œœœœ˜"Kš œ œœœž˜3šœ œœž˜3Kšœ˜K˜Kšœ œ˜—Kš œ œœ œœž˜Zšœ œœž9˜PKšœ$˜$Kšœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—š Ÿ œœœœœ˜7šœœœ˜+Kšœ˜K˜Kšœ˜K˜—Kšœ˜K˜Kšœ˜K˜—šŸœœœœ˜Kšœ™Kšœ œœ œ˜$Kšœœ)œ˜EKšœ œœ˜'Kšœ˜K˜—šŸœœ˜(Kšœ™Kšœ ˜ Kšœ˜Kšœ˜Kšœ œœ(˜>Kšœ œ˜ K˜Kšœ˜K˜—šŸœ œ˜.Kšœ œœ˜,Kšœ"˜"šœœœ˜!šœ˜Kšœž!˜4Kšœ˜—Kšœ˜Kšœœ˜Kšœœ˜K˜—KšœO˜OKšœœ˜Kšœ˜Kšœ4˜6Kšœ6˜8Kšœ˜Kšœ˜Kšœ˜K˜—šŸœœ˜šœ œœ˜$Kšœ œ2˜?Kšœ œ˜&Kšœ œ˜$K˜—šœœ˜Kšœ4˜6K˜—Kšœ˜Kšœœ˜Kšœ%œ˜EKšœ˜K˜—šŸœœ˜Kšœ œœœ˜*Kšœœ˜/Kšœ œ˜2Kšœœ˜.Kšœ˜K˜—šŸ œœ˜šœ œ˜Kšœ*˜*K˜(K˜)Kšœ.˜.Kšœ œ˜Kšœ˜—Kšœ˜K˜—šŸ œœ˜šœœ˜Kšœ œ˜Kšœ+˜+Kšœ)˜)Kšœ*˜*Kšœ/˜/Kšœ˜—Kšœ˜—K™—šž™K˜Kšœœ˜K™šŸ œœœœœ œœœ ˜GKšœ œœœœœœ˜9š œœœœœœ˜HKšœA˜CKšœ2˜2Kšœœ˜$Kšœ.œœ˜<—Kšœ%˜%K™ šœœ˜Kšœ œ˜Kšœ)œ˜;Kšœ(œ˜FKšœ#˜#Kšœœ˜—Kšœ ˜Kšœ˜K˜—šŸ œœœœœœœ˜VKšœœœ ˜2Kšœœ˜:Kšœ œœœ˜Kš œœœ/œœžH˜¨Kšœ ™ š œœ#œœœœ˜[Kšœ˜Kšœœ˜%Kšœ˜—šœ˜Kšœ™Kšœeœ˜lKšœe˜ešœ"œ˜1KšœF˜FKšœœ˜—Kšœ4˜4K˜—Kšœ˜K˜—šŸ œœœœ;˜XKšœ œœ˜/Kšœ œœœ˜Kšœ ™ šœœœœœœœ˜[Kšœ˜Kšœœ˜%Kšœ˜—Kšœ™šœœœœ˜K˜=Kšœ˜K˜K˜—˜Kšœ„˜„KšœZ˜ZKšœ,˜,K˜—Kšœ˜K˜—šŸœœ˜2Kšœ œœ˜3Kšœœ ˜Kšœ1˜1KšœP˜PK™4šœœœ˜!šœ˜Kšœž!˜4Kšœ˜—Kšœ˜Kšœœ˜Kšœœ˜K˜—Kš œœœœ(œ™RKšœœ*˜BK™K˜:Kšœ œœ(˜>Kšœ*˜*Kšœ˜—K˜šŸ œœœ˜GKšœ œ˜šœ œœ˜Kšœœ˜Kš œ ˜K˜—Kšœ3˜3Kšœ'œ1˜^šœ0™2Kšœœ9œ"™e—šœœ œ3˜LKšœž˜#Kšœ˜—šœ/œ ˜>Kšœ5œ+™cšœœœ˜7Kšœ œ˜Kšœ>˜>Kšœ˜šœœœ ˜Kšœ œ˜&Kšœ$œœ˜0Kšœ#˜#Kšœ˜—Kšœ˜K˜—Kšœ˜—Kšœ˜Kšœ ˜ Kšœ˜K˜—šŸ œœ˜ Kšœ”œŸœJ™ϋš œœœœ+˜cKšœ œ˜Kšœ7˜7šœ7œ˜?Kšœœ˜4Kšœ œ˜'K˜—K˜Kšœ˜—K˜K˜——K˜K˜Kšœ˜K˜™'K™>Kšœ Οrœ™—™/K™0—™,K™=——…—%7Ω