public final class Workspace extends java.lang.Object implements Nameable
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.
Constructor and Description |
---|
Workspace()
Create a workspace with an empty string as its name.
|
Workspace(java.lang.String name)
Create a workspace with the specified name.
|
Modifier and Type | Method and Description |
---|---|
protected java.lang.String |
_description(int detail,
int indent,
int bracket)
Return a description of the workspace.
|
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<NamedObj> |
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.
|
public Workspace()
public Workspace(java.lang.String name)
name
- Name of the workspace.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
- If thrown while getting the
description of subcomponents.public java.lang.String description(int detail) throws IllegalActionException
detail
- The level of detail.IllegalActionException
- If thrown while getting the
description of subcomponents.@Deprecated public java.util.Enumeration directory()
public java.util.List<NamedObj> directoryList()
public final void doneReading()
InvalidStateException
- If this method is called
before a corresponding call to getReadAccess() by the same thread.getReadAccess()
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()
This method suspends the calling thread until such access has been obtained. If the calling thread is interrupted while waiting to get write access, an InternalErrorException is thrown, and the thread does not have write permission to the workspace. It is essential that a call to this method is matched by a call to doneWriting(), regardless of whether this method returns normally or an exception is thrown. This is to ensure that the workspace is in a consistent state, otherwise read or write access may never again be granted in this workspace.
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)
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 { synchronized(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
- If thrown while getting the
description of subcomponents.