<<>> <> <> <> <> DIRECTORY MonitoredQueue USING [AddToSet], NodeProps USING [GetProp, NullCopy, NullRead, NullWrite, PutProp, Register], Process USING [GetCurrent], Rope USING [ROPE], TEditDocument USING [GetViewerForRoot, SpinAndLock, TEditDocumentData, Unlock], TEditLocks USING [Access, LockRecord, LockRef], TEditRefresh USING [dirtyDocs], TextNode USING [Ref], ViewerClasses USING [Lock, Viewer], ViewerLocks USING [LockOrder], ViewerPrivate USING [AcquireWriteLock, ReleaseWriteLock]; TEditLocksImpl: CEDAR MONITOR LOCKS lock USING lock: LockRef IMPORTS NodeProps, Process, MonitoredQueue, TEditDocument, TEditRefresh, ViewerLocks, ViewerPrivate EXPORTS TEditLocks = BEGIN OPEN TEditLocks, TEditDocument; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; locksBothCount: INT ¬ 0; dubious: INT ¬ 0; lockRootViewer: BOOL ¬ TRUE; <> lockSplitViewer: BOOL ¬ TRUE; <> LockBoth: PUBLIC PROC [doc1, doc2: TextNode.Ref, who: Rope.ROPE, access1, access2: Access ¬ write] RETURNS [lock1, lock2: LockRef] = { <> <> locksBothCount ¬ locksBothCount + 1; 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; DO v: Viewer ¬ TEditDocument.GetViewerForRoot[doc]; <> root: Viewer ¬ GetRoot[v]; localViewer: Viewer ¬ NIL; IF (lock ¬ GetLock[doc])=NIL THEN RETURN; IF root # NIL THEN { IF lockRootViewer AND root.link = NIL THEN localViewer ¬ root; IF lockSplitViewer AND root.link # NIL THEN localViewer ¬ root; IF localViewer # NIL THEN ViewerPrivate.AcquireWriteLock[localViewer]; }; IF root = GetRoot[v] THEN { <> IF TEditDocument.GetViewerForRoot[doc] = v THEN { <> LockIt[lock, who, access, doc]; IF TEditDocument.GetViewerForRoot[doc] = v THEN { <> NodeProps.PutProp[doc, $LockedViewer, localViewer]; EXIT; }; <> NodeProps.PutProp[doc, $LockedViewer, NIL]; UnlockIt[lock, doc]; }; }; <> IF localViewer # NIL THEN ViewerPrivate.ReleaseWriteLock[localViewer]; ENDLOOP; }; 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 lock.unlocked; ENDLOOP } ELSE { <> IF lock.process # myProcess THEN { <> IF lock.count > 0 THEN { <> lock.waitingForWrite ¬ lock.waitingForWrite+1; WHILE lock.count > 0 DO <> 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]; }; LockOrder: PUBLIC PROC [doc1, doc2: TextNode.Ref] RETURNS [BOOL] = { v1: Viewer ¬ IF doc1 = NIL THEN NIL ELSE TEditDocument.GetViewerForRoot[doc1]; v2: Viewer ¬ IF doc2 = NIL THEN NIL ELSE TEditDocument.GetViewerForRoot[doc2]; IF v1 # v2 THEN RETURN [ViewerLocks.LockOrder[v1, v2]]; RETURN [LOOPHOLE[doc1, INT] <= LOOPHOLE[doc2, INT]] }; Unlock: PUBLIC PROC [doc: TextNode.Ref] = { lock: LockRef; IF doc=NIL THEN ERROR; IF (lock ¬ GetLock[doc])=NIL THEN RETURN; UnlockIt[lock, doc]; }; UnlockIt: ENTRY PROC [lock: LockRef, doc: TextNode.Ref] = { ENABLE UNWIND => NULL; v: Viewer ¬ NARROW[NodeProps.GetProp[doc, $LockedViewer]]; IF lock=NIL THEN RETURN; IF lock.count <= 0 THEN ERROR; TRUSTED {IF lock.process # NIL AND lock.process # Process.GetCurrent[] THEN ERROR}; SELECT TRUE FROM (lock.count ¬ lock.count-1) > 0 => {}; ENDCASE => { SELECT TRUE FROM lock.process # NIL => { <> viewer: ViewerClasses.Viewer ¬ TEditDocument.GetViewerForRoot[doc]; IF viewer # v THEN dubious ¬ dubious + 1; IF viewer # NIL THEN { FOR v: ViewerClasses.Viewer ¬ viewer, v.link UNTIL v = NIL DO WITH v.data SELECT FROM tdd: TEditDocument.TEditDocumentData => tdd.dirty ¬ TRUE; ENDCASE; IF v.link = viewer THEN EXIT; ENDLOOP; MonitoredQueue.AddToSet[doc, TEditRefresh.dirtyDocs]; }; lock.process ¬ NIL; }; ENDCASE; NodeProps.PutProp[doc, $LockedViewer, NIL]; lock.whoFirst ¬ lock.whoLast ¬ NIL; lock.maxCount ¬ 0; BROADCAST lock.unlocked; }; IF v # NIL THEN ViewerPrivate.ReleaseWriteLock[v]; }; 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]; 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]; }; WaitingForWrite: PUBLIC PROC [lock: LockRef] RETURNS [BOOL] = { <> RETURN [lock.waitingForWrite > 0]; }; GetRoot: PROC [viewer: Viewer] RETURNS [root: Viewer] = { root ¬ viewer; IF root # NIL THEN DO next: Viewer ¬ root.parent; IF next = NIL THEN EXIT; root ¬ next; ENDLOOP; }; GetLock: PROC [root: TextNode.Ref] RETURNS [lock: LockRef] ~ INLINE --gfi saver-- { RETURN [LockedGetLock[privateLock, root]] }; privateLock: LockRef ~ NEW[LockRecord]; -- used only to lock GetLock lastRoot: TextNode.Ref ¬ NIL; lastLock: LockRef ¬ NIL; LockedGetLock: ENTRY PROC [lock: LockRef, root: TextNode.Ref] RETURNS [LockRef] ~ { ENABLE UNWIND => NULL; IF root = NIL THEN RETURN [NIL]; IF root = lastRoot THEN RETURN [lastLock]; lastRoot ¬ root; lastLock ¬ NARROW[NodeProps.GetProp[root, $DocumentLock]]; IF lastLock # NIL THEN RETURN [lastLock]; lastLock ¬ NEW[LockRecord]; NodeProps.PutProp[root, $DocumentLock, lastLock]; RETURN [lastLock] }; NodeProps.Register[$DocumentLock, NodeProps.NullRead, NodeProps.NullWrite, NodeProps.NullCopy]; <> END.