List actorList, CompositeActor container)
throws NotSchedulableException {
// A linked list containing all the actors that have no inputs.
LinkedList readyToScheduleActorList = new LinkedList();
Schedule newSchedule = new Schedule();
// An association between each actor and the number of firings
// for that actor that remain to be simulated.
// NOTE: This used to be a TreeMap using DFUtilities.NamedObjComparator().
// However, that comparator is very slow.
// Map firingsRemainingVector = new TreeMap(
// new DFUtilities.NamedObjComparator());
Map firingsRemainingVector = new HashMap();
// Initialized the firingsRemainingVector to the current
// firing vector.
firingsRemainingVector.putAll(_firingVector);
// A list of all that actors that we have not yet completely scheduled.
// FIXME: Is this list needed?
LinkedList unscheduledActorList = new LinkedList();
unscheduledActorList.addAll(actorList);
try {
// Initializing waitingTokens at all the input ports of actors and
// output ports of the model to zero is not necessary because
// SDFReceiver.clear() does it.
// The above statement seems incorrect to me. The only place where
// SDFReceiver.clear() is called is during initialization.
// For models that need to recompute the schedule during the
// execution, _waitingTokens is not cleared to zero, because
// initialize() is called only once at the beginning of the
// execution. Plus, in code generation, initialize() is never
// called. so I'm adding code to clear the _waitingTokes to zero
// here:
// --Gang Zhou
Iterator actorsIterator = actorList.iterator();
while (actorsIterator.hasNext()) {
Actor actor = (Actor) actorsIterator.next();
Iterator inputPorts = actor.inputPortList().iterator();
while (inputPorts.hasNext()) {
IOPort inputPort = (IOPort) inputPorts.next();
Receiver[][] receivers = inputPort.getReceivers();
if (receivers != null) {
for (int m = 0; m < receivers.length; m++) {
for (int n = 0; n < receivers[m].length; n++) {
((SDFReceiver) receivers[m][n])._waitingTokens = 0;
}
}
}
}
}
Iterator externalOutputPorts = container.outputPortList()
.iterator();
while (externalOutputPorts.hasNext()) {
IOPort outputPort = (IOPort) externalOutputPorts.next();
Receiver[][] receivers = outputPort.getInsideReceivers();
if (receivers != null) {
for (int m = 0; m < receivers.length; m++) {
for (int n = 0; n < receivers[m].length; n++) {
((SDFReceiver) receivers[m][n])._waitingTokens = 0;
}
}
}
}
// Simulate the creation of initialization tokens (delays).
// Fill readyToScheduleActorList with all the actors that have
// no unfulfilled input ports, and are thus ready to fire.
// This includes actors with no input ports and those
// whose input ports have consumption rates of zero.
Iterator actors = actorList.iterator();
while (actors.hasNext()) {
Actor actor = (Actor) actors.next();
int firingsRemaining = ((Integer) firingsRemainingVector
.get(actor)).intValue();
if (firingsRemaining == 0) {
unscheduledActorList.remove(actor);
continue;
}
int inputCount = _countUnfulfilledInputs(actor, actorList, true);
if (inputCount == 0) {
readyToScheduleActorList.addFirst(actor);
}
if (_debugging && VERBOSE) {
_debug("Actor " + ((ComponentEntity) actor).getName()
+ " has " + inputCount + " unfulfilledInputs.");
}
}
// Simulate production of initial tokens.
actors = actorList.iterator();
while (actors.hasNext()) {
Actor actor = (Actor) actors.next();
Iterator outputPorts = actor.outputPortList().iterator();
while (outputPorts.hasNext()) {
IOPort outputPort = (IOPort) outputPorts.next();
int count = DFUtilities.getTokenInitProduction(outputPort);
if (_debugging && VERBOSE) {
_debug("Simulating " + count
+ " initial tokens created on " + outputPort);
}
if (count > 0) {
_simulateTokensCreated(outputPort, count, actorList,
readyToScheduleActorList);
}
}
}
// Simulate a number of tokens initially present on each
// external input port.
for (Iterator inputPorts = container.inputPortList().iterator(); inputPorts
.hasNext();) {
IOPort port = (IOPort) inputPorts.next();
int count = ((Integer) externalRates.get(port)).intValue();
if (count > 0) {
_simulateExternalInputs(port, count, actorList,
readyToScheduleActorList);
}
}
// While we have actors left, pick one that is ready and fire it.
while (readyToScheduleActorList.size() > 0) {
if (_debugging && VERBOSE) {
_debug("Actors that can be scheduled:");
for (Iterator readyActors = readyToScheduleActorList
.iterator(); readyActors.hasNext();) {
Entity readyActor = (Entity) readyActors.next();
_debug(readyActor.getFullName());
}
_debug("Actors with firings left:");
for (Iterator remainingActors = unscheduledActorList
.iterator(); remainingActors.hasNext();) {
Entity remainingActor = (Entity) remainingActors.next();
_debug(remainingActor.getFullName());
}
}
// Pick an actor that is ready to fire.
Actor currentActor = (Actor) readyToScheduleActorList
.getFirst();
// Remove it from the list of actors we are waiting to fire.
while (readyToScheduleActorList.remove(currentActor)) {
}
// Determine the number of times currentActor can fire.
int numberOfFirings = _computeMaximumFirings(currentActor);
// We should never schedule something more than the number
// of times expected by the balance equations. This might
// happen because we assume an infinite number of tokens
// are waiting on external ports.
int firingsRemaining = ((Integer) firingsRemainingVector
.get(currentActor)).intValue();
if (numberOfFirings > firingsRemaining) {
numberOfFirings = firingsRemaining;
}
if (_debugging && VERBOSE) {
_debug("Scheduling actor " + currentActor.getName() + " "
+ numberOfFirings + " times.");
}
// Update the firingsRemainingVector for this actor.
firingsRemaining -= numberOfFirings;
firingsRemainingVector.put(currentActor, Integer
.valueOf(firingsRemaining));
if (_debugging && VERBOSE) {
_debug(currentActor.getName() + " should fire "
+ firingsRemaining + " more times.");
}
// Simulate the tokens that are consumed by the actors
// input ports.
_simulateInputConsumption(currentActor, numberOfFirings);
// Add it to the schedule numberOfFirings times.
Firing firing = new Firing();
firing.setActor(currentActor);
firing.setIterationCount(numberOfFirings);
newSchedule.add(firing);
// Get all its outputPorts
// and simulate the proper production of tokens.
for (Iterator outputPorts = (currentActor).outputPortList()
.iterator(); outputPorts.hasNext();) {
IOPort outputPort = (IOPort) outputPorts.next();
int count = DFUtilities.getTokenProductionRate(outputPort);
_simulateTokensCreated(outputPort, count * numberOfFirings,
unscheduledActorList, readyToScheduleActorList);
}
// Figure out what to do with the actor, now that it has been
// scheduled.
if (firingsRemaining < 0) {
// If we screwed up somewhere, and fired this more
// times than we thought we should have
// then throw an exception.
// This should never happen.
throw new InternalErrorException("Balance Equation "
+ "solution does not agree with "
+ "scheduling algorithm!");
}
if (firingsRemaining == 0) {
// If we've fired this actor all the
// times that it should, then
// we get rid of it entirely.
if (_debugging && VERBOSE) {
_debug("Actor = " + currentActor + " is done firing.");
}
// Remove the actor from the unscheduledActorList
// since we don't need to fire it any more.
while (unscheduledActorList.remove(currentActor)) {
;
}
if (_debugging && VERBOSE) {
_debug("Remaining actors:");
for (Iterator readyActors = readyToScheduleActorList
.iterator(); readyActors.hasNext();) {
Entity entity = (Entity) readyActors.next();
_debug(entity.getFullName());
}
}
} else {
// Otherwise the actor still has firings left.
// Count the number of unfulfilled inputs.
int inputCount = _countUnfulfilledInputs(currentActor,
unscheduledActorList, false);
// We've already removed currentActor from
// readyToSchedule actors, and presumably
// fired it until it can be fired no more.
// This check is here for robustness...
// if the actor can still be scheduled
// i.e. all its inputs are satisfied, and it
// appears in the unscheduled actors list
// then put it on the readyToScheduleActorList.
if ((inputCount <= 0)
&& unscheduledActorList.contains(currentActor)) {
readyToScheduleActorList.addFirst(currentActor);
}
}
}
} catch (IllegalActionException ex) {
// This could happen if we call getTokenConsumptionRate on a
// port that isn't a part of the actor. This probably means
// the graph is screwed up, or somebody else is mucking
// with it.
throw new InternalErrorException(this, ex,
"SDF Scheduler Failed internal consistency check.");
}
// If there are any actors left when we're done, then report the
// error.
if (unscheduledActorList.size() > 0) {
StringBuffer message = new StringBuffer(
"Actors remain that cannot be scheduled!\n"
+ "Unscheduled actors:\n");
// Only display the first 100 connected or disconnected actors.
int count = 0;
for (Iterator actors = unscheduledActorList.iterator(); actors
.hasNext()
&& count < 100; count++) {
Entity entity = (Entity) actors.next();
message.append(entity.getFullName() + " ");
}
if (count >= 99) {
message.append("...");
}
message.append("\nScheduled actors:\n");
List scheduledActorList = new LinkedList();
scheduledActorList.addAll(actorList);
scheduledActorList.removeAll(unscheduledActorList);
count = 0;
for (Iterator actors = scheduledActorList.iterator(); actors
.hasNext()
&& count < 100; count++) {
Entity entity = (Entity) actors.next();
message.append(entity.getFullName() + " ");
}
if (count >= 99) {
message.append("...");
}
throw new NotSchedulableException(message.toString());
}
if (_debugging) {
_debug("Schedule is:");
_debug(newSchedule.toString());
}
return newSchedule;
}