FSOpenFileImpl.mesa
Last Edited by: Schroeder, December 14, 1983 11:43 am
Last Edited by: Levin, August 9, 1983 11:29 am
DIRECTORY
BasicTime USING [GMT],
File USING [Error, Handle, Info, PagesForWords, Read, wordsPerPage, Write],
FS USING [Error, Lock, OpenFile, PagesForBytes],
FSBackdoor USING [MakeFName, ProduceError, Version],
FSFileOps USING [GetFileInfo, GetProps, SetBytesAndCreated, SetFilePages],
FSLock USING [ActiveFile, GetAttachment, RecordREF, RemoveREF, ReportClose],
FSReport USING [FileError, ReportCreation],
Process USING [Detach],
Rope USING [Concat, ROPE],
SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ];
FSOpenFileImpl: CEDAR MONITOR
LOCKS f USING f: OpenFile
IMPORTS File, FS, FSBackdoor, FSFileOps, FSLock, FSReport, Process, Rope, SafeStorage
EXPORTS FS, FSBackdoor, FSLock
= BEGIN
Types
OpenFile: TYPE = REF OpenFileObj;
OpenFileObj: TYPE = MONITORED RECORD [
opCount: CARDINAL,
a: FSLock.ActiveFile];
ProcFile: TYPE = REF ProcFileObj;
ProcFileObj: TYPE = RECORD [
fileProcs: REF FileProcs,
clientFile: REF];
Exported to FS
GetClass: PUBLIC PROC [file: FS.OpenFile] RETURNS [a: ATOM] =
BEGIN
WITH file SELECT FROM
f: OpenFile =>
a ← $FS;
p: ProcFile =>
IF p.fileProcs.GetClass = NIL
THEN NotImplemented[]
ELSE a ← p.fileProcs.GetClass[p.clientFile];
ENDCASE => InvalidOpenFile[];
END;
SameFile: PUBLIC PROC [file1, file2: FS.OpenFile] RETURNS [same: BOOLEAN] =
BEGIN
WITH file1 SELECT FROM
f1: OpenFile =>
WITH file2 SELECT FROM
f2: OpenFile =>
BEGIN
a1, a2: FSLock.ActiveFile;
a1 ← StartOp[f1, readOp];
EndOp[f1];
a2 ← StartOp[f2, readOp];
EndOp[f2];
RETURN [ a1.h = a2.h ];
END;
p2: ProcFile =>
RETURN [FALSE];
ENDCASE;
p1: ProcFile =>
WITH file2 SELECT FROM
f2: OpenFile =>
RETURN [FALSE];
p2: ProcFile =>
IF p1.fileProcs = p2.fileProcs
THEN BEGIN
IF p1.fileProcs.SameFile = NIL
THEN NotImplemented[]
ELSE RETURN [p1.fileProcs.SameFile[p1.clientFile, p2.clientFile]];
END
ELSE RETURN [FALSE];
ENDCASE;
ENDCASE;
InvalidOpenFile[];
END;
GetName: PUBLIC PROC [file: FS.OpenFile] RETURNS [fullFName, attachedTo: Rope.ROPE] =
BEGIN
WITH file SELECT FROM
f: OpenFile =>
BEGIN
a: FSLock.ActiveFile ← StartOp[f, readOp];
fullFName ← FSBackdoor.MakeFName[a.nameBody, a.version];
a ← FSLock.GetAttachment[a];
IF a # NIL
THEN attachedTo ← FSBackdoor.MakeFName[a.nameBody, a.version]
ELSE attachedTo ← NIL;
EndOp[f];
END;
p: ProcFile =>
IF p.fileProcs.GetName = NIL
THEN NotImplemented[]
ELSE [fullFName, attachedTo] ← p.fileProcs.GetName[p.clientFile];
ENDCASE => InvalidOpenFile[];
END;
GetInfo: PUBLIC PROC [file: FS.OpenFile] RETURNS [keep: CARDINAL, pages, bytes: INT, created: BasicTime.GMT, lock: FS.Lock] =
BEGIN
WITH file SELECT FROM
f: OpenFile =>
BEGIN
a: FSLock.ActiveFile = StartOp[f, readOp];
[bytes, keep, created] ← FSFileOps.GetProps[a.h ! FS.Error => EndOp[f] ];
pages ← File.Info[a.h
! File.Error => { EndOp[f]; FSReport.FileError[why] } ].size;
lock ← IF a.fileLock = read THEN read ELSE write;
EndOp[f];
END;
p: ProcFile =>
IF p.fileProcs.GetInfo = NIL
THEN NotImplemented[]
ELSE [keep, pages, bytes, created, lock] ← p.fileProcs.GetInfo[p.clientFile];
ENDCASE => InvalidOpenFile[];
END;
SetPageCount: PUBLIC PROC [file: FS.OpenFile, pages: INT] =
BEGIN
WITH file SELECT FROM
f: OpenFile =>
BEGIN
a: FSLock.ActiveFile = StartOp[f, writeOp];
FSFileOps.SetFilePages[a.h, pages ! FS.Error => EndOp[f] ];
EndOp[f];
END;
p: ProcFile =>
IF p.fileProcs.SetPageCount = NIL
THEN NotImplemented[]
ELSE p.fileProcs.SetPageCount[p.clientFile, pages];
ENDCASE => InvalidOpenFile[];
END;
SetByteCountAndCreatedTime: PUBLIC PROC [file: FS.OpenFile, bytes: INT, created: BasicTime.GMT] =
BEGIN
WITH file SELECT FROM
f: OpenFile =>
BEGIN
a: FSLock.ActiveFile = StartOp[f, writeOp];
BEGIN ENABLE FS.Error => EndOp[f];
IF bytes < -1
THEN FSBackdoor.ProduceError[$badByteCount, "Tried to set a negative byte count."];
IF (bytes > -1 AND FSFileOps.GetFileInfo[a.h].pages < FS.PagesForBytes[bytes])
THEN FSBackdoor.ProduceError[$badByteCount, "Tried to set a byte count that is beyond the end of file."];
FSFileOps.SetBytesAndCreated[a.h, bytes, created];
END;
EndOp[f];
END;
p: ProcFile =>
IF p.fileProcs.SetByteCountAndCreatedTime = NIL
THEN NotImplemented[]
ELSE p.fileProcs.SetByteCountAndCreatedTime[p.clientFile, bytes, created];
ENDCASE => InvalidOpenFile[];
END;
Read: PUBLIC PROC [file: FS.OpenFile, from, nPages: INT, to: LONG POINTER] = TRUSTED
BEGIN
WITH file SELECT FROM
f: OpenFile =>
BEGIN
a: FSLock.ActiveFile = StartOp[f, readOp];
File.Read[a.h, [from], nPages, to
! File.Error => { EndOp[f]; FSReport.FileError[why] } ];
EndOp[f];
END;
p: ProcFile =>
IF p.fileProcs.Read = NIL
THEN NotImplemented[]
ELSE p.fileProcs.Read[p.clientFile, from, nPages, to];
ENDCASE => InvalidOpenFile[];
END;
Write: PUBLIC PROC [file: FS.OpenFile, to: INT, nPages: INT, from: LONG POINTER] =
BEGIN
WITH file SELECT FROM
f: OpenFile =>
BEGIN
a: FSLock.ActiveFile = StartOp[f, writeOp];
File.Write[a.h, [to], nPages, from
! File.Error => { EndOp[f]; FSReport.FileError[why] } ];
EndOp[f];
END;
p: ProcFile =>
IF p.fileProcs.Write = NIL
THEN NotImplemented[]
ELSE p.fileProcs.Write[p.clientFile, to, nPages, from];
ENDCASE => InvalidOpenFile[];
END;
Close: PUBLIC PROC [file: FS.OpenFile] =
BEGIN
WITH file SELECT FROM
f: OpenFile =>
BEGIN
a: FSLock.ActiveFile = InnerClose[f];
IF a = NIL THEN InvalidOpenFile[TRUE];
IF a.fileLock=write
THEN BEGIN -- write close, so report creation
fileName: Rope.ROPE = FSBackdoor.MakeFName[a.nameBody, a.version];
FSLock.ReportClose[a];
FSReport.ReportCreation[writeClose, fileName];
END
ELSE FSLock.ReportClose[a];
END;
p: ProcFile =>
IF p.fileProcs.Close = NIL
THEN NotImplemented[]
ELSE p.fileProcs.Close[p.clientFile];
ENDCASE => InvalidOpenFile[];
END;
WordsForPages: PUBLIC PROC[pages: INT] RETURNS [words: INT] =
{ RETURN [File.wordsPerPage*pages] };
BytesForPages: PUBLIC PROC[pages: INT] RETURNS [bytes: INT] =
{ RETURN [File.wordsPerPage*pages*2] };
PagesForWords: PUBLIC PROC [words: INT] RETURNS [pages: INT] =
{ RETURN [File.PagesForWords[words]] };
PagesForBytes: PUBLIC PROC [bytes: INT] RETURNS [pages: INT] =
{ RETURN [File.PagesForWords[(bytes+1)/2]] };
Exported to FSBackdoor
FileProcs: PUBLIC TYPE = RECORD [
GetClass: PROC [REF] RETURNS [ATOM],
SameFile: PROC [REF, REF] RETURNS [BOOLEAN],
GetName: PROC [REF] RETURNS [Rope.ROPE, Rope.ROPE],
GetInfo: PROC [REF] RETURNS [CARDINAL, INT, INT, BasicTime.GMT, FS.Lock],
SetPageCount: PROC [REF, INT],
SetByteCountAndCreatedTime: PROC [REF, INT, BasicTime.GMT],
Read: UNSAFE PROC [REF, INT, INT, LONG POINTER],
Write: PROC [REF, INT, INT, LONG POINTER],
Close: PROC [REF]
];
CreateFileProcs: PUBLIC PROC [
GetClass: PROC [REF] RETURNS [ATOM],
SameFile: PROC [REF, REF] RETURNS [BOOLEAN],
GetName: PROC [REF] RETURNS [Rope.ROPE, Rope.ROPE],
GetInfo: PROC [REF] RETURNS [CARDINAL, INT, INT, BasicTime.GMT, FS.Lock],
SetPageCount: PROC [REF, INT],
SetByteCountAndCreatedTime: PROC [REF, INT, BasicTime.GMT],
Read: UNSAFE PROC [REF, INT, INT, LONG POINTER],
Write: PROC [REF, INT, INT, LONG POINTER],
Close: PROC [REF]
] RETURNS [REF FileProcs] =
{ RETURN [ NEW [ FileProcs ← [GetClass, SameFile, GetName, GetInfo, SetPageCount, SetByteCountAndCreatedTime, Read, Write, Close] ] ] };
CreateProcsOpenFile: PUBLIC PROC [clientFile: REF, fileProcs: REF FileProcs] RETURNS [FS.OpenFile] =
{ RETURN [ [ NEW [ ProcFileObj ← [fileProcs, clientFile] ] ] ] };
GetClientFileAndProcs: PUBLIC PROC [file: FS.OpenFile] RETURNS [clientFile: REF, fileProcs: REF FileProcs] =
BEGIN
clientFile ← fileProcs ← NIL;
WITH file SELECT FROM
p: ProcFile => BEGIN
clientFile ← p.clientFile;
fileProcs ← p.fileProcs;
END;
ENDCASE;
END;
GetFileHandle: PUBLIC PROC [file: FS.OpenFile] RETURNS [h: File.Handle] =
BEGIN
WITH file SELECT FROM
f: OpenFile => BEGIN
a: FSLock.ActiveFile = StartOp[f, readOp];
h ← a.h;
EndOp[f];
END;
p: ProcFile => NotImplemented[];
ENDCASE => InvalidOpenFile[];
END;
Exported to FSLock
NewOpenFile: PUBLIC PROC [a: FSLock.ActiveFile] RETURNS [FS.OpenFile] =
BEGIN
f: OpenFile;
f ← NEW [ OpenFileObj ← [opCount: 0, a: a] ];
FSLock.RecordREF[f];
SafeStorage.EnableFinalization[f];
RETURN [ [f] ]
END;
Internal procedures
NotImplemented: PROC [] =
{ FSBackdoor.ProduceError[ notImplemented, "This operation is not implemented for this FS.OpenFile."] };
InvalidOpenFile: PROC [closed: BOOLEANFALSE] =
{ FSBackdoor.ProduceError[ invalidOpenFile, Rope.Concat["FS.OpenFile presented ", IF closed THEN "is closed." ELSE "contains NIL or an unrecognized TYPE."] ] };
StartOp: PROC [f: OpenFile, op: {writeOp, readOp}] RETURNS [a: FSLock.ActiveFile] =
BEGIN
InnerStartOp: ENTRY PROC [f: OpenFile] =
BEGIN
a ← f.a;
IF a = NIL THEN { open ← FALSE; RETURN };
IF op = writeOp AND a.fileLock = read THEN { correctLock ← FALSE; RETURN };
f.opCount ← f.opCount + 1;
END;
open, correctLock: BOOLEANTRUE;
InnerStartOp[f];
IF NOT open THEN InvalidOpenFile[TRUE];
IF NOT correctLock THEN FSBackdoor.ProduceError[wrongLock, "This operation requires a write lock and only a read lock is held."];
END;
lastOpFinished: CONDITION; -- This condition is notified whenever EndOp discovers a NIL ActiveFile and the opCount goes to zero
EndOp: ENTRY PROC [f: OpenFile] =
BEGIN
f.opCount ← f.opCount - 1;
IF f.a = NIL AND f.opCount = 0 THEN BROADCAST lastOpFinished;
END;
InnerClose: ENTRY PROC [f: OpenFile] RETURNS [a: FSLock.ActiveFile] =
BEGIN
a ← f.a;
IF f.a # NIL THEN BEGIN
f.a ← NIL; -- triggers BROADCASTs on lastOpFinished
UNTIL f.opCount = 0 DO WAIT lastOpFinished ENDLOOP;
END;
END;
Finalization stuff
fQ: SafeStorage.FinalizationQueue;
Finalize: PROC =
BEGIN
DO
f: OpenFile ← NARROW [ SafeStorage.FQNext[fQ] ];
FSLock.RemoveREF[f];
IF f.a # NIL THEN Close[ [f] ! FS.Error => CONTINUE];
f ← NIL;
ENDLOOP;
END;
Start code
fQ ← SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[CODE[OpenFileObj], 1, fQ];
TRUSTED { Process.Detach[FORK Finalize[]] };
END.