package ptolemy.apps.apes; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import ptolemy.actor.Actor; import ptolemy.actor.CompositeActor; import ptolemy.actor.IOPort; import ptolemy.actor.NoRoomException; import ptolemy.actor.TimedDirector; import ptolemy.actor.util.BreakCausalityInterface; import ptolemy.actor.util.CausalityInterface; import ptolemy.actor.util.Time; import ptolemy.data.BooleanToken; import ptolemy.data.DoubleToken; import ptolemy.data.IntToken; import ptolemy.data.StringToken; import ptolemy.data.Token; import ptolemy.data.expr.Parameter; import ptolemy.data.expr.StringParameter; import ptolemy.data.type.BaseType; import ptolemy.kernel.CompositeEntity; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.NameDuplicationException; import ptolemy.kernel.util.NamedObj; import ptolemy.kernel.util.Workspace; /** * The CTask executes legacy C-Code in a separate thread. The thread is started * in the initialize method. The C-Code run in the thread executes and calls back * to the JAVA code via callbacks. These callbacks are either system calls or access * point callbacks (@see: AccessPointCallbackDispatcher). At some point, the * accessPointCallback method of this class is called and the thread is stalled in * there. In the fire, the thread is resumed. * In order to maximize concurrency, the thread executing the fire of this actor * and the thread created in this actor can run in parallel for a minimum delay time provided * in the accesspointCallback method. After this minimum delay, the thread executing the * executive director and thus the fire of this actor is blocked until the thread created * in this actor returns to the accessPointCallbackMethod. This is done by scheduling * a refiring of this actor after the minimum delay after resuming the thread. * @author Patricia Derler and Stefan Resmerita */ public class CTask extends ApeActor implements Runnable { public enum Type { BASIC_TASK, EXTENDED_TASK, IRS_1, IRS_2 } public CTask() throws IllegalActionException, NameDuplicationException { super(); _initialize(); } public CTask(Workspace workspace) throws IllegalActionException, NameDuplicationException { super(workspace); _initialize(); } public CTask(CompositeEntity container, String name) throws IllegalActionException, NameDuplicationException { super(container, name); _initialize(); } public CPUScheduler cpuScheduler; public EventManager eventManager; public static AccessPointCallbackDispatcher dispatcher; public Parameter methodName; public void accessPointCallback(double extime, double minNextTime) throws NoRoomException, IllegalActionException { if (!_actorStopped){ if (extime >= 0) { ResourceToken token = new ResourceToken(this, new Time(getDirector(), extime), null); _buffer = token; } synchronized (this) { _minDelay = new Time(getDirector(), minNextTime); _inExecution = false; this.notifyAll(); // wake up the DEDirector thread while (!_inExecution) { try { this.wait(); } catch (InterruptedException e) { if (!_actorStopped){ e.printStackTrace(); } } } } } } //return to the C part /** * Break the dependency between input and output ports such that there is no infinite loop. */ public CausalityInterface getCausalityInterface() throws IllegalActionException { if (_causalityInterface == null) { _causalityInterface = new BreakCausalityInterface(this, getDirector().defaultDependency()); } return _causalityInterface; } /** * Returns the priority of the actor. The priority is an int value. The * default return value is 0. * * @param actor * Given actor. * @return Priority of the given actor. */ public static int getPriority(Actor actor) { try { Parameter parameter = (Parameter) ((NamedObj) actor) .getAttribute("priority"); if (parameter != null) { IntToken token = (IntToken) parameter.getToken(); return token.intValue(); } else { return 0; } } catch (ClassCastException ex) { return 0; } catch (IllegalActionException ex) { return 0; } } private ArrayList<Object[]> _bufferOutputValue = new ArrayList(); public void setOutputValue(String varName, double value) throws NoRoomException, IllegalActionException { for (IOPort port : (List<IOPort>)outputPortList()) { if (port != output) { if (port.getName().equals(varName)) { _bufferOutputValue.add(new Object[]{port, new DoubleToken(value)}); } } } } private native void setGlobalVariable(String name, double value); public void setGlobalVariable(String varName) { setGlobalVariable(varName, _inputPortValues.get(varName)); } public boolean prefire() throws IllegalActionException { for (IOPort port : (List<IOPort>)inputPortList()) { if (port != input) { for (int i = 0; i < port.getWidth(); i++) { while (port.hasToken(i)) { Token token = port.get(i); double value = 0.0; if (token instanceof DoubleToken) { value = ((DoubleToken)token).doubleValue(); } else if (token instanceof IntToken) { value = ((IntToken)token).doubleValue(); } // TODO add other token types //_inputPortValues.put(port.getName(), value); setGlobalVariable(port.getName(), value); } } } } return super.prefire(); } private HashMap<String, Double> _inputPortValues; public void fire() throws IllegalActionException { boolean readInputs = false; while (input.hasToken(0)) { input.get(0); readInputs = true; } // consume all values on ports - if port values should be read and used, // this has to be done in the prefire or in the overriden fire before // calling this fire method for (IOPort port : (List<IOPort>)inputPortList()) { if (port != input) { for (int i = 0; i < port.getWidth(); i++) { while (port.hasToken(i)) { port.get(0); } } } } boolean continueExecution = true; while (continueExecution) { continueExecution = false; // fired as a result of the fireAt if (_waitForMinDelay && !readInputs) { synchronized (this) { while (_inExecution) { try { this.wait(); } catch (InterruptedException e) { if (_stopRequested){ break; } else{ e.printStackTrace(); } } } } if (_buffer != null) { output.send("CPUScheduler", _buffer); _buffer = null; } for (Object[] entry : bufferedTokens) { CTask task = (CTask) entry[0]; Token token = (Token) entry[1]; output.send(task, token); } bufferedTokens.clear(); for (Object[] entry : _bufferOutputValue) { IOPort port = (IOPort) entry[0]; Token token = (Token) entry[1]; for (int i = 0; i < port.getWidth(); i++) { port.send(i, token); } } _bufferOutputValue.clear(); for (ResourceToken token : bufferedResourceTokens) { output.send(token); } bufferedResourceTokens.clear(); _waitForMinDelay = false; } else if (readInputs) { // fired by the CPUScheduler synchronized (this) { if (_minDelay.getDoubleValue() > 0) { _waitForMinDelay = true; getDirector().fireAt(this, getDirector().getModelTime().add(_minDelay)); } else if (_minDelay.getDoubleValue() == 0) { _waitForMinDelay = true; continueExecution = true; } _inExecution = true; this.notifyAll(); } readInputs = false; } } } /** * resolve resourceActors and start the thread. */ public void preinitialize() throws IllegalActionException { super.preinitialize(); if (!(super.getDirector() instanceof TimedDirector)) { throw new IllegalActionException(this, "Enclosing director must be a TimedDirector."); } // just for testing purposes to do system calls CompositeActor compositeActor = (CompositeActor) getContainer(); List entities = compositeActor.entityList(); for (Iterator it = entities.iterator(); it.hasNext();) { Object entity = it.next(); if (entity instanceof Actor) { Actor actor = (Actor) entity; if (actor instanceof CPUScheduler) { cpuScheduler = (CPUScheduler) actor; } else if (actor instanceof EventManager) { eventManager = (EventManager) actor; } } } if (dispatcher == null) { dispatcher = new AccessPointCallbackDispatcher(); try { String libName = ((StringToken)((StringParameter)((NamedObj)getContainer()).getAttribute("CCodeLibrary")).getToken()).stringValue(); System.loadLibrary(libName); dispatcher.InitializeC(); // cpuScheduler.InitializeC(); // eventManager.InitializeC(); } catch (Exception ex) { ex.printStackTrace(); } } dispatcher.addTask(this); _waitForMinDelay = false; _thread = new Thread(this); _thread.start(); synchronized(this) { while (_inExecution) { try { this.wait(); } catch (Exception ex) { ex.printStackTrace(); } } } } public void initialize() throws IllegalActionException { super.initialize(); // TODO call startup function } public void run() { Thread.currentThread().setName(this.getName()); while (!_actorStopped) { _callCMethod(); } } public void wrapup() throws IllegalActionException { _actorStopped = true; _thread.interrupt(); _thread = null; } protected void _callCMethod() { CMethod(this.getName()); } private native void CMethod(String taskName); private void _initialize() throws IllegalActionException, NameDuplicationException { _inputPortValues = new HashMap(); Parameter sourceActorList= (Parameter) input.getAttribute("sourceActors"); sourceActorList.setExpression("*"); Parameter destinationActorList= (Parameter) output.getAttribute("destinationActors"); destinationActorList.setExpression("CPUScheduler"); // priority = new Parameter(this, "priority"); // priority.setExpression("0"); // priority.setTypeEquals(BaseType.INT); // // ID = new Parameter(this, "ID"); // ID.setExpression("0"); // ID.setTypeEquals(BaseType.INT); } // public Parameter priority; // public Parameter ID; /** * Buffers the token produced in the accessPointCallback if the DE Director thread running to avoid concurrent modification of the eventQueue in the DE Director. */ private ResourceToken _buffer; /** The causality interface, if it has been created. */ private CausalityInterface _causalityInterface; private Thread _thread; private Time _minDelay; private boolean _inExecution = true; private boolean _waitForMinDelay; private boolean _actorStopped = false; private ArrayList<Object[]> bufferedTokens = new ArrayList(); private ArrayList<ResourceToken> bufferedResourceTokens = new ArrayList(); public void bufferOutput(Actor task, BooleanToken token) { bufferedTokens.add(new Object[]{task, token}); } public void bufferOutput(ResourceToken resourceToken) { bufferedResourceTokens.add(resourceToken); } }