DIRECTORY TEditLocks, TEditLocksPrivate, Process USING [GetCurrent], MonitoredQueue USING [AddToSet], NodeProps, Rope USING [ROPE], TextNode USING [Ref], TEditDocument USING [GetViewerForRoot, SpinAndLock, TEditDocumentData, Unlock], TEditRefresh USING [dirtyDocs], ViewerClasses USING [Viewer], ViewerLocks USING [LockOrder], ViewerPrivate; TEditLocksImpl: CEDAR MONITOR LOCKS lock USING lock: LockRef IMPORTS NodeProps, Process, MonitoredQueue, TEditDocument, TEditLocksPrivate, 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 _ TEditLocksPrivate.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 _ TEditLocksPrivate.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; }; END. TEditLocksImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Michael Plass, March 21, 1985 3:06:44 pm PST 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. ΚD˜codešœ™Kšœ Οmœ1™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˜—Kšžœ˜—…—ά!"