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. : TEditLocksImpl.mesa Copyright Σ 1985, 1987, 1991 by Xerox Corporation. All rights reserved. Michael Plass, October 16, 1987 5:00:29 pm PDT Russ Atkinson (RRA) June 18, 1985 4:01:34 pm PDT Governs the locking of the root viewer for non-split viewers Governs the locking of the root viewer for split viewers Like two calls on Lock, but orders the calls according to LockOrder. RRA: this is not used, I think! Be prepared for v = NIL At this point we have a lock on the correct viewer (if asked to do so) The viewer is still the right one, so try to lock the document itself Success! So note the current association for unlocking. Rats! The viewer changed while we were trying for the lock, so let's undo everything. At this point we have a viewer lock, but on the wrong viewer! wait for write lock to clear want write access I don't have a write lock on it already doc is locked wait for all locks to clear had write lock must lock before read tdd.text Returns true iff some process is waiting to get a write lock for the document. Don't copy document lock or big trouble. Κ q•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Οeœ=™HK™.K™0—K˜šΟk ˜ Kšœžœ ˜ Kšœ žœ=˜LKšœžœ˜Kšœžœžœ˜Kšœžœ<˜OKšœ žœ˜/Kšœ žœ ˜Kšœ žœ˜Kšœžœ˜#Kšœ žœ ˜Kšœžœ&˜9K˜—KšΠlnœž œžœžœ˜Kšžœžœ žœžœ˜?Kšžœžœžœ-˜FK˜—šžœžœ˜KšœF™Fšžœ)žœ˜1KšœE™EK˜šžœ)žœ˜1Kšœ8™8Kšœ3˜3Kšžœ˜Kšœ˜—KšœU™UKšœ&žœ˜+Kšœ˜K˜—K˜—Kšœ=™=Kšžœžœžœ-˜FKšžœ˜—˜K˜——š œžœžœžœ(˜YKšžœžœžœ˜Kšœ žœ˜Kšžœžœ˜5šžœ ˜šžœ˜šžœž˜ šžœžœž˜Kšœ™Kšžœ˜Kšž˜——Kšœ˜—šžœ˜Kšœ™šžœžœ˜"Kšœ'™'šžœžœ˜Kšœ ™ K˜.šžœž˜Kšœ™Kšžœ˜Kšžœ˜—K˜0—Kšœ˜—Kšœ˜——Kšžœžœžœ˜AK˜Kšœžœ˜/Kšœ˜—K˜š   œžœžœžœžœ˜DKš œ žœžœžœžœžœ&˜NKš œ žœžœžœžœžœ&˜NKšžœ žœžœ!˜7Kš žœžœžœžœžœ˜3K˜K˜—š œžœžœ˜+Kšœ˜Kšžœžœžœžœ˜Kšžœžœžœžœ˜)K˜K˜—K˜š œžœžœ'˜;Kšžœžœžœ˜Kšœ žœ(˜:Kšžœžœžœžœ˜Kšžœžœžœ˜Kš žœžœžœžœ%žœžœ˜Sšžœžœž˜Kšœ&˜&šžœ˜ šžœžœž˜šœžœ˜Kšœ™KšœC˜CKšžœ žœ˜)šžœ žœžœ˜šžœ*žœžœž˜=šžœžœž˜Kšœ4žœ˜9Kšžœ˜—Kšžœžœžœ˜Kšžœ˜—Kšœ5˜5Kšœ˜—Kšœžœ˜Kšœ˜—Kšžœ˜—Kšœ&žœ˜+Kšœžœ˜#K˜Kšž œ˜K˜——Kšžœžœžœ#˜2K˜—K˜š  œžœžœ2žœ,žœžœžœ˜₯šžœEž˜KKšœ™Kšžœžœ˜ —šž˜K˜KšœΟc3˜NKš žœžœžœžœžœ˜Kšœ˜šžœEžœ˜MKšœžœžœ˜$—Kšžœžœžœ‘˜/Kšœ‘7˜LKšžœ˜—Kšœ˜—K˜š œž œ+˜GKšžœ žœžœ˜&Kšœ˜Kšœ˜—K˜š  œž œžœžœžœ˜?K™NKšžœ˜"Kšœ˜—K˜š œžœžœ˜9K˜šžœžœžœž˜K˜Kšžœžœžœžœ˜K˜ Kšžœ˜—K˜K˜—š  œžœžœžœ‘ œ˜SKšžœ#˜)Kšœ˜K˜—Kšœžœ‘˜DKšœžœ˜šœžœ˜K˜—š  œžœžœ%žœ˜SKšžœžœžœ˜Kš žœžœžœžœžœ˜ Kšžœžœžœ ˜*K˜Kšœ žœ)˜:Kšžœ žœžœžœ ˜)Kšœ žœ ˜Kšœ1˜1Kšžœ ˜Kšœ˜K˜—šœ_˜_K™(K˜——Kšžœ˜—…—%Γ