/* Parent class of all atomic CSP actors.
Copyright (c) 1998-2006 The Regents of the University of California.
All rights reserved.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the above
copyright notice and the following two paragraphs appear in all copies
of this software.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.
PT_COPYRIGHT_VERSION_2
COPYRIGHTENDKEY
*/
package ptolemy.domains.csp.kernel;
import ptolemy.actor.TypedAtomicActor;
import ptolemy.actor.process.TerminateProcessException;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.InvalidStateException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.Workspace;
//////////////////////////////////////////////////////////////////////////
//// CSPActor
/**
This class is the base class of all atomic actors using the
non-deterministic communication and timed features of the communicating
sequential processes(CSP) domain.
<p>
Two conditional communication constructs are available: "Conditional if"
(CIF) and "Conditional do" (CDO). The constructs are analogous to,
but different from, the common <I>if</I> and <I>do</I> statements. The
steps involved in using both of these are
<BR>(1) create the branches involved and assign an identification number
to each branch.
<BR>(2) call the chooseBranch() method, which invokes the chooseBranch()
method of the controller to determine which branch should succeed.
<BR>(3) execute the statements associated with the successful branch.
<P>
Each branch is either an instance of ConditionalSend or ConditionalReceive,
depending on the communication in the branch. Please see these classes for
details on <I>guarded communication statements</I>, which they represent.
The identification number assigned to each branch only needs to identify
the branch uniquely for one sequence of the steps above. A good example
of how to use a CDO is the code in the actor CSPBuffer, in the
ptolemy.domains.csp.lib package. One significant difference between a
CDO (or CIF) and a common <I>do</I> (<I>if</I>) is that all the branches
are evaluated in parallel, as opposed to sequentially.
<p>The chooseBranch() method takes an array of the branches as an
argument, and simply passes the branches to the chooseBranch() method
of the controller to decide which branch is successful. The successful
branch is the branch that succeeds with its communication. See the
chooseBranch() method of ConditionalBranchController for details
about how the successful branch is chosen.
<p>Time is supported by the method delay(double). This delays the
process until time has advanced the argument time from the current
model time. If this method is called with a zero argument, then
the process continues immediately. As far as each process is
concerned, time can only increase while the process is blocked
trying to rendezvous or when it is delayed. A process can be aware
of the current model time, but it should only affect the model time
through delays. Thus time is centralized in that it is advanced by
the director controlling the process represented by this actor.
<p>A process can also choose to delay its execution until the next
occasion a deadlock occurs by calling _waitForDeadlock(). The
process resumes at the same model time at which it delayed. This is
useful if a process wishes to delay itself until some changes to
the topology have been carried out.
<p> The model of computation used in this domain extends the
original CSP, as proposed by Hoare in 1978, model of computation in
two ways. First it allows non-deterministic communication using
both sends and receives. The original model only allowed
non-deterministic receives. Second, a centralized notion of time
has been added. The original proposal was untimed. Neither of these
extensions are new, but it is worth noting the differences between
the model used here and the original model. If an actor wishes to
use either non-deterministic rendezvous or time, it must derive
from this class. Otherwise deriving from AtomicActor is sufficient.
<p>
@author Neil Smyth, Bilung Lee
@version $Id: CSPActor.java,v 1.92 2006/02/07 00:45:34 cxh Exp $
@since Ptolemy II 0.2
@Pt.ProposedRating Green (nsmyth)
@Pt.AcceptedRating Yellow (liuj)
@see ConditionalBranch
@see ConditionalReceive
@see ConditionalSend
*/
public class CSPActor extends TypedAtomicActor implements BranchActor {
/** Construct a CSPActor in the default workspace with an empty string
* as its name.
* The object is added to the workspace directory.
* Increment the version number of the workspace.
*/
public CSPActor() {
super();
}
/** Construct a CSPActor in the specified workspace with an empty
* string as a name. You can then change the name with setName().
* If the workspace argument is null, then use the default workspace.
* The object is added to the workspace directory.
* Increment the version number of the workspace.
*
* @param workspace The workspace that will list the entity.
*/
public CSPActor(Workspace workspace) {
super(workspace);
}
/** Construct a CSPActor in the specified container with the specified
* name. The name must be unique within the container or an exception
* is thrown. The container argument must not be null, or a
* NullPointerException will be thrown.
*
* @param container The TypedCompositeActor that contains this actor.
* @param name The actor's name.
* @exception IllegalActionException If the entity cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the name argument coincides
* with an entity already in the container.
*/
public CSPActor(CompositeEntity container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
_conditionalBranchController = new ConditionalBranchController(this);
}
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Determine which branch succeeds with a rendezvous. Pass
* the branches to ConditionalBranchController to decide.
* <p>
* @param branches The set of conditional branches involved.
* @return The ID of the successful branch, or -1 if none of the
* branches were enabled.
* @exception IllegalActionException If the rendezvous fails
* (e.g. because of incompatible types).
*/
public int chooseBranch(ConditionalBranch[] branches)
throws IllegalActionException {
return _conditionalBranchController.chooseBranch(branches);
}
/** Clone this actor into the specified workspace. The new actor is
* <i>not</i> added to the directory of that workspace (you must do this
* yourself if you want it there).
* The result is a new actor with the same number of ports (cloned)
* as the original, but no connections and no container.
* A container must be set before much can be done with the actor.
* <p>
* @param workspace The workspace for the cloned object.
* @exception CloneNotSupportedException If cloned ports cannot have
* as their container the cloned entity (this should not occur), or
* if one of the attributes cannot be cloned.
* @return A new CSPActor.
*/
public Object clone(Workspace workspace) throws CloneNotSupportedException {
CSPActor newObject = (CSPActor) super.clone(workspace);
newObject._delayed = false;
newObject._conditionalBranchController = new ConditionalBranchController(
newObject);
return newObject;
}
/** Delay this actor. The actor resumes executing when the
* director advances the current model time to
* "getCurrentTime() + delta". If the actor tries to
* delay for a negative time, an exception is thrown. A delay
* of zero time has no effect and this method returns immediately.
* @param delta The time to delay this actor for from the current
* time.
* @exception IllegalActionException If the argument is negative.
* @exception TerminateProcessException If the director requests
* termination by calling the protected method _cancelDelay().
*/
public void delay(double delta) throws IllegalActionException {
try {
CSPDirector director = (CSPDirector) getDirector();
synchronized (director) {
if (delta == 0.0) {
return;
} else if (delta < 0.0) {
throw new IllegalActionException(this,
"delay() called with a negative argument: " + delta);
} else {
_delayed = true;
director._actorDelayed(delta, this);
while (_delayed) {
director.wait();
}
if (_cancelDelay) {
// Throwing this exception is really not
// not necessary for a "well" written actor.
throw new TerminateProcessException("delay cancelled");
}
}
}
} catch (InterruptedException ex) {
throw new TerminateProcessException("CSPActor interrupted "
+ "while delayed.");
}
}
/** Return the conditional branch control of this actor.
*/
public AbstractBranchController getBranchController() {
return _conditionalBranchController;
}
/** Initialize the state of the actor.
* @exception IllegalActionException Not thrown in this base class,
* but might be in a derived class.
*/
public void initialize() throws IllegalActionException {
super.initialize();
_delayed = false;
_cancelDelay = false;
if (_debugging) {
_conditionalBranchController.addDebugListener(this);
}
}
/** Return false. If an actor wishes to continue for more than
* one iteration it should override this method to return true.
* @return True if another iteration can occur.
*/
public boolean postfire() {
if (_debugging) {
_debug("Invoking postfire.");
}
return false;
}
/** Terminate abruptly any threads created by this actor. Note that
* this method does not allow the threads to terminate gracefully.
*/
public void terminate() {
synchronized (getDirector()) {
_conditionalBranchController.terminate();
}
}
///////////////////////////////////////////////////////////////////
//// protected methods ////
/** If the actor is delayed, then cancel the delay.
* Some time after this method is called, the thread that called
* delay() will be restarted and the delay() method will throw
* a TerminateProcessException.
*/
protected void _cancelDelay() {
Object director = getDirector();
synchronized (director) {
if (_delayed) {
_cancelDelay = true;
_delayed = false;
director.notifyAll();
}
}
}
/** Resume a delayed actor. This method is only called by CSPDirector
* after time has sufficiently advanced.
*/
protected void _continue() {
if (_delayed == false) {
throw new InvalidStateException("CSPActor._continue() "
+ "called on an actor that was not delayed: " + getName());
}
Object director = getDirector();
synchronized (director) {
_delayed = false;
director.notifyAll();
}
}
/** Wait for deadlock to occur. The current model time will not
* advance while this actor is delayed. This method may be useful if
* an actor wishes to delay itself until some topology changes
* have been carried out.
*/
protected void _waitForDeadlock() {
try {
CSPDirector director = (CSPDirector) getDirector();
synchronized (director) {
_delayed = true;
director._actorDelayed(0.0, this);
while (_delayed) {
director.wait();
}
}
} catch (InterruptedException ex) {
throw new TerminateProcessException("CSPActor interrupted "
+ "while waiting for deadlock.");
}
}
///////////////////////////////////////////////////////////////////
//// private variables ////
/** Flag that causes the delay() method to abort with an exception. */
private boolean _cancelDelay = false;
/** This object is in charge of all conditional rendezvous issues. */
private ConditionalBranchController _conditionalBranchController = null;
/** Flag indicating this actor is delayed. */
private boolean _delayed = false;
}