-- File: Locker.mesa, Last Edit: HGM December 18, 1980 10:48 PM DIRECTORY Storage USING [CopyString, Free, FreeString, Node], String USING [EquivalentString], Lock USING [Lock, LockObject, ReadWrite]; Locker: MONITOR IMPORTS Storage, String EXPORTS Lock = BEGIN OPEN Lock; first: Lock ← NIL; EnumerateLocks: PUBLIC ENTRY PROCEDURE [proc: PROCEDURE [Lock]] = BEGIN FOR lock: Lock ← first, lock.next UNTIL lock = NIL DO proc[lock]; ENDLOOP; END; GetLockLocation: PUBLIC PROCEDURE RETURNS [POINTER TO Lock] = BEGIN RETURN[@first]; END; wholeDiskBusy: BOOLEAN ← FALSE; lockFree: CONDITION; DiskNotBusy: ERROR = CODE; LockDiskAndWait: PUBLIC ENTRY PROCEDURE [fileName: STRING, why: ReadWrite] = BEGIN ok: BOOLEAN ← why # write; UNTIL ok DO FOR lock: Lock ← first, lock.next UNTIL lock = NIL DO IF String.EquivalentString[lock.name, fileName] THEN BEGIN WAIT lockFree; EXIT; END; REPEAT FINISHED => ok ← TRUE; -- not in list yet, nobody else reading or writing ENDLOOP; ENDLOOP; FOR lock: Lock ← first, lock.next UNTIL lock = NIL DO IF String.EquivalentString[lock.name, fileName] THEN BEGIN lock.useCount ← lock.useCount + 1; UNTIL ~lock.write DO WAIT lockFree; ENDLOOP; IF why = write THEN lock.write ← TRUE; EXIT; END; REPEAT FINISHED => BEGIN new: Lock ← Storage.Node[SIZE[LockObject]]; new↑ ← [first, Storage.CopyString[fileName], 1, why = write]; first ← new; END; ENDLOOP; END; LockDisk: PUBLIC ENTRY PROCEDURE [ fileName: STRING, why: ReadWrite, fast: BOOLEAN] RETURNS [ok: BOOLEAN] = BEGIN IF fast AND wholeDiskBusy THEN RETURN[FALSE]; FOR lock: Lock ← first, lock.next UNTIL lock = NIL DO IF String.EquivalentString[lock.name, fileName] THEN BEGIN IF lock.write OR why = write THEN RETURN[FALSE]; lock.useCount ← lock.useCount + 1; EXIT; END; REPEAT FINISHED => BEGIN new: Lock ← Storage.Node[SIZE[LockObject]]; new↑ ← [first, Storage.CopyString[fileName], 1, why = write]; first ← new; END; ENDLOOP; IF fast THEN wholeDiskBusy ← TRUE; RETURN[TRUE]; END; UnlockDisk: PUBLIC ENTRY PROCEDURE [fileName: STRING, fast: BOOLEAN] = BEGIN where: POINTER TO Lock ← @first; FOR lock: Lock ← first, lock.next UNTIL lock = NIL DO IF String.EquivalentString[lock.name, fileName] THEN BEGIN lock.useCount ← lock.useCount - 1; lock.write ← FALSE; IF lock.useCount = 0 THEN BEGIN where↑ ← lock.next; Storage.FreeString[lock.name]; Storage.Free[lock]; END; EXIT; END; where ← @lock.next; REPEAT FINISHED => ERROR DiskNotBusy; ENDLOOP; IF fast THEN wholeDiskBusy ← FALSE; BROADCAST lockFree; END; END.