FSOpenFileImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bob Hagmann February 4, 1985 9:41:38 am PST
Schroeder, December 14, 1983 11:43 am
Levin, August 9, 1983 11:29 am
Russ Atkinson (RRA) May 13, 1985 8:48:39 pm PDT
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
= {
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] = {
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[];
};
SameFile: PUBLIC PROC [file1, file2: FS.OpenFile] RETURNS [same: BOOL] = {
WITH file1 SELECT FROM
f1: OpenFile =>
WITH file2 SELECT FROM
f2: OpenFile => {
a1, a2: FSLock.ActiveFile;
a1 ← StartOp[f1, readOp];
EndOp[f1];
a2 ← StartOp[f2, readOp];
EndOp[f2];
RETURN [ a1.h = a2.h ];
};
p2: ProcFile =>
RETURN [FALSE];
ENDCASE;
p1: ProcFile =>
WITH file2 SELECT FROM
f2: OpenFile =>
RETURN [FALSE];
p2: ProcFile =>
IF p1.fileProcs = p2.fileProcs
THEN {
IF p1.fileProcs.SameFile = NIL
THEN NotImplemented[]
ELSE RETURN [p1.fileProcs.SameFile[p1.clientFile, p2.clientFile]];
}
ELSE RETURN [FALSE];
ENDCASE;
ENDCASE;
InvalidOpenFile[];
};
GetName: PUBLIC PROC [file: FS.OpenFile] RETURNS [fullFName, attachedTo: Rope.ROPE] = {
WITH file SELECT FROM
f: OpenFile =>
{
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];
};
p: ProcFile =>
IF p.fileProcs.GetName = NIL
THEN NotImplemented[]
ELSE [fullFName, attachedTo] ← p.fileProcs.GetName[p.clientFile];
ENDCASE => InvalidOpenFile[];
};
GetInfo: PUBLIC PROC [file: FS.OpenFile] RETURNS [keep: CARDINAL, pages, bytes: INT, created: BasicTime.GMT, lock: FS.Lock] = {
WITH file SELECT FROM
f: OpenFile => {
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];
};
p: ProcFile =>
IF p.fileProcs.GetInfo = NIL
THEN NotImplemented[]
ELSE [keep, pages, bytes, created, lock] ← p.fileProcs.GetInfo[p.clientFile];
ENDCASE => InvalidOpenFile[];
};
SetPageCount: PUBLIC PROC [file: FS.OpenFile, pages: INT] = {
WITH file SELECT FROM
f: OpenFile => {
a: FSLock.ActiveFile = StartOp[f, writeOp];
FSFileOps.SetFilePages[a.h, pages ! FS.Error => EndOp[f] ];
EndOp[f];
};
p: ProcFile =>
IF p.fileProcs.SetPageCount = NIL
THEN NotImplemented[]
ELSE p.fileProcs.SetPageCount[p.clientFile, pages];
ENDCASE => InvalidOpenFile[];
};
SetByteCountAndCreatedTime: PUBLIC PROC [file: FS.OpenFile, bytes: INT, created: BasicTime.GMT] = {
WITH file SELECT FROM
f: OpenFile => {
a: FSLock.ActiveFile = StartOp[f, writeOp];
{ 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 byte count beyond the end of file"];
FSFileOps.SetBytesAndCreated[a.h, bytes, created];
};
EndOp[f];
};
p: ProcFile =>
IF p.fileProcs.SetByteCountAndCreatedTime = NIL
THEN NotImplemented[]
ELSE p.fileProcs.SetByteCountAndCreatedTime[p.clientFile, bytes, created];
ENDCASE => InvalidOpenFile[];
};
Read: PUBLIC PROC [file: FS.OpenFile, from, nPages: INT, to: LONG POINTER] = TRUSTED {
WITH file SELECT FROM
f: OpenFile => {
a: FSLock.ActiveFile = StartOp[f, readOp];
File.Read[a.h, [from], nPages, to
! File.Error => { EndOp[f]; FSReport.FileError[why] } ];
EndOp[f];
};
p: ProcFile =>
IF p.fileProcs.Read = NIL
THEN NotImplemented[]
ELSE p.fileProcs.Read[p.clientFile, from, nPages, to];
ENDCASE => InvalidOpenFile[];
};
Write: PUBLIC PROC [file: FS.OpenFile, to: INT, nPages: INT, from: LONG POINTER] = {
WITH file SELECT FROM
f: OpenFile => {
a: FSLock.ActiveFile = StartOp[f, writeOp];
File.Write[a.h, [to], nPages, from
! File.Error => { EndOp[f]; FSReport.FileError[why] } ];
EndOp[f];
};
p: ProcFile =>
IF p.fileProcs.Write = NIL
THEN NotImplemented[]
ELSE p.fileProcs.Write[p.clientFile, to, nPages, from];
ENDCASE => InvalidOpenFile[];
};
Close: PUBLIC PROC [file: FS.OpenFile] = {
WITH file SELECT FROM
f: OpenFile => {
a: FSLock.ActiveFile = InnerClose[f];
IF a = NIL THEN InvalidOpenFile[TRUE];
IF a.fileLock=write
THEN {
write close, so report creation
fileName: Rope.ROPE = FSBackdoor.MakeFName[a.nameBody, a.version];
FSLock.ReportClose[a];
FSReport.ReportCreation[writeClose, fileName];
}
ELSE FSLock.ReportClose[a];
};
p: ProcFile =>
IF p.fileProcs.Close = NIL
THEN NotImplemented[]
ELSE p.fileProcs.Close[p.clientFile];
ENDCASE => InvalidOpenFile[];
};
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 [BOOL],
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 [BOOL],
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] = {
clientFile ← fileProcs ← NIL;
WITH file SELECT FROM
p: ProcFile => {
clientFile ← p.clientFile;
fileProcs ← p.fileProcs;
};
ENDCASE;
};
GetFileHandle: PUBLIC PROC [file: FS.OpenFile] RETURNS [h: File.Handle] = {
WITH file SELECT FROM
f: OpenFile => {
a: FSLock.ActiveFile = StartOp[f, readOp];
h ← a.h;
EndOp[f];
};
p: ProcFile => NotImplemented[];
ENDCASE => InvalidOpenFile[];
};
Exported to FSLock
NewOpenFile: PUBLIC PROC [a: FSLock.ActiveFile] RETURNS [FS.OpenFile] = {
f: OpenFile;
f ← NEW [ OpenFileObj ← [opCount: 0, a: a] ];
FSLock.RecordREF[f];
SafeStorage.EnableFinalization[f];
RETURN [ [f] ]
};
Internal procedures
NotImplemented: PROC [] = {
FSBackdoor.ProduceError[ notImplemented, "Operation not implemented for this file"];
};
InvalidOpenFile: PROC [closed: BOOLFALSE] = {
FSBackdoor.ProduceError[ invalidOpenFile, Rope.Concat["File presented ", IF closed THEN "is closed." ELSE "is invalid"] ]
};
StartOp: PROC [f: OpenFile, op: {writeOp, readOp}] RETURNS [a: FSLock.ActiveFile] = {
InnerStartOp: ENTRY PROC [f: OpenFile] = {
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;
};
open, correctLock: BOOLTRUE;
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."];
};
lastOpFinished: CONDITION;
This condition is notified whenever EndOp discovers a NIL ActiveFile and the opCount goes to zero
EndOp: ENTRY PROC [f: OpenFile] = {
f.opCount ← f.opCount - 1;
IF f.a = NIL AND f.opCount = 0 THEN BROADCAST lastOpFinished;
};
InnerClose: ENTRY PROC [f: OpenFile] RETURNS [a: FSLock.ActiveFile] = {
a ← f.a;
IF f.a # NIL THEN {
f.a ← NIL; -- triggers BROADCASTs on lastOpFinished
UNTIL f.opCount = 0 DO WAIT lastOpFinished ENDLOOP;
};
};
Finalization stuff
fQ: SafeStorage.FinalizationQueue;
Finalize: PROC = {
DO
f: OpenFile ← NARROW [ SafeStorage.FQNext[fQ] ];
FSLock.RemoveREF[f];
IF f.a # NIL THEN Close[ [f] ! FS.Error => CONTINUE];
f ← NIL;
ENDLOOP;
};
Start code
fQ ← SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[CODE[OpenFileObj], 1, fQ];
TRUSTED { Process.Detach[FORK Finalize[]] };
}.
Bob Hagmann February 4, 1985 9:41:21 am PST
changes to: Copyright