TiogaLocksImpl.mesa Edited by Paxton on July 11, 1983 1:49 pm
Last Edited by: Maxwell, January 6, 1983 11:43 am
DIRECTORY
TiogaLocks,
TiogaLocksPrivate,
Process USING [GetCurrent],
Rope USING [ROPE],
TiogaNode USING [RefBranchNode];
TiogaLocksImpl: CEDAR MONITOR
LOCKS lock USING lock: LockRef
IMPORTS TiogaLocks, TiogaLocksPrivate, Process
EXPORTS TiogaLocks = BEGIN OPEN TiogaLocks;
LockBoth: PUBLIC PROC [doc1, doc2: TiogaNode.RefBranchNode, who: Rope.ROPE,
access1, access2: Access ← write] RETURNS [lock1, lock2: LockRef] = {
Like two calls on Lock, but orders the calls according to LockOrder.
IF (doc1=doc2 AND access1=write) OR (doc1 # doc2 AND LockOrder[doc1, doc2]) THEN {
lock1 ← Lock[doc1, who, access1]; lock2 ← Lock[doc2, who, access2] }
ELSE { lock2 ← Lock[doc2, who, access2]; lock1 ← Lock[doc1, who, access1] }};
Lock: PUBLIC PROC [
doc: TiogaNode.RefBranchNode, who: Rope.ROPE, access: Access ← write]
RETURNS [lock: LockRef] = {
IF doc=NIL THEN ERROR;
IF (lock ← TiogaLocksPrivate.GetLock[doc])=NIL THEN RETURN;
LockIt[lock, who, access, doc];
};
LockIt: ENTRY PROC [
lock: LockRef, who: Rope.ROPE, access: Access, doc: TiogaNode.RefBranchNode] = {
ENABLE UNWIND => NULL;
myProcess: PROCESS;
TRUSTED {myProcess ← LOOPHOLE[Process.GetCurrent[]]};
IF access=read THEN {
IF lock.process # myProcess THEN
WHILE lock.process # NIL DO -- wait for write lock to clear
WAIT lock.unlocked; ENDLOOP }
ELSE { -- want write access
IF lock.process # myProcess THEN { -- I don't have a write lock on it already
IF lock.count > 0 THEN { -- doc is locked
lock.waitingForWrite ← lock.waitingForWrite+1;
WHILE lock.count > 0 DO -- wait for all locks to clear
WAIT lock.unlocked; ENDLOOP;
lock.waitingForWrite ← lock.waitingForWrite-1 };
lock.process ← myProcess }};
IF lock.count=0 THEN lock.whoFirst ← who ELSE lock.whoLast ← who;
lock.count ← lock.count+1;
lock.maxCount ← MAX[lock.maxCount, lock.count] };
Unlock: PUBLIC PROC [doc: TiogaNode.RefBranchNode] = {
lock: LockRef;
IF doc=NIL THEN ERROR;
IF (lock ← TiogaLocksPrivate.GetLock[doc])=NIL THEN RETURN;
UnlockIt[lock, doc];
};
UnlockIt: ENTRY PROC [lock: LockRef, doc: TiogaNode.RefBranchNode] = {
ENABLE UNWIND => NULL;
IF lock=NIL THEN RETURN;
IF lock.count <= 0 THEN ERROR;
TRUSTED {IF lock.process # NIL AND lock.process # Process.GetCurrent[] THEN ERROR};
IF (lock.count ← lock.count-1) > 0 THEN RETURN; -- still locked
IF lock.process # NIL THEN { -- had write lock
IF dirtyDocProc # NIL THEN dirtyDocProc[doc];
lock.process ← NIL };
lock.whoFirst ← lock.whoLast ← NIL;
lock.maxCount ← 0;
BROADCAST lock.unlocked;
};
dirtyDocProc: PROC [doc: RefBranchNode];
RegisterForCallAfterReleaseWriteLock: PUBLIC PROC [proc: PROC [doc: RefBranchNode]] = {
dirtyDocProc ← proc; -- someday change this so have multiple proc's registered
};
UnregisterForCallAfterReleaseWriteLock: PUBLIC PROC [
proc: PROC [doc: RefBranchNode]] = {
dirtyDocProc ← NIL };
END...
The following belong in TiogaLayout (?)
LockDocAndTdd: PUBLIC PROC [tdd: TiogaDocument.TiogaDocumentData,
who: Rope.ROPE, access: Access ← write, interrupt, defer: BOOLFALSE]
RETURNS [lock: LockRef] = {
IF ~TiogaDocument.SpinAndLock[tdd, "LockDocAndTdd1", interrupt, defer] THEN RETURN [NIL];
must lock before read tdd.text
DO
doc: TiogaNode.RefBranchNode ← tdd.text;
TiogaDocument.Unlock[tdd]; -- must unlock tdd before try to lock the document.
IF doc=NIL THEN RETURN [NIL];
lock ← Lock[doc, who, access];
IF ~TiogaDocument.SpinAndLock[tdd, "LockDocAndTdd2", interrupt, defer] THEN {
UnlockIt[lock, doc]; RETURN [NIL] };
IF tdd.text = doc THEN RETURN; -- all went well
UnlockIt[lock, doc]; -- document changed during the time we had tdd unlocked
ENDLOOP };
UnlockDocAndTdd: PUBLIC PROC [tdd: TiogaDocument.TiogaDocumentData] = {
IF tdd.text#NIL THEN Unlock[tdd.text];
TiogaDocument.Unlock[tdd] };