|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES All Classes | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectptolemy.kernel.util.Workspace
public final class Workspace
An instance of Workspace is used for synchronization and version tracking of interdependent groups of objects. These objects are said to be in the workspace. This is not the same as the container association in Ptolemy II. A workspace is never returned by a getContainer() method.
The workspace provides a rudimentary directory service that can be used to keep track of the objects within it. It is not required to use it in order to use the workspace for synchronization. Items are added to the directory by calling add(). The names of the items in the directory are not required to be unique.
The synchronization model of the workspace is a multiple-reader, single-writer model. Any number of threads can simultaneously read the workspace. Only one thread at a time can have write access to the workspace, and while the write access is held, no other thread can get read access.
When reading the state of objects in the workspace, a thread must ensure that no other thread is simultaneously modifying the objects in the workspace. To read-synchronize on a workspace, use the following code:
try { _workspace.getReadAccess(); // ... code that reads } finally { _workspace.doneReading(); }We assume that the _workspace variable references the workspace, as for example in the NamedObj class. The getReadAccess() method suspends the current thread if another thread is currently modifying the workspace, and otherwise returns immediately. Note that multiple readers can simultaneously have read access. The finally clause is executed even if an exception occurs. This is essential because without the call to doneReading(), the workspace will never again allow any thread to modify it.
To make safe changes to the objects in a workspace, a thread must write-synchronize using the following code:
try { _workspace.getWriteAccess(); // ... code that writes } finally { _workspace.doneWriting(); }Again, the call to doneWriting() is essential, or the workspace will remain permanently locked to either reading or writing.
Note that it is not necessary to obtain a write lock just to add an item to the workspace directory. The methods for accessing the directory are all synchronized, so there is no risk of any thread reading an inconsistent state.
A major subtlety in using this class concerns acquiring a write lock while holding a read lock. If a thread holds a read lock and calls getWriteAccess(), if any other thread holds a read lock, then the call to getWriteAccess() will block the calling thread until those other read accesses are released. However, while the thread is blocked, it yields its read permissions. This prevents a deadlock from occurring, but it means that the another thread may acquire write permission while the thread is stalled and modify the model. Specifically, the pattern is:
try { _workspace.getReadAccess(); ... do things ... try { _workspace.getWriteAccess(); ... at this point, the structure of a model may have changed! ... ... make my own changes knowing that the structure may have changed... } finally { _workspace.doneWriting(); } ... continue doing things, knowing the model may have changed... } finally { _workspace.doneReading(); }Unfortunately, a user may acquire a read access and invoke a method that, unknown to the user, acquires write access. For this reason, it is very important to document methods that acquire write access, and to avoid invoking them within blocks that hold read access. Note that there is no difficulty acquiring read access from within a block holding write access.
Green (liuxj) |
Green (liuxj) |
Nested Class Summary | |
---|---|
private static class |
Workspace.AccessRecord
|
Field Summary | |
---|---|
private java.util.LinkedList |
_directory
|
private java.lang.Thread |
_lastReader
|
private Workspace.AccessRecord |
_lastReaderRecord
|
private java.lang.String |
_name
|
private long |
_numReaders
|
private java.util.HashMap |
_readerRecords
|
private long |
_version
|
private int |
_waitingWriteRequests
|
private int |
_writeDepth
|
private java.lang.Thread |
_writer
|
Constructor Summary | |
---|---|
Workspace()
Create a workspace with an empty string as its name. |
|
Workspace(java.lang.String name)
Create a workspace with the specified name. |
Method Summary | |
---|---|
protected java.lang.String |
_description(int detail,
int indent,
int bracket)
Return a description of the workspace. |
private void |
_doneWriting(boolean incrementWorkspaceVersion)
Indicate that the calling thread is finished writing. |
private Workspace.AccessRecord |
_getAccessRecord(java.lang.Thread current,
boolean createNew)
Return the AccessRecord object for the specified thread. |
private void |
_reacquireReadPermissions(int count)
|
private int |
_releaseAllReadPermissions()
Frees the thread of all the readAccesses on the workspace held by the current thread. |
void |
add(NamedObj item)
Add an item to the directory. |
java.lang.String |
description()
Return a full description of the workspace and everything in its directory. |
java.lang.String |
description(int detail)
Return a description of the workspace. |
java.util.Enumeration |
directory()
Deprecated. Use directoryList() instead. |
java.util.List |
directoryList()
Return an unmodifiable list of the items in the directory, in the order in which they were added. |
void |
doneReading()
Indicate that the calling thread is finished reading. |
void |
doneTemporaryWriting()
Indicate that the calling thread is finished writing. |
void |
doneWriting()
Indicate that the calling thread is finished writing. |
NamedObj |
getContainer()
Get the container. |
java.lang.String |
getDisplayName()
Return a name to present to the user, which is the same as what is returned by getName(). |
java.lang.String |
getFullName()
Get the full name. |
java.lang.String |
getName()
Get the name. |
java.lang.String |
getName(NamedObj relativeTo)
Get the name. |
void |
getReadAccess()
Obtain permission to read objects in the workspace. |
long |
getVersion()
Get the version number. |
void |
getWriteAccess()
Obtain permission to write to objects in the workspace. |
boolean |
handleModelError(NamedObj context,
IllegalActionException exception)
Handle a model error by throwing the specified exception. |
void |
incrVersion()
Increment the version number by one. |
void |
reacquireReadPermission(int depth)
Reacquire read permission on the workspace for the current thread. |
int |
releaseReadPermission()
Release read permission on the workspace held by the current thread, and return the depth of the nested calls to getReadAccess(). |
void |
remove(NamedObj item)
Remove the specified item from the directory. |
void |
removeAll()
Remove all items from the directory. |
void |
setName(java.lang.String name)
Set or change the name. |
java.lang.String |
toString()
Return a concise description of the object. |
void |
wait(java.lang.Object obj)
Release all the read accesses held by the current thread and suspend the thread by calling Object.wait() on the specified object. |
void |
wait(java.lang.Object obj,
long timeout)
This method is equivalent to the single argument version except that you can specify a timeout, which is in milliseconds. |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
Field Detail |
---|
private java.util.LinkedList _directory
private java.lang.String _name
private long _version
private transient java.lang.Thread _writer
private int _waitingWriteRequests
private int _writeDepth
private transient java.lang.Thread _lastReader
private transient Workspace.AccessRecord _lastReaderRecord
private transient java.util.HashMap _readerRecords
private long _numReaders
Constructor Detail |
---|
public Workspace()
public Workspace(java.lang.String name)
name
- Name of the workspace.Method Detail |
---|
public void add(NamedObj item) throws IllegalActionException
item
- Item to list in the directory.
IllegalActionException
- If the item has a container, is
already in the directory, or is not in this workspace.public java.lang.String description() throws IllegalActionException
description
in interface Nameable
IllegalActionException
public java.lang.String description(int detail) throws IllegalActionException
detail
- The level of detail.
IllegalActionException
public java.util.Enumeration directory()
public java.util.List directoryList()
public final void doneReading()
InvalidStateException
- If this method is called
before a corresponding call to getReadAccess() by the same thread.public final void doneTemporaryWriting()
InvalidStateException
- If this method is called before
a corresponding call to getWriteAccess() by the same thread.Attribute.Attribute(NamedObj, String, boolean)
,
doneWriting()
public final void doneWriting()
InvalidStateException
- If this method is called before
a corresponding call to getWriteAccess() by the same thread.public NamedObj getContainer()
getContainer
in interface Nameable
public java.lang.String getDisplayName()
getDisplayName
in interface Nameable
getName()
public java.lang.String getFullName()
getFullName
in interface Nameable
public java.lang.String getName()
getName
in interface Nameable
setName(String)
public java.lang.String getName(NamedObj relativeTo)
getName
in interface Nameable
relativeTo
- This argument is ignored.
setName(String)
public final void getReadAccess()
InternalErrorException
- If the calling thread is interrupted
while waiting to get read access.doneReading()
public final long getVersion()
public final void getWriteAccess()
InternalErrorException
- If the calling thread is interrupted
while waiting to get write access.doneWriting()
public boolean handleModelError(NamedObj context, IllegalActionException exception) throws IllegalActionException
context
- The object in which the error occurred.exception
- An exception that represents the error.
IllegalActionException
- The exception passed
as an argument is always thrown.public final void incrVersion()
public void reacquireReadPermission(int depth)
depth
- The depth of the permissions to reacquire.releaseReadPermission()
public int releaseReadPermission()
int depth = releaseReadPermission(); try { ... do whatever here ... } finally { reacquireReadPermission(depth); }Note that this is done automatically by the wait(Object) method, so if you can use that instead, please do.
reacquireReadPermission(int)
,
wait(Object)
,
wait(Object, long)
public void remove(NamedObj item)
item
- The NamedObj to be removed.public void removeAll()
public void setName(java.lang.String name)
setName
in interface Nameable
name
- The new name.getName()
public java.lang.String toString()
toString
in class java.lang.Object
public void wait(java.lang.Object obj) throws java.lang.InterruptedException
int depth = 0; try { synchrononized(obj) { ... depth = releaseReadPermission(); obj.wait(); } } finally { if (depth > 0) { reacquireReadPermission(depth); } }
obj
- The object that the thread wants to wait on.
java.lang.InterruptedException
- If the calling thread is interrupted
while waiting on the specified object and all the read accesses held
earlier by the thread are re-acquired.
InternalErrorException
- If re-acquiring the read accesses
held earlier by the thread fails.public void wait(java.lang.Object obj, long timeout) throws java.lang.InterruptedException
obj
- The object that the thread wants to wait on.timeout
- The maximum amount of time to wait, in milliseconds,
or zero to not specify a timeout.
java.lang.InterruptedException
- If the calling thread is interrupted
while waiting on the specified object and all the read accesses held
earlier by the thread are re-acquired.
InternalErrorException
- If re-acquiring the read accesses
held earlier by the thread fails.wait(Object)
protected java.lang.String _description(int detail, int indent, int bracket) throws IllegalActionException
detail
- The level of detail.indent
- The amount of indenting.bracket
- The number of surrounding brackets (0, 1, or 2).
IllegalActionException
private final void _doneWriting(boolean incrementWorkspaceVersion)
incrementWorkspaceVersion
- True if we should increment
the version. The incrementWorkspaceVersion parameter should
almost always be true, if it is set to false, then perhaps a
temporary variable is being added.
InvalidStateException
- If this method is called before
a corresponding call to getWriteAccess() by the same thread.private final Workspace.AccessRecord _getAccessRecord(java.lang.Thread current, boolean createNew)
private void _reacquireReadPermissions(int count)
private int _releaseAllReadPermissions()
|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES All Classes | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |