ViewerLocks.mesa; Written by S. McGregor
Edited by McGregor on July 21, 1983 10:42 am
Last Edited by: Maxwell, January 13, 1983 2:50 pm
Last Edited by: Wyatt, November 4, 1983 9:57 am
DIRECTORY
ViewerClasses USING [Column, Viewer];
ViewerLocks: CEDAR DEFINITIONS
= BEGIN OPEN ViewerClasses;
ViewerLocks is an interlocking mechanism to arbitrate access to the Viewer window tree, viewer columns, and individual viewers. A particular viewer may be locked for reading or for writing. A read request implies that the calling code would like to examine values in the ViewerClasses.ViewerRec and insure that these values are invariant for the duration that the read lock is held. Any number of clients can simultaneously hold read locks on a single viewer. A write lock is an exclusive access to a viewer for the implied operation of modifying one or more of the fields in the ViewerClasses.ViewerRec.
Locks are process re-entrant, meaning that a single process may request a lock multiple times without deadlock; i.e. if a process currently has write access to a viewer, it may in addition request read or write access (this obviously doesn't grant any new permissions, but is useful when a client has already locked a viewer and wishes to call code that may attempt a lock). Due to the ordering of the read and write resources, a process holding a read lock may not request a write lock on the same viewer without first releasing the read lock.
The viewer tree as well as the particular columns are another set of resources that may be locked. Locking a column is equivalent to (but not implemented as) locking all of the viewers in that column. Locking the viewer tree is equivalent to locking all of the columns for write in addition to the root pointer for the tree. The viewer tree must be locked by the client any time a top level viewer changes size or position in the tree that crosses column boundaries. Since all painting operations are protected by a read lock on the updated viewer, the viewer tree lock will not be granted until painting activity has subsided.
The hierarchy of locks in the viewers window package is, from highest to lowest: the viewer tree, a write lock on a column, a write lock on a particular viewer, a read lock on a column, and a read lock on a particular viewer. A client may only request a new lock if it is at an equal or lower logical level. The documentation in the viewer interfaces has been updated to show which locks the operation requires, so that the client can avoid deadlocks.
A fine point: if a client program needs to lock multiple viewers, it must acquire the lock for each viewer in the proper order. Routines are provided below that correctly sort the parameter list for up to three viewers; otherwise the client must order the lock requests so as to lock the viewers in ref order (treating the viewer ref as a LONG CARDINAL and locking the highest first). Columns must be locked in the order {static, left, right, color}.
Users are advised to make use of the call-back operations below when they need to perform locked operations on viewers. The suggested usage is to create a local procedure containing the critical code, which is passed to the routines below. The local procedure will then have full access to all of the variables in the outer procedure.
CallUnderWriteLock:
PROC [proc:
PROC, viewer: Viewer] ;
Call a client procedure while holding a write lock on a viewer.
CallUnderWriteLocks:
PROC [proc:
PROC, v0, v1, v2: Viewer ←
NIL] ;
Call a client procedure while holding write locks on a set of viewers.
CallUnderReadLock:
PROC [proc:
PROC, viewer: Viewer] ;
Call a client procedure while holding a read lock on a viewer.
CallUnderReadLocks:
PROC [proc:
PROC, v0, v1, v2: Viewer ←
NIL] ;
Call a client procedure while holding read locks on a set of viewers.
CallUnderColumnLock:
PROC [proc:
PROC, column: Column] ;
Call a client procedure while holding write locks for a particular column.
CallUnderColumnLocks:
PROC [proc:
PROC, c0, c1: Column] ;
Call a client procedure while holding write locks for two columns.
CallUnderViewerTreeLock:
PROC [proc:
PROC] ;
Call a client procedure while holding write locks for all columns and viewers.
LockOrder:
PROC [v0, v1: Viewer]
RETURNS [
BOOL] =
INLINE {
RETURN [LOOPHOLE[v0, LONG CARDINAL] >= LOOPHOLE[v1, LONG CARDINAL]] };
Returns true if ok to lock v0 before v1.
The operations below are the basic locking operations and require considerably more sophistication to use, because of a number of special arrangements with the Cedar debugger. Please use the call-back operations above unless you are a window manager wizard.
AcquireWriteLock:
PRIVATE
PROC [viewer: Viewer] ;
Grant write (exclusive) access to a particular viewer; also acquires a read lock for the viewer column.
ReleaseWriteLock:
PRIVATE
PROC [viewer: Viewer] ;
Release write access to viewer and read access to corresponding column.
AcquireWriteLocks:
PRIVATE
PROC [v0, v1, v2: Viewer ←
NIL] ;
Grant write (exclusive) access to a set of viewers, sorting them in correct locking order.
ReleaseWriteLocks:
PRIVATE
PROC [v0, v1, v2: Viewer ←
NIL] ;
Release write access to a set of viewers. Provided for convenience.
AcquireReadLock:
PRIVATE
PROC [viewer: Viewer] ;
Grant read (shared) access to a particular viewer; also acquires a read lock for the viewer column.
ReleaseReadLock:
PRIVATE
PROC [viewer: Viewer] ;
Release read access to viewer and read access to corresponding column.
AcquireReadLocks:
PRIVATE
PROC [v0, v1, v2: Viewer ←
NIL] ;
Grant read (shared) access to a set of viewers, sorting them in correct locking order.
ReleaseReadLocks:
PRIVATE
PROC [v0, v1, v2: Viewer ←
NIL] ;
Release read access to a set of viewers. Provided for convenience.
AcquireColumnWriteLock:
PRIVATE
PROC [column: Column] ;
Grant write (exclusive) access to a particular column. Logically equivalent to (but not implemented as) acquiring a write lock for all viewers in that column.
ReleaseColumnWriteLock:
PRIVATE
PROC [column: Column] ;
Release write access to column.
Wedged:
ERROR [column: Column] ;
An attempt to acquire a column lock may fail if a process currently holding a column lock is caught in the debugger from either an uncaught signal or a breakpoint. In such a case, the signal Wedged is raised by the locking machinery.
ColumnWedged: PRIVATE PROC[column: Column] RETURNS[wedged, write: BOOLEAN];
MarkColumnWedged:
PRIVATE
PROC [column: Column] ;
The debugger will raise the information signal AMEvents.Debugging when a process takes a breakpoint or gets and uncaught signal. Any client that locks a viewer or column must catch this signal and call MarkColumnWedged, marking the set of columns they have currently locked. Failure to do so may result in deadlock in the event that an uncaught signal or breakpoint is encountered.
MarkColumnUnWedged:
PRIVATE
PROC [column: Column] ;
The debugger will raise the information signal AMEvents.Debugged when a process is aborted from the debugger or when it has proceeded. Any client that locks a viewer or column must catch this signal and call MarkColumnUnWedged, marking the set of columns they have currently locked. Failure to do so may result in deadlock in the event that an uncaught signal or breakpoint is encountered.
LockViewerTree:
PRIVATE
PROC ;
Grant write (exclusive) access to all viewers and root of viewer tree. Logically equivalent to acquiring a write lock for all columns. Note that the signal ColumnWedged may be raised when this operation is attempted, and the comments above on MarkColumnWedged and MarkColumnUnWedged apply.
ReleaseViewerTree:
PRIVATE
PROC ;
Release access to viewer tree.
END.