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, LONG CARDINAL]-2]]; page2: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[LOOPHOLE[ref, LONG CARDINAL]+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 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 c 1985, 1986 by Xerox Corporation. All rights reserved. John Maxwell November 28, 1983 2:49 pm Bob Hagmann November 21, 1986 3:03:10 pm PST Russ Atkinson (RRA) March 31, 1986 7:29:10 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šœ Οmœ7™BKšœ'™'K™,K™1K™+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š œžœžœžœžœžœžœ˜YKš œžœžœžœžœžœžœ ˜`Kšžœ˜Kšœ˜—Kšœžœ˜Kšœžœ˜K˜Kšœ˜—K˜š  œžœž œžœžœ˜DKšžœžœ žœžœžœžœžœ žœžœžœžœ˜wKšœ˜—K˜š  œžœžœžœ˜"Kš žœ žœžœžœŸ˜3šžœ žœžœŸ˜3Kšœ˜K˜Kšœ žœ˜—Kš žœ žœžœ žœžœŸ˜Zšžœ žœžœŸ9˜PKšœ$˜$Kšœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—š   œžœžœžœžœ˜6šžœžœžœ˜+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šœ0˜0—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™ϋšžœž˜Kšœ žœ˜Kšœ7˜7šžœ7žœ˜?Kšžœžœ˜4Kšœ žœ˜'K˜—K˜Kšžœ˜—K˜K˜——K˜K˜Kšœ˜K˜™'K™>Kšœ Οrœ™—™/K™0—™,K™=——…—$Τ7‚