YggUnixFileImpl.mesa
Copyright Ó 1988 by Xerox Corporation. All rights reserved.
Bob Hagmann May 9, 1988 9:48:40 am PDT
This module converts documents from stable to volatile forms.
DIRECTORY
Basics USING [charsPerWord],
RefText USING [AppendChar, AppendRope, New],
Rope USING [Length, ROPE],
SymTab USING [Create, Delete, EachPairAction, Fetch, Insert, Pairs, Ref],
YggDummyProcess USING [MsecToTicks, Pause, SetTimeout],
YggEnvironment USING [bytesPerPage],
YggFile USING [FileHandleRep, PageCount, PageNumber],
YggInternal USING [FileHandle];
YggUnixFileImpl: CEDAR MONITOR
IMPORTS RefText, Rope, SymTab, YggDummyProcess, YggEnvironment
EXPORTS YggInternal, YggFile
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
OpenFiles: SymTab.Ref ← NIL;
FileHandle: PUBLIC TYPE ~ YggInternal.FileHandle;
FileHandleRep: PUBLIC TYPE ~ YggFile.FileHandleRep;
AllNulls: PACKED ARRAY [0..Basics.charsPerWord) OF CHARALL[0C];
Exported procedures
Open: PUBLIC PROC [name: Rope.ROPE] RETURNS [FileHandle] = {
entry: FileHandle ← NIL;
entry ← RememberOpenFile[name];
RETURN[entry];
};
Create: PUBLIC PROC [name: Rope.ROPE, size: YggFile.PageCount ] RETURNS [FileHandle ← NIL] = {
unixName: REF TEXT;
entry: FileHandle ← NIL;
fd: INT ← 0;
entry ← RememberOpenFile[name];
allocateFD[entry, FALSE]; -- try (somewhat) to make sure that there are enough fd's
unixName ← UnixStringFromRope[name];
fd ← UCreat[LOOPHOLE[unixName, CARD]+UNITS[TEXT[0]]];
entry.fd ← fd;
deAllocateFD[entry];
RETURN[entry];
};
Delete: PUBLIC PROC [file: FileHandle] = {
RemoveOpenFile[file.name]
};
Info: PUBLIC PROC [file: FileHandle] RETURNS [size: YggFile.PageCount ← 0, name: Rope.ROPE ← NIL] = {
stat: StatRecord;
result: INT;
allocateFD[file];
TRUSTED {result ← UFStat[file.fd, @stat];};
deAllocateFD[file];
IF result < 0 THEN ERROR;
RETURN[stat.size, file.name];
};
SetSize: PUBLIC PROC [file: FileHandle, size: YggFile.PageCount] = {
stat: StatRecord;
result: INT;
allocateFD[file];
TRUSTED {result ← UFStat[file.fd, @stat];};
IF result >= 0 THEN {
SELECT stat.size FROM
> size => { -- truncate
[] ← UFtruncate[file.fd, size];
deAllocateFD[file];
};
< size => { -- expand
bytesWritten: INT;
[] ← ULSeek[fd: file.fd, offset: size-1, whence: set];
TRUSTED {bytesWritten ← UWrite[fd: file.fd, buf: @AllNulls, nbytes: 1];};
deAllocateFD[file];
IF bytesWritten # 1 THEN ERROR;
};
ENDCASE => {deAllocateFD[file];};
}
ELSE deAllocateFD[file];
IF result < 0 THEN ERROR;
};
Read: PUBLIC UNSAFE PROC [file: FileHandle, from: YggFile.PageNumber, nPages: YggFile.PageCount, to: LONG POINTER] = {
bytesRead: INT;
bytesNeeded: INT;
allocateFD[file];
[] ← ULSeek[fd: file.fd, offset: from*YggEnvironment.bytesPerPage, whence: set];
bytesNeeded ← nPages*YggEnvironment.bytesPerPage;
bytesRead ← URead[fd: file.fd, buf: to, nbytes: bytesNeeded];
deAllocateFD[file];
IF bytesRead = 0 THEN ERROR;
IF bytesRead # bytesNeeded THEN ERROR;
};
Write: PUBLIC PROC [file: FileHandle, to: YggFile.PageNumber, nPages: YggFile.PageCount, from: LONG POINTER] = {
bytesWritten: INT;
bytesToWrite: INT;
allocateFD[file];
[] ← ULSeek[fd: file.fd, offset: to*YggEnvironment.bytesPerPage, whence: set];
bytesToWrite ← nPages*YggEnvironment.bytesPerPage;
bytesWritten ← UWrite[fd: file.fd, buf: from, nbytes: bytesToWrite];
IF bytesWritten = 0 THEN ERROR;
IF bytesWritten # bytesToWrite THEN ERROR;
deAllocateFD[file];
};
File descriptor management
numberOfOpenFiles: INT ← 0;
maxNumberOfOpenFiles: INT ← 20;
myCondition: CONDITION;
BROADCASTmyCondition: ENTRY PROC ~ {
BROADCAST myCondition;
};
allocateFD: PROC [file: FileHandle, openIt: BOOL ← TRUE] ~ {
unixName: REF TEXT;
firstPart: ENTRY PROC ~ {
DO
IF file.locked THEN {WAIT myCondition; LOOP;};
IF file.fd > 0 THEN file.fdLockCount ← file.fdLockCount + 1
ELSE {
IF file.fdLockCount # 0 THEN ERROR;
file.fdLockCount ← 1;
unixName ← UnixStringFromRope[file.name];
file.locked ← TRUE;
numberOfOpenFiles ← numberOfOpenFiles + 1;
WHILE numberOfOpenFiles > maxNumberOfOpenFiles DO
eachPairAction: SymTab.EachPairAction = {
PROC [key: Key, val: Val] RETURNS [quit: BOOLFALSE];
ce: REF FileHandleRep;
quit ← FALSE;
ce ← NARROW[val];
IF ce.users = 0 AND ce.fd > 0 AND ~ce.locked THEN {
UClose[ce.fd];
ce.fd ← 0;
numberOfOpenFiles ← numberOfOpenFiles - 1;
IF numberOfOpenFiles < maxNumberOfOpenFiles -2 THEN quit ← TRUE;
};
};
[] ← SymTab.Pairs[x: OpenFiles, action: eachPairAction];
IF numberOfOpenFiles > maxNumberOfOpenFiles THEN YggDummyProcess.Pause[YggDummyProcess.MsecToTicks[73]];
ENDLOOP;
};
ENDLOOP;
};
IF file.fd <= 0 THEN {
file.fd ← UOpen[LOOPHOLE[unixName, CARD]+UNITS[TEXT[0]]];
IF file.fd <= 0 THEN ERROR;
};
file.locked ← FALSE;
BROADCASTmyCondition[];
};
deAllocateFD: ENTRY PROC [file: FileHandle] ~ {
IF file.fdLockCount <= 0 THEN ERROR;
file.fdLockCount ← file.fdLockCount - 1;
};
Open file symbol table management
LookupNameInOpenFiles: ENTRY PROC [name: Rope.ROPE] RETURNS [entry: REF FileHandleRep ← NIL] ~ {
Given a name, return the object for it.
found: BOOL;
val: REF;
[found, val] ← SymTab.Fetch[x: OpenFiles, key: name];
IF found THEN {
entry ← NARROW[val];
};
};
RememberOpenFile: ENTRY PROC [name: Rope.ROPE] RETURNS [entry: REF FileHandleRep ← NIL] ~ {
Add this document to the cache for the did.
found: BOOL;
val: REF;
[found, val] ← SymTab.Fetch[x: OpenFiles, key: name];
IF found THEN {
entry ← NARROW[val];
entry.users ← entry.users + 1;
RETURN;
}
ELSE {
IF ~SymTab.Insert[x: OpenFiles, key: name, val: NEW[FileHandleRep ← [name: name, users: 1]]] THEN ERROR;
};
};
RemoveOpenFile: ENTRY PROC [name: Rope.ROPE] ~ {
Remove this did from the cache.
found: BOOL;
val: REF;
[found, val] ← SymTab.Fetch[x: OpenFiles, key: name];
IF found THEN {
entry: REF FileHandleRep;
entry ← NARROW[val];
[] ← SymTab.Delete[x: OpenFiles, key: name];
};
};
Talking to UNIX
UnixStringFromRope: PROC [rope: Rope.ROPE] RETURNS [REF TEXT] ~ {
len: INT ← rope.Length[];
rtn: REF TEXT ← RefText.New[len+1];
[] ← RefText.AppendRope[to: rtn, from: rope];
[] ← RefText.AppendChar[to: rtn, from: '\000];
RETURN[rtn];
};
UOpen: PROC [name: CARD, flags: CARD ← 2, mode: CARD ← 644B] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"open"
};
UCreat: PROC [name: CARD, mode: CARD ← 644B] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"creat"
};
URead: PROC [fd: INT, buf: POINTER, nbytes: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"read"
};
UWrite: PROC [fd: INT, buf: POINTER, nbytes: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"write"
};
Whence: TYPE ~ MACHINE DEPENDENT {set(0), incr(1), xtnd(2), (CARD.LAST)};
from /usr/include/sys/file.h
ULSeek: PROC [fd: INT, offset: INT, whence: Whence] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"lseek"
};
UFSync: PROC [fd: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"fsync"
};
UFtruncate: PROC [fd: INT, length: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"ftruncate"
};
Short: TYPE ~ INT16;
UShort: TYPE ~ CARD16;
Long: TYPE ~ INT32;
ULong: TYPE ~ CARD32;
Int: TYPE ~ INT32;
DevT: TYPE ~ Short;
InoT: TYPE ~ ULong;
TimeT: TYPE ~ Long;
OffT: TYPE ~ Int;
from /usr/include/sys/types.h
StatRecord: TYPE ~ MACHINE DEPENDENT RECORD [
dev: DevT, -- device inode resides on
ino: InoT, -- this inode's number
mode: UShort, -- protection
nlink: Short, -- number of hard links to the file
uid: Short, -- user ID of owner
gid: Short, -- group ID of owner
rdev: DevT, -- the device type, for inode that is device
size: OffT, -- total size of file, in bytes
atime: TimeT, -- file last access time
spare1: Int,
mtime: TimeT, -- file last modify time
spare2: Int,
ctime: TimeT, -- file last status change time
spare3: Int,
blksize: Long, -- optimal blocksize for file system i/o ops
blocks: Long, -- actual number of blocks allocated
spare4: ARRAY [0..2) OF Long
];
from /usr/include/sys/stat.h
UFStat: PROC [fd: INT, buf: POINTER TO StatRecord] RETURNS [INT] ~ TRUSTED MACHINE CODE {
"fstat"
};
UClose: PROC [fd: INT] ~ TRUSTED MACHINE CODE {
"close"
};
Initialization
OpenFiles ← SymTab.Create[mod: 129, case: TRUE];
TRUSTED {
YggDummyProcess.SetTimeout[condition: @myCondition, ticks: YggDummyProcess.MsecToTicks[133]];
};
END.