ViewerLocksImpl.mesa; Written by S. McGregor
Edited by McGregor on July 21, 1983 2:58 pm
Last Edited by: Maxwell, February 7, 1983 12:50 pm
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: BOOLFALSE; -- 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: BOOLTRUE] =
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
Viewers must be sorted so that they are locked from highest to lowest, by column.
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
no sort required
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: BOOLTRUE] =
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
viewers must be sorted so that they are locked from highest to lowest
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
no sort required
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: BOOLTRUE] = BEGIN
Same as AcquireColumnWriteLock except does ERROR since internal proc
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: BOOLTRUE] =
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.