/*
* Code generator helper class associated with the PNDirector class.
*
* Copyright (c) 2008-2009 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.codegen.c.domains.pn.kernel;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import ptolemy.actor.Actor;
import ptolemy.actor.CompositeActor;
import ptolemy.actor.IOPort;
import ptolemy.actor.lib.LimitedFiringSource;
import ptolemy.actor.util.DFUtilities;
import ptolemy.codegen.actor.Director;
import ptolemy.codegen.kernel.CodeGeneratorHelper;
import ptolemy.codegen.kernel.PortCodeGenerator;
import ptolemy.data.BooleanToken;
import ptolemy.data.IntToken;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.Entity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NamedObj;
//////////////////////////////////////////////////////////////////
////PNDirector
/**
* Code generator helper associated with the PNDirector class. This director
* initializes all the actors, then starts a thread for each actor that invokes
* the fire code for the actor in an infinite loop.
*
* FIXME: How to make it possible for executions to be finite?
*
* @author Edward A. Lee (based on SDFDirector helper class)
* @version $Id$
* @since Ptolemy II 7.1
* @Pt.ProposedRating Yellow (eal)
* @Pt.AcceptedRating Red (eal)
*/
public class PNDirector extends Director {
/**
* Construct the code generator helper associated with the given PNDirector.
*
* @param pnDirector The associated ptolemy.domains.pn.kernel.PNDirector
*/
public PNDirector(ptolemy.domains.pn.kernel.PNDirector pnDirector) {
super(pnDirector);
}
// //////////////////////////////////////////////////////////////////////
// // public methods ////
/**
* Generate the body code that lies between variable declaration and wrapup.
*
* @return The generated body code.
* @exception IllegalActionException If the {@link #_generateFireCode()}
* method throws the exceptions.
*/
public String generateFireCode() throws IllegalActionException {
StringBuffer code = new StringBuffer();
CompositeActor compositeActor = (CompositeActor) _director
.getContainer();
code.append(_codeGenerator.comment("Create a thread for each actor."));
code.append("pthread_attr_init(&pthread_custom_attr);" + _eol + _eol);
List<Actor> actorList = compositeActor.deepEntityList();
for (Actor actor : actorList) {
// Generate the thread pointer.
code.append("pthread_t thread_");
code.append(_getActorThreadLabel(actor));
code.append(";" + _eol);
}
for (Actor actor : actorList) {
code.append("pthread_create(");
code.append("&thread_" + _getActorThreadLabel(actor));
code.append(", &pthread_custom_attr, ");
code.append(_getActorThreadLabel(actor));
code.append(", NULL);" + _eol);
}
for (Actor actor : actorList) {
code.append("pthread_join(");
code.append("thread_" + _getActorThreadLabel(actor));
code.append(", NULL);" + _eol);
}
return code.toString();
}
/**
* Get the files needed by the code generated from this helper class. This
* base class returns an empty set.
*
* @return A set of strings that are header files needed by the code
* generated from this helper class.
* @exception IllegalActionException If something goes wrong.
*/
public Set getHeaderFiles() throws IllegalActionException {
Set files = new HashSet();
files.add("<pthread.h>");
// files.add("<thread.h>");
return files;
}
/**
* Return the libraries specified in the "libraries" blocks in the templates
* of the actors included in this CompositeActor.
*
* @return A Set of libraries.
* @exception IllegalActionException If thrown when gathering libraries.
*/
public Set getLibraries() throws IllegalActionException {
Set libraries = new LinkedHashSet();
libraries.add("pthread");
// libraries.add("thread");
return libraries;
}
/**
* Return the code for getting data from the specific port channel.
* @param port The specified port.
* @param channel The specified channel.
* @return the code for getting data from the specific port channel,
* in this case, return the empty string.
* @exception IllegalActionException Not thrown here.
*/
public String generateCodeForGet(IOPort port, int channel)
throws IllegalActionException {
// getReadOffset()
// incrementReadOffset()
return "";
}
/**
* Return the code for sending data from the specific port channel.
* @param port The specified port.
* @param channel The specified channel.
* @return the code for sending data from the specific port channel,
* in this case, return the empty string.
* @exception IllegalActionException Not thrown here.
*/
public String generateCodeForSend(IOPort port, int channel)
throws IllegalActionException {
return "";
}
/**
* Return the generated label for the director header.
* @return The generated label for the director header.
*/
public String generateDirectorHeader() {
return CodeGeneratorHelper.generateName(_director) + "_controlBlock";
}
/**
* Generate the initialize code for the associated PN director.
* @return The generated initialize code.
* @exception IllegalActionException If the helper associated with the
* director throws it while generating initialize code.
*/
public String generateInitializeCode() throws IllegalActionException {
StringBuffer code = new StringBuffer();
code.append(_codeGenerator
.comment("Initialization code of the PNDirector."));
// Don't generate the code to initialize all the actors.
// code.append(super.generateInitializeCode());
List args = new LinkedList();
args.add(generateDirectorHeader());
args.add(_generateActorNameFileForDebugging());
// Initialize the director head variable.
code.append(_codeStream.getCodeBlock("initBlock", args));
args.remove(1);
// Initialize each buffer variables.
for (String bufferVariable : _buffers) {
args.set(0, bufferVariable);
code.append(_codeStream.getCodeBlock("initBuffer", args));
}
return code.toString();
}
/**
* Return the main loop code for the associated PN director.
* @return The generated main loop code.
* @exception IllegalActionException If the helper associated with the
* director throws it while generating main loop code.
*/
public String generateMainLoop() throws IllegalActionException {
StringBuffer code = new StringBuffer();
code
.append(((CodeGeneratorHelper) _getHelper(_director
.getContainer())).generateFireCode());
return code.toString();
}
/**
* Return the generated header label for the specified port channel.
* @param port The specified port.
* @param i The specified channel number.
* @return The generated header label for the specified port channel.
*/
public static String generatePortHeader(IOPort port, int i) {
return CodeGeneratorHelper.generateName(port) + "_" + i + "_pnHeader";
}
/**
* Generate the postfire code for the associated PN director.
* @return The generated postfire code.
* @exception IllegalActionException If the helper associated with the
* director throws it while generating postfire code.
*/
public String generatePostfireCode() throws IllegalActionException {
return "";
}
/**
* Generate the preinitialize code for the associated PN director.
*
* @return The generated preinitialize code.
* @exception IllegalActionException If the helper associated with an actor
* throws it while generating preinitialize code for the actor.
*/
public String generatePreinitializeCode() throws IllegalActionException {
StringBuffer bufferCode = new StringBuffer();
_buffers.clear();
List actorList = ((CompositeEntity) _director.getContainer())
.deepEntityList();
Iterator actors = actorList.iterator();
while (actors.hasNext()) {
Entity actor = (Entity) actors.next();
Iterator ports = actor.portList().iterator();
while (ports.hasNext()) {
IOPort port = (IOPort) ports.next();
bufferCode.append(_createDynamicOffsetVariables(port));
}
}
StringBuffer code = new StringBuffer(super.generatePreinitializeCode());
List args = new LinkedList();
args.add(generateDirectorHeader());
args.add(((CompositeActor) _director.getContainer()).deepEntityList()
.size());
args.add(_buffers.size());
args.add(_generateActorNameFileForDebugging());
code.append(_codeStream.getCodeBlock("preinitBlock", args));
code.append(bufferCode);
_generateThreadFunctionCode(code);
return code.toString();
}
/**
* Generate the transfer output code for the specified port.
* @param port The specified port.
* @param code The given buffer to generate code to.
* @exception IllegalActionException Not thrown here.
*/
public void generateTransferOutputsCode(IOPort port, StringBuffer code)
throws IllegalActionException {
code.append(_codeGenerator.comment("PNDirector: "
+ "Transfer tokens to the outside."));
}
/**
* Generate code for transferring enough tokens to complete an internal
* iteration.
*
* @param inputPort The port to transfer tokens.
* @param code The string buffer that the generated code is appended to.
* @exception IllegalActionException If thrown while transferring tokens.
*/
public void generateTransferInputsCode(IOPort inputPort, StringBuffer code)
throws IllegalActionException {
code.append(_codeGenerator.comment("PNDirector: "
+ "Transfer tokens to the inside."));
int rate = DFUtilities.getTokenConsumptionRate(inputPort);
CompositeActor container = (CompositeActor) getComponent()
.getContainer();
ptolemy.codegen.c.actor.TypedCompositeActor compositeActorHelper = (ptolemy.codegen.c.actor.TypedCompositeActor) _getHelper(container);
for (int i = 0; i < inputPort.getWidth(); i++) {
if (i < inputPort.getWidthInside()) {
String name = generateSimpleName(inputPort);
if (inputPort.isMultiport()) {
name = name + '#' + i;
}
for (int k = 0; k < rate; k++) {
code.append(compositeActorHelper.getReference("@" + name
+ "," + k));
code.append(" = " + _eol);
code.append(compositeActorHelper.getReference(name + ","
+ k));
code.append(";" + _eol);
}
}
}
// Generate the type conversion code before fire code.
code.append(compositeActorHelper.generateTypeConvertFireCode(true));
// The offset of the input port itself is updated by outside director.
_updateConnectedPortsOffset(inputPort, code, rate);
}
/**
* Generate variable initialization for the referenced parameters.
*
* @return code The generated code.
* @exception IllegalActionException If the helper class for the model
* director cannot be found.
*/
public String generateVariableInitialization()
throws IllegalActionException {
return "";
}
/**
* Generate the wrapup code for the associated PN director.
*
* @return The generated preinitialize code.
* @exception IllegalActionException If the helper associated with an actor
* throws it while generating preinitialize code for the actor.
*/
public String generateWrapupCode() throws IllegalActionException {
StringBuffer code = new StringBuffer();
// Note: We don't need to call the super class method nor
// append the wrapup code for each of the containing actor.
// Instead, the actor wrapup code resides in the actor
// thread function code.
List args = new LinkedList();
args.add(generateDirectorHeader());
args.add(_generateActorNameFileForDebugging());
code.append(_codeStream.getCodeBlock("wrapupBlock", args));
return code.toString();
}
/**
* Return the size of the generated buffer for the specified port channel.
* This returns the value of "initialQueueCapacity" parameter of the
* PNDirector
* @param port The specified port.
* @param channelNumber The specified channel number.
* @return The size of the generated buffer.
* @exception IllegalActionException If an error occurs when getting the
* value from the parameter.
*/
public int getBufferSize(IOPort port, int channelNumber)
throws IllegalActionException {
IntToken sizeToken = (IntToken) ((ptolemy.domains.pn.kernel.PNDirector) _director).initialQueueCapacity
.getToken();
// FIXME: Force buffer size to be at least 2.
// We need to handle size 1 as special case.
return Math.max(sizeToken.intValue(), 2);
}
/**
* Generate the shared code for the associated PN director.
* @return The generated shared code.
* @exception IllegalActionException If the helper associated with the
* director throws it while generating shared code.
*/
public Set getSharedCode() throws IllegalActionException {
Set sharedCode = new HashSet();
// sharedCode.add(_codeStream.getCodeBlock("sharedBlock"));
return sharedCode;
}
// //////////////////////////////////////////////////////////////////////
// // protected methods ////
/**
* Create offset variables for the channels of the given port. The offset
* variables are generated unconditionally.
* @param port The port whose offset variables are generated.
* @return Code that declares the read and write offset variables.
* @exception IllegalActionException If getting the rate or reading
* parameters throws it.
*/
protected String _createDynamicOffsetVariables(IOPort port)
throws IllegalActionException {
StringBuffer code = new StringBuffer();
code
.append(_eol
+ _codeGenerator
.comment("PN Director's offset variable declarations."));
int width;
if (port.isInput()) {
width = port.getWidth();
} else {
width = 0;// port.getWidthInside();
}
if (width != 0) {
// Declare the buffer header struct.
List args = new LinkedList();
args.add(""); // buffer header name
args.add(""); // director header name
args.add(""); // capacity
args.add(""); // index
// FIXME: Do some filtering, only generate needed buffer.
for (int i = 0; i < width; i++) {
args.set(0, generatePortHeader(port, i));
args.set(1, generateDirectorHeader());
args.set(2, getBufferSize(port, i));
args.set(3, _buffers.size());
code.append(_codeStream.getCodeBlock("declareBufferHeader",
args));
// Record all the buffer instantiated.
_buffers.add(generatePortHeader(port, i));
}
}
return code.toString();
}
/**
* Generate the notTerminate flag variable for the associated PN director.
* Generating notTerminate instead of terminate saves the negation in
* checking the flag (e.g. "while (!terminate) ...").
*
* @return The varaible label of the notTerminate flag.
*/
protected String _generateNotTerminateFlag() {
return "true";// "director_controlBlock.controlBlock";
}
/**
* @param code The given code buffer.
* @exception IllegalActionException
*/
private void _generateThreadFunctionCode(StringBuffer code)
throws IllegalActionException {
List actorList = ((CompositeActor) _director.getContainer())
.deepEntityList();
// Generate the function for each actor thread.
for (Actor actor : (List<Actor>) actorList) {
StringBuffer functionCode = new StringBuffer();
CodeGeneratorHelper helper = (CodeGeneratorHelper) _getHelper((NamedObj) actor);
// This is necessary for non-inline mode. The actor fire
// function code generated after this, so we need to generate
// a declaration for the function.
if (_codeGenerator.inline.getToken() == BooleanToken.FALSE) {
code.append(_eol + "void " + generateName((NamedObj) actor)
+ "();");
}
code.append(_eol + "void* " + _getActorThreadLabel(actor)
+ "(void* arg) {" + _eol);
// Generate debug code for printing the thread ID and actor name.
List args = new LinkedList();
args.add(_generateActorNameFileForDebugging());
args.add(actor.getDisplayName());
code.append(_codeStream.getCodeBlock("printThreadName", args));
// mainLoop
// Check if the actor is an opague CompositeActor.
// The actor is guaranteed to be opague from calling
// deepEntityList(),
// so all we need to check whether or not it is a CompositeActor.
if (actor instanceof CompositeActor) {
Director directorHelper = (Director) _getHelper(actor
.getDirector());
// If so, it should contain a different Director.
assert directorHelper != this;
functionCode.append(directorHelper.generateMainLoop());
functionCode.append("$incrementReadBlockingThreads(&"
+ generateDirectorHeader() + ");" + _eol);
} else {
StringBuffer pnPostfireCode = new StringBuffer();
String loopCode = "while (true) {" + _eol;
// Generate a for loop if there is a firing count limit.
if (actor instanceof LimitedFiringSource) {
int firingCount = ((IntToken) ((LimitedFiringSource) actor).firingCountLimit
.getToken()).intValue();
if (firingCount != 0) {
loopCode = "int i = 0;" + _eol + "for (; i < "
+ firingCount + "; i++) {" + _eol;
}
pnPostfireCode.append(_eol);
}
functionCode.append(loopCode);
functionCode.append(helper.generateFireCode());
// FIXME: Doesn't look like the following comment is correct.
// If not inline, generateFireCode() would be a call
// to the fire function which already includes the
// type conversion code.
// if (inline) {
// functionCode.append(helper.generateTypeConvertFireCode());
// }
functionCode.append(helper.generatePostfireCode());
boolean forComposite = actor instanceof CompositeActor;
// Increment port offset.
for (IOPort port : (List<IOPort>) ((Entity) actor).portList()) {
// Determine the amount to increment.
int rate = 0;
try {
rate = DFUtilities.getRate(port);
} catch (NullPointerException ex) {
// int i = 0;
}
PortCodeGenerator portHelper = (PortCodeGenerator) _getHelper(port);
CodeGeneratorHelper portCGHelper = (CodeGeneratorHelper) portHelper;
if (portCGHelper.checkRemote(forComposite, port)) {
pnPostfireCode.append(portHelper
.updateConnectedPortsOffset(rate, _director));
}
if (port.isInput()) {
pnPostfireCode.append(portHelper.updateOffset(rate,
_director));
}
}
// Code for incrementing buffer offsets.
functionCode.append(pnPostfireCode.toString());
functionCode.append("}" + _eol);
functionCode.append("$incrementReadBlockingThreads(&"
+ generateDirectorHeader() + ");" + _eol);
}
// wrapup
functionCode.append(helper.generateWrapupCode());
functionCode.append("return NULL;" + _eol);
functionCode.append("}" + _eol);
// init
// This needs to be called last because all references
// need to be collected before generating their initialization.
String initializeCode = helper.generateInitializeCode();
String variableInitializeCode = helper
.generateVariableInitialization();
code.append(variableInitializeCode);
code.append(initializeCode);
code.append(functionCode);
}
}
private String _generateActorNameFileForDebugging() {
return CodeGeneratorHelper.generateName(_director) + "_nameMapFile";
}
/**
* Generate the thread function name for a given actor.
*
* @param actor The given actor.
* @return A unique label for the actor thread function.
*/
private String _getActorThreadLabel(Actor actor) {
return CodeGeneratorHelper.generateName((NamedObj) actor)
+ "_ThreadFunction";
}
private final HashSet<String> _buffers = new HashSet<String>();
}