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