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: 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
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:
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
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:
BOOL ←
TRUE] =
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:
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.