<> <> <> DIRECTORY AMEvents USING [Debugged, Debugging], Process USING [GetCurrent, InitializeCondition, MsecToTicks, Pause], Runtime USING [CallDebugger], UserTerminal USING [BlinkDisplay], ViewerClasses USING [Column, Lock, Viewer], ViewerLocks, ViewerOps USING [EnumerateChildren, EnumerateViewers, EnumProc]; ViewerLocksImpl: MONITOR IMPORTS AMEvents, Process, Runtime, UserTerminal, ViewerOps EXPORTS ViewerLocks = BEGIN OPEN ViewerClasses, ViewerOps; lockFree: CONDITION; -- new viewer or column has been released treeLockSynch: CONDITION; -- synchronise read & write requests pending a tree request treeRequest: BOOL _ FALSE; -- viewer tree lock pending request state ColumnInfo: TYPE = RECORD [ lock: Lock, wedgeCount: CARDINAL ]; columns: ARRAY Column OF ColumnInfo ; Error: PROC = INLINE {Runtime.CallDebugger["ViewerLocks bug"]}; InOrder: PROC[v1, v2: Viewer] RETURNS [BOOLEAN] = INLINE { -- highest to lowest RETURN[LOOPHOLE[v1, LONG CARDINAL] >= LOOPHOLE[v2, LONG CARDINAL]]}; CallUnderWriteLock: PUBLIC SAFE PROC [proc: PROC, viewer: Viewer] = TRUSTED BEGIN ENABLE { AMEvents.Debugging => MarkColumnWedged[ViewerColumn[viewer]]; AMEvents.Debugged => MarkColumnUnWedged[ViewerColumn[viewer]]}; AcquireWriteLock[viewer]; proc[]; ReleaseWriteLock[viewer]; END; CallUnderWriteLocks: PUBLIC SAFE PROC [proc: PROC, v0, v1, v2: Viewer _ NIL] = TRUSTED BEGIN ENABLE { AMEvents.Debugging => { IF v0 # NIL THEN MarkColumnWedged[ViewerColumn[v0]]; IF v1 # NIL THEN MarkColumnWedged[ViewerColumn[v1]]; IF v2 # NIL THEN MarkColumnWedged[ViewerColumn[v2]]}; AMEvents.Debugged => { IF v0 # NIL THEN MarkColumnUnWedged[ViewerColumn[v0]]; IF v1 # NIL THEN MarkColumnUnWedged[ViewerColumn[v1]]; IF v2 # NIL THEN MarkColumnUnWedged[ViewerColumn[v2]]}}; AcquireWriteLocks[v0, v1, v2]; proc[]; ReleaseWriteLocks[v0, v1, v2]; END; CallUnderReadLock: PUBLIC SAFE PROC [proc: PROC, viewer: Viewer] = TRUSTED BEGIN ENABLE { AMEvents.Debugging => MarkColumnWedged[ViewerColumn[viewer]]; AMEvents.Debugged => MarkColumnUnWedged[ViewerColumn[viewer]]}; AcquireReadLock[viewer]; proc[]; ReleaseReadLock[viewer]; END; CallUnderReadLocks: PUBLIC SAFE PROC [proc: PROC, v0, v1, v2: Viewer _ NIL] = TRUSTED BEGIN ENABLE { AMEvents.Debugging => { IF v0 # NIL THEN MarkColumnWedged[ViewerColumn[v0]]; IF v1 # NIL THEN MarkColumnWedged[ViewerColumn[v1]]; IF v2 # NIL THEN MarkColumnWedged[ViewerColumn[v2]]}; AMEvents.Debugged => { IF v0 # NIL THEN MarkColumnUnWedged[ViewerColumn[v0]]; IF v1 # NIL THEN MarkColumnUnWedged[ViewerColumn[v1]]; IF v2 # NIL THEN MarkColumnUnWedged[ViewerColumn[v2]]}; }; AcquireReadLocks[v0, v1, v2]; proc[]; ReleaseReadLocks[v0, v1, v2]; END; CallUnderColumnLock: PUBLIC SAFE PROC [proc: PROC, column: Column] = TRUSTED BEGIN ENABLE { AMEvents.Debugging => MarkColumnWedged[column]; AMEvents.Debugged => MarkColumnUnWedged[column]}; Wait: ENTRY PROC = INLINE {ENABLE UNWIND => NULL; WAIT lockFree}; AcquireColumnWriteLock[column ! Wedged => {Wait; RETRY}]; proc[]; ReleaseColumnWriteLock[column]; END; CallUnderColumnLocks: PUBLIC SAFE PROC [proc: PROC, c0, c1: Column] = TRUSTED BEGIN ENABLE { AMEvents.Debugging => {MarkColumnWedged[c0]; MarkColumnWedged[c1]}; AMEvents.Debugged => {MarkColumnUnWedged[c0]; MarkColumnUnWedged[c1]}}; Wait: ENTRY PROC = INLINE {ENABLE UNWIND => NULL; WAIT lockFree}; IF c0 > c1 THEN {c: Column _ c0; c0 _ c1; c1 _ c}; AcquireColumnWriteLock[c0 ! Wedged => {Wait; RETRY}]; AcquireColumnWriteLock[c1 ! Wedged => {Wait; RETRY}]; proc[]; ReleaseColumnWriteLock[c1]; ReleaseColumnWriteLock[c0]; END; CallUnderViewerTreeLock: PUBLIC SAFE PROC [proc: PROC] = TRUSTED BEGIN LockViewerTree[]; proc[]; ReleaseViewerTree[]; END; ViewerColumn: PROC [viewer: Viewer] RETURNS [column: Column] = INLINE {RETURN[IF viewer.iconic THEN static ELSE viewer.column]}; AcquireWriteLock: PUBLIC ENTRY SAFE PROC [viewer: Viewer] = TRUSTED { ENABLE UNWIND => NULL; IF ~WriteLock[viewer] THEN RETURN WITH ERROR Wedged[ViewerColumn[viewer]]}; WriteLock: INTERNAL PROC [viewer: Viewer] RETURNS[success: BOOL _ TRUE] = INLINE BEGIN OPEN viewer; process: PROCESS ~ Process.GetCurrent[]; IF treeRequest THEN WAIT treeLockSynch; -- pause IF ~ReadLockColumn[viewer] THEN RETURN[FALSE]; UNTIL (lock.count=0 OR lock.process=process) DO WAIT lockFree; ENDLOOP; lock _ [process, lock.count+1]; END; ReleaseWriteLock: PUBLIC ENTRY SAFE PROC [viewer: Viewer] = TRUSTED BEGIN OPEN viewer; ENABLE UNWIND => NULL; ReleaseReadLockColumn[ViewerColumn[viewer]]; IF lock.count = 0 THEN Error[]; IF (lock.count _ lock.count-1) = 0 THEN {lock.process _ NIL; BROADCAST lockFree}; END; AcquireWriteLocks: PUBLIC ENTRY SAFE PROC [v0, v1, v2: Viewer _ NIL] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; v: Viewer; IF ~InOrder[v0, v1] THEN {v _ v0; v0 _ v1; v1 _ v}; IF ~InOrder[v1, v2] THEN {v _ v1; v1 _ v2; v2 _ v}; IF ~InOrder[v0, v1] THEN {v _ v0; v0 _ v1; v1 _ v}; IF v0#NIL THEN IF ~WriteLock[v0] THEN RETURN WITH ERROR Wedged[ViewerColumn[v0]]; IF v1#NIL THEN IF ~WriteLock[v1] THEN { ReleaseWriteLock[v0]; RETURN WITH ERROR Wedged[ViewerColumn[v1]]}; IF v2#NIL THEN IF ~WriteLock[v2] THEN { ReleaseWriteLocks[v0, v1]; RETURN WITH ERROR Wedged[ViewerColumn[v2]]}; END; ReleaseWriteLocks: PUBLIC SAFE PROC [v0, v1, v2: Viewer _ NIL] = TRUSTED BEGIN <> IF v0#NIL THEN ReleaseWriteLock[v0]; IF v1#NIL THEN ReleaseWriteLock[v1]; IF v2#NIL THEN ReleaseWriteLock[v2]; END; AcquireReadLock: PUBLIC ENTRY SAFE PROC [viewer: Viewer] = TRUSTED { ENABLE UNWIND => NULL; IF ~ReadLock[viewer] THEN RETURN WITH ERROR Wedged[ViewerColumn[viewer]]}; ReadLock: INTERNAL PROC [viewer: Viewer] RETURNS[success: BOOL _ TRUE] = INLINE BEGIN OPEN viewer; process: PROCESS ~ Process.GetCurrent[]; IF treeRequest THEN WAIT treeLockSynch; -- pause IF ~ReadLockColumn[viewer] THEN RETURN[FALSE]; UNTIL (lock.process=NIL OR lock.process=process) DO WAIT lockFree; ENDLOOP; lock.count _ lock.count+1; END; ReleaseReadLock: PUBLIC ENTRY SAFE PROC [viewer: Viewer] = TRUSTED BEGIN ENABLE UNWIND => NULL; ReleaseReadLockColumn[ViewerColumn[viewer]]; IF viewer.lock.count = 0 THEN Error[]; IF (viewer.lock.count _ viewer.lock.count-1) = 0 THEN BROADCAST lockFree; END; AcquireReadLocks: PUBLIC ENTRY SAFE PROC [v0, v1, v2: Viewer _ NIL] = TRUSTED BEGIN <> ENABLE UNWIND => NULL; v: Viewer; IF ~InOrder[v0, v1] THEN {v _ v0; v0 _ v1; v1 _ v}; IF ~InOrder[v1, v2] THEN {v _ v1; v1 _ v2; v2 _ v}; IF ~InOrder[v0, v1] THEN {v _ v0; v0 _ v1; v1 _ v}; IF v0#NIL THEN IF ~ReadLock[v0] THEN RETURN WITH ERROR Wedged[ViewerColumn[v0]]; IF v1#NIL THEN IF ~ReadLock[v1] THEN { ReleaseReadLock[v0]; RETURN WITH ERROR Wedged[ViewerColumn[v1]]}; IF v2#NIL THEN IF ~ReadLock[v2] THEN { ReleaseReadLocks[v0, v1]; RETURN WITH ERROR Wedged[ViewerColumn[v2]]}; END; ReleaseReadLocks: PUBLIC SAFE PROC [v0, v1, v2: Viewer _ NIL] = TRUSTED BEGIN <> IF v0#NIL THEN ReleaseReadLock[v0]; IF v1#NIL THEN ReleaseReadLock[v1]; IF v2#NIL THEN ReleaseReadLock[v2]; END; AcquireColumnWriteLock: PUBLIC ENTRY SAFE PROC [column: Column] = TRUSTED BEGIN ENABLE UNWIND => NULL; IF ~ColumnLock[column] THEN RETURN WITH ERROR Wedged[column]; END; ColumnLock: INTERNAL PROC [column: Column] RETURNS[success: BOOL _ TRUE] = BEGIN <> process: PROCESS ~ Process.GetCurrent[]; IF columns[column].wedgeCount>0 THEN RETURN[FALSE]; UNTIL (columns[column].lock.count=0 OR columns[column].lock.process=process) DO WAIT lockFree; ENDLOOP; columns[column].lock _ [process, columns[column].lock.count+1]; END; ReleaseColumnWriteLock: PUBLIC ENTRY SAFE PROC [column: Column] = TRUSTED {ENABLE UNWIND => NULL; ReleaseColumnLock[column]}; ReleaseColumnLock: INTERNAL PROC [column: Column] = INLINE { IF columns[column].lock.count = 0 THEN Error[]; IF (columns[column].lock.count _ columns[column].lock.count-1) = 0 THEN {columns[column].lock.process _ NIL; BROADCAST lockFree}}; ReadLockColumn: INTERNAL PROC [viewer: Viewer] RETURNS[success: BOOL _ TRUE] = INLINE BEGIN column: Column _ ViewerColumn[viewer]; process: PROCESS ~ Process.GetCurrent[]; IF columns[column].wedgeCount>0 THEN RETURN[FALSE]; UNTIL (columns[column].lock.process=NIL OR columns[column].lock.process=process) DO WAIT lockFree; column _ ViewerColumn[viewer]; IF columns[column].wedgeCount>0 THEN RETURN[FALSE]; ENDLOOP; columns[column].lock.count _ columns[column].lock.count+1; END; ReleaseReadLockColumn: INTERNAL PROC [column: Column] = INLINE BEGIN IF columns[column].lock.count = 0 THEN Error[]; IF (columns[column].lock.count _ columns[column].lock.count-1) = 0 THEN {columns[column].lock.process _ NIL; BROADCAST lockFree}; END; Wedged: PUBLIC SAFE ERROR [wedged: Column] = CODE; ColumnWedged: PUBLIC ENTRY SAFE PROC [column: Column] RETURNS[wedged, write: BOOLEAN] = TRUSTED {RETURN[columns[column].wedgeCount # 0, columns[column].lock.process # NIL]}; MarkColumnWedged: PUBLIC ENTRY SAFE PROC [column: Column] = TRUSTED BEGIN ENABLE UNWIND => NULL; IF columns[column].wedgeCount = 0 THEN THROUGH [0..3) DO UserTerminal.BlinkDisplay[]; Process.Pause[Process.MsecToTicks[100]]; ENDLOOP; columns[column].wedgeCount _ columns[column].wedgeCount+1; END; MarkColumnUnWedged: PUBLIC ENTRY SAFE PROC [column: Column] = TRUSTED BEGIN ENABLE UNWIND => NULL; IF (columns[column].wedgeCount _ columns[column].wedgeCount-1) = 0 THEN BROADCAST lockFree; END; LockViewerTree: PUBLIC ENTRY SAFE PROC = TRUSTED BEGIN ENABLE UNWIND => NULL; treeRequest _ TRUE; -- synchronise further read and write lock requests FOR column: Column IN Column DO IF ~ColumnLock[column] THEN { FOR c: Column IN [FIRST[Column]..column) DO ReleaseColumnLock[c]; ENDLOOP; treeRequest _ FALSE; RETURN WITH ERROR Wedged[column]}; ENDLOOP; treeRequest _ FALSE; END; ReleaseViewerTree: PUBLIC ENTRY SAFE PROC = TRUSTED BEGIN ENABLE UNWIND => NULL; FOR column: Column IN Column DO ReleaseColumnLock[column]; ENDLOOP; END; ClearAllLocks: ENTRY PROC = BEGIN ClearLock: ViewerOps.EnumProc = CHECKED { IF v.parent = NIL THEN ViewerOps.EnumerateChildren[v, ClearLock]; v.lock _ [NIL, 0]}; FOR c: Column IN Column DO columns[c] _ [[NIL, 0], 0]; ENDLOOP; ViewerOps.EnumerateViewers[ClearLock]; BROADCAST lockFree; END; Process.InitializeCondition[@treeLockSynch, Process.MsecToTicks[50]]; END.