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: BOOL ← FALSE]
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] };