TEditLocksImpl.mesa Edited by Paxton on November 5, 1982 10:25 am
Last Edited by: Maxwell, January 6, 1983 11:43 am
DIRECTORY
TEditLocks,
TEditLocksPrivate,
Process USING [GetCurrent],
MonitoredQueue USING [AddToSet],
Rope USING [ROPE],
TextNode USING [Ref],
TEditDocument USING [GetViewerForRoot, SpinAndLock, TEditDocumentData, Unlock],
TEditRefresh USING [dirtyDocs],
ViewerClasses USING [Viewer];
TEditLocksImpl: CEDAR MONITOR
LOCKS lock USING lock: LockRef
IMPORTS TEditLocks, TEditLocksPrivate, Process, MonitoredQueue, TEditDocument, TEditRefresh
EXPORTS TEditLocks = BEGIN OPEN TEditLocks, TEditDocument;
LockBoth: PUBLIC PROC [doc1, doc2: TextNode.Ref, 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: TextNode.Ref, who: Rope.ROPE, access: Access ← write] RETURNS [lock: LockRef] = {
IF doc=NIL THEN ERROR;
IF (lock ← TEditLocksPrivate.GetLock[doc])=NIL THEN RETURN;
LockIt[lock, who, access, doc];
};
LockIt: ENTRY PROC [lock: LockRef, who: Rope.ROPE, access: Access, doc: TextNode.Ref] = {
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: TextNode.Ref] = {
lock: LockRef;
IF doc=NIL THEN ERROR;
IF (lock ← TEditLocksPrivate.GetLock[doc])=NIL THEN RETURN;
UnlockIt[lock, doc];
};
UnlockIt: ENTRY PROC [lock: LockRef, doc: TextNode.Ref] = {
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
viewer: ViewerClasses.Viewer;
IF (viewer ← TEditDocument.GetViewerForRoot[doc]) # NIL THEN {
MarkDirty: PROC [v: ViewerClasses.Viewer] = {
tdd: TEditDocument.TEditDocumentData ← NARROW[v.data];
IF tdd#NIL THEN tdd.dirty ← TRUE };
MarkDirty[viewer];
IF viewer.link # NIL THEN
FOR v: ViewerClasses.Viewer ← viewer.link, v.link UNTIL v=viewer DO
MarkDirty[v]; ENDLOOP;
MonitoredQueue.AddToSet[doc, TEditRefresh.dirtyDocs] };
lock.process ← NIL };
lock.whoFirst ← lock.whoLast ← NIL;
lock.maxCount ← 0;
BROADCAST lock.unlocked;
};
LockDocAndTdd: PUBLIC PROC [tdd: TEditDocument.TEditDocumentData,
who: Rope.ROPE, access: Access ← write, interrupt, defer: BOOLFALSE]
RETURNS [lock: LockRef] = {
IF ~TEditDocument.SpinAndLock[tdd, "LockDocAndTdd1", interrupt, defer] THEN RETURN [NIL];
must lock before read tdd.text
DO
doc: TextNode.Ref ← tdd.text;
TEditDocument.Unlock[tdd]; -- must unlock tdd before try to lock the document.
IF doc=NIL THEN RETURN [NIL];
lock ← Lock[doc, who, access];
IF ~TEditDocument.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: TEditDocument.TEditDocumentData] = {
IF tdd.text#NIL THEN Unlock[tdd.text];
TEditDocument.Unlock[tdd] };
END...