001/* Director for the dynamic dataflow model of computation.
002
003 Copyright (c) 2001-2015 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.domains.ddf.kernel;
029
030import java.util.Arrays;
031import java.util.HashMap;
032import java.util.HashSet;
033import java.util.Iterator;
034import java.util.LinkedList;
035import java.util.List;
036import java.util.Set;
037
038import ptolemy.actor.Actor;
039import ptolemy.actor.Director;
040import ptolemy.actor.FiringEvent;
041import ptolemy.actor.IOPort;
042import ptolemy.actor.NoTokenException;
043import ptolemy.actor.QueueReceiver;
044import ptolemy.actor.Receiver;
045import ptolemy.actor.TypedCompositeActor;
046import ptolemy.actor.parameters.ParameterPort;
047import ptolemy.actor.util.DFUtilities;
048import ptolemy.data.ArrayToken;
049import ptolemy.data.BooleanToken;
050import ptolemy.data.IntToken;
051import ptolemy.data.Token;
052import ptolemy.data.expr.Parameter;
053import ptolemy.data.expr.Variable;
054import ptolemy.data.type.BaseType;
055import ptolemy.kernel.ComponentPort;
056import ptolemy.kernel.CompositeEntity;
057import ptolemy.kernel.Entity;
058import ptolemy.kernel.Port;
059import ptolemy.kernel.util.Attribute;
060import ptolemy.kernel.util.IllegalActionException;
061import ptolemy.kernel.util.InternalErrorException;
062import ptolemy.kernel.util.NameDuplicationException;
063import ptolemy.kernel.util.NamedObj;
064import ptolemy.kernel.util.Workspace;
065
066///////////////////////////////////////////////////////////////////
067//// DDFDirector
068
069/**
070 The dynamic dataflow (DDF) domain is a superset of the synchronous
071 dataflow(SDF) and Boolean dataflow(BDF) domains. In the SDF domain,
072 an actor consumes and produces a fixed number of tokens per firing.
073 This static information makes possible compile-time scheduling. In the
074 DDF domain, there are few constraints on the production and consumption
075 behavior of actors, and the schedulers make no attempt to construct a
076 compile-time schedule. Instead, each actor has a set of firing rules
077 (patterns) and can be fired if one of them is satisfied, i.e., one
078 particular firing pattern forms a prefix of sequences of unconsumed
079 tokens at input ports. The canonical actors in the DDF domain include
080 Select and Switch, which consume or produce tokens on different channels
081 based on the token received from the control port. You can also use
082 the two-channel versions BooleanSelect and BooleanSwitch.
083 <p>
084 The dynamic scheduler implemented in this director fires all enabled
085 and non-deferrable actors once in a basic iteration. A deferrable
086 actor is one that will not help one of the downstream actors become
087 enabled because that downstream actor either already has enough tokens on
088 the channel connecting those two actors or is waiting for tokens on
089 another channel. If no actor fires so far, which means there is no
090 enabled and non-deferrable actor, then among all enabled and deferrable
091 actors, this director fires those which have the smallest maximum number
092 of tokens on their output channels which satisfy the demand of destination
093 actors. If still no actor fires, then there is no enabled actor. A user
094 can treat several such basic iterations as a single iteration by adding
095 a parameter with name <i>requiredFiringsPerIteration</i> to an actor
096 (which is often a sink actor or an actor directly connected to output port
097 of the composite actor) and specifying the number of times this actor must
098 be fired in a single iteration. If the value of the parameter
099 <i>runUntilDeadlockInOneIteration</i> is a BooleanToken with value true,
100 one single iteration consists of repeating the basic iteration until
101 deadlock is reached (thus overriding the previous definition of one
102 iteration), which is the status of the model where all active
103 actors under the control of this director are unable to fire because
104 their firing rules are not satisfied. However, they may be able to fire
105 again during next iteration when tokens are transferred in from an outside
106 domain. Note <i>runUntilDeadlockInOneIteration</i> can be set to true
107 only when this director is not on the top level.
108 <p>
109 The algorithm implementing one basic iteration goes like this:
110 <pre>
111 E = set of enabled actors
112 D = set of deferrable enabled actors
113 </pre>
114 One basic(default) iteration consists of:
115 <pre>
116 if (E\D != empty set) {
117 fire (E\D)
118 } else if (D != empty set) {
119 fire minimax(D)
120 } else {
121 declare deadlock
122 }
123 </pre>
124 The function "minimax(D)" returns a subset of D with the smallest
125 maximum number of tokens on their output channels which satisfy the
126 demand of destination actors.
127 <p>
128 Note that any SDF model can be run with a DDF Director. However, the
129 notion of iteration is different. One could try to imitate the SDF
130 iteration in the DDF domain by controlling the number of firings in one
131 iteration for some actors, such as requiring a plotter to plot a fixed
132 number of points in each iteration.
133 <p>
134 In the DDF domain, the firing rule of any actor is specified by the token
135 consumption rates of its input ports. A general DDF actor could change
136 the consumption rates of its input ports after each firing of this actor.
137 For multiports, an array token could be used to specify different rates
138 for different channels connected to the same multiport. Note that in SDF,
139 all channels connected to the same multiport have the same rate.
140 <p>
141 Based on DDFSimpleSched in Ptolemy Classic, by Edward Lee.
142 See E. A. Lee et al., "The Almagest," documentation for Ptolemy Classic,
143 Vol. 1, Chapter 7, 1997.
144
145 @author Gang Zhou
146 @version $Id$
147 @since Ptolemy II 4.1
148 @Pt.ProposedRating Yellow (zgang)
149 @Pt.AcceptedRating Yellow (cxh)
150 */
151public class DDFDirector extends Director {
152    /** Construct a director in the default workspace with an empty string
153     *  as its name. The director is added to the list of objects in
154     *  the workspace. Increment the version number of the workspace.
155     *  @exception IllegalActionException If the name has a period in it, or
156     *   the director is not compatible with the specified container.
157     *  @exception NameDuplicationException If the container already contains
158     *   an entity with the specified name.
159     */
160    public DDFDirector()
161            throws IllegalActionException, NameDuplicationException {
162        super();
163        _init();
164    }
165
166    /** Construct a director in the  workspace with an empty name.
167     *  The director is added to the list of objects in the workspace.
168     *  Increment the version number of the workspace.
169     *  @param workspace The workspace of this object.
170     *  @exception IllegalActionException If the name has a period in it, or
171     *   the director is not compatible with the specified container.
172     *  @exception NameDuplicationException If the container already contains
173     *   an entity with the specified name.
174     */
175    public DDFDirector(Workspace workspace)
176            throws IllegalActionException, NameDuplicationException {
177        super(workspace);
178        _init();
179    }
180
181    /** Construct a director in the given container with the given name.
182     *  The container argument must not be null, or a NullPointerException
183     *  will be thrown.
184     *  If the name argument is null, then the name is set to the
185     *  empty string. Increment the version number of the workspace.
186     *  @param container The container of this director.
187     *  @param name Name of this director.
188     *  @exception IllegalActionException Not thrown in this base class.
189     *   May be thrown in the derived classes if the director
190     *   is not compatible with the specified container.
191     *  @exception NameDuplicationException If the name collides with
192     *   an attribute that already exists in the given container.
193     */
194    public DDFDirector(CompositeEntity container, String name)
195            throws IllegalActionException, NameDuplicationException {
196        super(container, name);
197        _init();
198    }
199
200    ///////////////////////////////////////////////////////////////////
201    ////                         parameters                        ////
202
203    /** A Parameter representing the number of times that postfire() may
204     *  be called before it returns false.  If the value is less than or
205     *  equal to zero, then the execution will never return false in
206     *  postfire(), and thus the execution can continue forever or until
207     *  the model is deadlocked.
208     *  The default value is an IntToken with the value zero.
209     */
210    public Parameter iterations;
211
212    /** A Parameter representing the maximum capacity of each receiver
213     *  controlled by this director. This is an integer that defaults to 0,
214     *  which means the queue in each receiver is unbounded. To specify
215     *  bounded queues, set this to a positive integer.
216     */
217    public Parameter maximumReceiverCapacity;
218
219    /** A parameter indicating whether one iteration consists of
220     *  repeated basic iterations until deadlock. If this parameter is
221     *  true, the model will be executed until deadlock in one iteration.
222     *  The default value is a BooleanToken with the value false. It
223     *  cannot be set to true if this director is at the top level.
224     */
225    public Parameter runUntilDeadlockInOneIteration;
226
227    ///////////////////////////////////////////////////////////////////
228    ////                         public methods                    ////
229
230    /** If the attribute being changed is <i>runUntilDeadlockInOneIteration</i>
231     *  and it is set to be true, then verify this director is not at the
232     *  top level.
233     *  @param attribute The changed parameter.
234     *  @exception IllegalActionException If this director is at top
235     *   level and <i>runUntilDeadlockInOneIteration</i> is set to be true,
236     *   or getToken() throws IllegalActionException.
237     */
238    @Override
239    public void attributeChanged(Attribute attribute)
240            throws IllegalActionException {
241        if (attribute == runUntilDeadlockInOneIteration) {
242            _runUntilDeadlock = ((BooleanToken) runUntilDeadlockInOneIteration
243                    .getToken()).booleanValue();
244
245            if (_runUntilDeadlock && _isTopLevel()) {
246                // The reason we don't allow this is because we cannot
247                // pause the model easily if the whole execution is in
248                // one iteration. And the same effect can be achieved
249                // by setting the parameter iterations to zero anyway.
250                throw new IllegalActionException(this,
251                        "Cannot set runUntilDeadlockInOneIteration to be "
252                                + "true if this DDFDirector is at top level. "
253                                + "Instead you should set the parameter iterations "
254                                + "to be zero to achieve the same effect.");
255            }
256        } else {
257            super.attributeChanged(attribute);
258        }
259    }
260
261    /** Clone the director into the specified workspace. This calls the
262     *  base class and then sets the attribute public members to refer
263     *  to the attributes of the new director.
264     *  @param workspace The workspace for the new director.
265     *  @return A new director.
266     *  @exception CloneNotSupportedException If a derived class contains
267     *  an attribute that cannot be cloned.
268     */
269    @Override
270    public Object clone(Workspace workspace) throws CloneNotSupportedException {
271        DDFDirector newObject = (DDFDirector) super.clone(workspace);
272        newObject._actorsInfo = new HashMap();
273        newObject._actorsToCheckNumberOfFirings = new LinkedList();
274        newObject._disabledActors = new HashSet();
275        return newObject;
276    }
277
278    /** Set the flag indicating whether type resolution is disabled or not.
279     *  This method is used in an ActorRecursion actor. When a composite
280     *  actor is cloned into an ActorRecursion actor, type compatibility
281     *  has already been checked, therefore there is no need to invalidate
282     *  resolved types.
283     *  @param flag The flag to be set.
284     */
285    public void disableTypeResolution(boolean flag) {
286        _isTypeResolutionDisabled = flag;
287    }
288
289    /** Execute the model for one iteration. First scan all active actors
290     *  to put all enabled and non-deferrable actors in a list and find the
291     *  minimax actors. Fire all actors once in the list. If no actor has been
292     *  fired, fire the minimax actors. If still no actor has been fired,
293     *  a deadlock has been detected. This concludes one basic iteration,
294     *  and by default also one iteration of this director. However,
295     *  if some actor has a parameter named <i>requiredFiringsPerIteration</i>
296     *  defined, continue to execute basic iterations until the actor has
297     *  been fired at least the number of times given in that parameter. If
298     *  more than one actor has such a parameter, then the iteration will
299     *  continue until all are satisfied. If the parameter
300     *  <i>runUntilDeadlockInOneIteration</i> has value true, one iteration
301     *  consists of repeatedly executing basic iterations until the actors
302     *  under control of this director have reached a deadlock.
303     *  @exception IllegalActionException If any actor executed by this
304     *   actor returns false in prefire().
305     */
306    @Override
307    public void fire() throws IllegalActionException {
308        boolean repeatBasicIteration = false;
309        if (_debugging) {
310            _debug("DDFDirector.fire()");
311        }
312        do {
313            // The List to store actors that are enabled and not deferrable.
314            List toBeFiredActors = new LinkedList();
315
316            // The list to store minimax actors.
317            List minimaxActors = new LinkedList();
318            int minimaxSize = Integer.MAX_VALUE;
319
320            Iterator actors = ((TypedCompositeActor) getContainer())
321                    .deepEntityList().iterator();
322
323            while (actors.hasNext()) {
324                // Scan all actors to find all enabled and not
325                // deferrable actors.
326                Actor actor = (Actor) actors.next();
327
328                if (_disabledActors.contains(actor)) {
329                    continue;
330                }
331
332                ActorInfo actorInfo = (ActorInfo) _actorsInfo.get(actor);
333                ActorEnablingStatus status = actorInfo.status;
334
335                if (status == ActorEnablingStatus.ENABLED_NOT_DEFERRABLE) {
336                    toBeFiredActors.add(actor);
337                }
338
339                // Find set of minimax actors.
340                if (status == ActorEnablingStatus.ENABLED_DEFERRABLE) {
341                    int newSize = actorInfo.maximumNumberOfTokens;
342
343                    if (newSize < minimaxSize) {
344                        minimaxActors.clear();
345                        minimaxActors.add(actor);
346                        minimaxSize = newSize;
347                    } else if (newSize == minimaxSize) {
348                        minimaxActors.add(actor);
349                    }
350                }
351            }
352
353            // No actor has been fired at the beginning of the
354            // basic iteration.
355            _firedOne = false;
356
357            // Fire all enabled and not deferrable actors.
358            Iterator enabledActors = toBeFiredActors.iterator();
359
360            while (enabledActors.hasNext()) {
361                Actor actor = (Actor) enabledActors.next();
362                boolean isActorFired = _fireActor(actor);
363                _firedOne = isActorFired || _firedOne;
364            }
365
366            // If no actor has been fired, fire the set of minimax actors.
367            if (!_firedOne) {
368                Iterator minimaxActorsIterator = minimaxActors.iterator();
369
370                while (minimaxActorsIterator.hasNext()) {
371                    Actor minimaxActor = (Actor) minimaxActorsIterator.next();
372                    boolean isActorFired = _fireActor(minimaxActor);
373                    _firedOne = isActorFired || _firedOne;
374                }
375            }
376
377            if (_runUntilDeadlock) {
378                // Repeat basic iteration if at lease one actor
379                // has been fired.
380                repeatBasicIteration = _firedOne;
381            } else if (_firedOne) {
382                // Check to see if we need to repeat basic iteration to
383                // satisfy requiredFiringsPerIteration for some actors.
384                actors = _actorsToCheckNumberOfFirings.iterator();
385
386                repeatBasicIteration = false;
387
388                while (actors.hasNext()) {
389                    Actor actor = (Actor) actors.next();
390
391                    // If the actor has been deleted from the topology,
392                    // there is no need to check.
393                    if (actor.getContainer() == null) {
394                        actors.remove();
395                        continue;
396                    }
397
398                    ActorInfo actorInfo = (ActorInfo) _actorsInfo.get(actor);
399                    int requiredFirings = actorInfo.requiredFiringsPerIteration;
400                    int firingsDone = actorInfo.numberOfFirings;
401
402                    if (firingsDone < requiredFirings) {
403                        repeatBasicIteration = true;
404                        break;
405                    }
406                }
407            } else {
408                // If no actor has been fired, declare deadlock
409                if (_debugging) {
410                    _debug("deadlock detected");
411                }
412
413                repeatBasicIteration = false;
414            }
415        } while (repeatBasicIteration && !_stopRequested);
416    }
417
418    /** Initialize the model controlled by this director. Initialize the
419     *  actors associated with this director. Set all the state variables
420     *  to the their initial values.  The order in which the actors are
421     *  initialized is arbitrary. If actors connected directly to output
422     *  ports produce initial tokens, then send those tokens to the outside
423     *  of the composite actor.
424     *  @exception IllegalActionException If the initialize() method of
425     *   one of the associated actors throws it.
426     */
427    @Override
428    public void initialize() throws IllegalActionException {
429        _iterationCount = 0;
430        _runUntilDeadlock = ((BooleanToken) runUntilDeadlockInOneIteration
431                .getToken()).booleanValue();
432        _actorsToCheckNumberOfFirings.clear();
433        _disabledActors.clear();
434
435        super.initialize();
436
437        Iterator outputPorts = ((Actor) getContainer()).outputPortList()
438                .iterator();
439
440        while (outputPorts.hasNext()) {
441            IOPort outputPort = (IOPort) outputPorts.next();
442
443            for (int i = 0; i < outputPort.getWidthInside(); i++) {
444                while (outputPort.hasNewTokenInside(i)) {
445                    Token token = outputPort.getInside(i);
446
447                    if (_debugging) {
448                        _debug("transferring initial tokens from "
449                                + outputPort.getFullName());
450                    }
451
452                    outputPort.send(i, token);
453                }
454            }
455        }
456        if (_debugging) {
457            _debug("DDFDirector.initialize() finished.");
458        }
459    }
460
461    /** Initialize the given actor. This method is called by the
462     *  initialize() method of the director, and by the manager whenever
463     *  an actor is added to the executing model as a mutation. It first
464     *  calls the actor's initialize() method which may emit initial tokens.
465     *  Then it updates the enabling status of the actor and all actors
466     *  connected to this actor. Finally it records the value given by
467     *  <i>requiredFiringsPerIteration</i> if the actor has such a parameter.
468     *  Any change to this parameter during execution will be ignored.
469     *  @param actor The actor to be initialized.
470     *  @exception IllegalActionException If the
471     *   <i>requiredFiringsPerIteration</i> parameter does not contain
472     *   an IntToken.
473     */
474    @Override
475    public void initialize(Actor actor) throws IllegalActionException {
476        super.initialize(actor);
477
478        // Since an actor may produce initial tokens during initialization,
479        // the enabling status of those directly connected actors as well
480        // as itself must be updated.
481        _updateConnectedActorsStatus(actor);
482
483        // Determine requiredFiringsPerIteration for this actor.
484        // The default value 0 means no requirement on this actor.
485        ActorInfo actorInfo = (ActorInfo) _actorsInfo.get(actor);
486        actorInfo.requiredFiringsPerIteration = 0;
487
488        Variable requiredFiringsPerIteration = (Variable) ((Entity) actor)
489                .getAttribute("requiredFiringsPerIteration");
490
491        if (requiredFiringsPerIteration != null) {
492            Token token = requiredFiringsPerIteration.getToken();
493
494            if (token instanceof IntToken) {
495                int value = ((IntToken) token).intValue();
496
497                if (value > 0) {
498                    actorInfo.requiredFiringsPerIteration = value;
499                }
500
501                _actorsToCheckNumberOfFirings.add(actor);
502            } else {
503                throw new IllegalActionException(this, actor,
504                        "The variable "
505                                + "requiredFiringsPerIteration must contain "
506                                + "an IntToken.");
507            }
508        }
509    }
510
511    /** Call base class method to invalidate resolved types if the flag to
512     *  disable type resolution is set to false. If the flag is true,
513     *  override the base class method to skip invalidating resolved types.
514     *  This method is used for an ActorRecursion actor. When a composite
515     *  actor is cloned into an ActorRecursion actor, type compatibility
516     *  has already been checked, therefore there is no need to invalidate
517     *  resolved types.
518     */
519    @Override
520    public void invalidateResolvedTypes() {
521        if (!_isTypeResolutionDisabled) {
522            super.invalidateResolvedTypes();
523        }
524    }
525
526    /** Merge an opaque composite actor controlled by an inside DDFDirector
527     *  with the outside domain controlled by this director. It aggregates
528     *  the status variables for the inside actors with the status variables
529     *  for the outside actors. This method can be used in ActorRecursion
530     *  which clones a composite actor into itself and then merges with the
531     *  outside DDF domain.
532     *  @param insideDirector The inside DDFDirector to be merged.
533     */
534    public void merge(DDFDirector insideDirector) {
535        _disabledActors.addAll(insideDirector._disabledActors);
536        _actorsToCheckNumberOfFirings
537                .addAll(insideDirector._actorsToCheckNumberOfFirings);
538        _actorsInfo.putAll(insideDirector._actorsInfo);
539    }
540
541    /** Return a new QueueReceiver. Set the capacity of the FIFO queue
542     *  in the receiver to the value specified by the director parameter
543     *  <i>maximumReceiverCapacity</i> if that value is greater than 0.
544     *  @return A new QueueReceiver.
545     */
546    @Override
547    public Receiver newReceiver() {
548        QueueReceiver receiver = new QueueReceiver();
549
550        try {
551            int capacity = ((IntToken) maximumReceiverCapacity.getToken())
552                    .intValue();
553
554            if (capacity > 0) {
555                receiver.setCapacity(capacity);
556            }
557        } catch (IllegalActionException e) {
558            throw new InternalErrorException(e);
559        }
560
561        return receiver;
562    }
563
564    /** Increment the number of iterations. Return false if the system
565     *  has finished executing by reaching the iteration limit or the system
566     *  is deadlocked.
567     *  @return True if the Director wants to be fired again in the future.
568     *  @exception IllegalActionException If the <i>iterations</i> parameter
569     *   does not contain a legal value.
570     */
571    @Override
572    public boolean postfire() throws IllegalActionException {
573        int iterationsValue = ((IntToken) iterations.getToken()).intValue();
574        _iterationCount++;
575
576        if (iterationsValue > 0 && _iterationCount >= iterationsValue) {
577            if (_debugging) {
578                _debug("iteration limit reached");
579            }
580
581            return false;
582        }
583
584        // The DDF domain is deadlocked if no actor is fired in the last
585        // basic iteration. However, if the DDF domain is embedded inside
586        // another domain, then we have to check whether transferring
587        // more tokens into the DDF domain will break the deadlock.
588        boolean isDeadlocked = !_firedOne;
589
590        if (isDeadlocked && isEmbedded()) {
591            Iterator inputPorts = ((Actor) getContainer()).inputPortList()
592                    .iterator();
593
594            while (inputPorts.hasNext()) {
595                IOPort inputPort = (IOPort) inputPorts.next();
596                Receiver[][] deepReceivers = inputPort.deepGetReceivers();
597                foundNotSatisfiedReceiver: for (int i = 0; i < deepReceivers.length; i++) {
598                    for (int j = 0; j < deepReceivers[i].length; j++) {
599                        QueueReceiver deepReceiver = (QueueReceiver) deepReceivers[i][j];
600                        IOPort port = deepReceiver.getContainer();
601
602                        // We don't consider the weird case where the input
603                        // port is directly connected to an output port
604                        // of the container.
605                        if (port.getContainer() != getContainer()) {
606                            int tokenConsumptionRate = _getTokenConsumptionRate(
607                                    deepReceiver);
608
609                            if (deepReceiver.size() < tokenConsumptionRate) {
610                                isDeadlocked = false;
611                                break foundNotSatisfiedReceiver;
612                            }
613                        }
614                    }
615                }
616            }
617        }
618
619        return super.postfire() && !isDeadlocked;
620    }
621
622    /** Check the input ports of the container composite actor (if there
623     *  are any) to see whether they have enough tokens, and return true
624     *  if they do. If there are no input ports, then also return true.
625     *  Otherwise, return false. If an input port does not have a parameter
626     *  <i>tokenConsumptionRate</i>, then skip checking on that port because
627     *  it will transfer all tokens (if there are any) to the inside. Note
628     *  the difference from SDF domain where the default rate is 1. Finally,
629     *  initialize numberOfFirings to zero for those actors for which positive
630     *  requiredFiringsPerIteration has been defined.
631     *  @return true If all of the input ports of the container of this
632     *   director have enough tokens.
633     *  @exception IllegalActionException If any called method throws
634     *   IllegalActionException.
635     */
636    @Override
637    public boolean prefire() throws IllegalActionException {
638        if (_debugging) {
639            _debug("DDFDirector.prefire()\niterationCount " + _iterationCount);
640        }
641
642        super.prefire();
643
644        Actor container = (Actor) getContainer();
645        Iterator inputPorts = container.inputPortList().iterator();
646
647        while (inputPorts.hasNext()) {
648            IOPort inputPort = (IOPort) inputPorts.next();
649
650            // NOTE: If the port is a ParameterPort, then we should not
651            // insist on there being an input.
652            if (inputPort instanceof ParameterPort) {
653                continue;
654            }
655
656            int[] rate = _getTokenConsumptionRate(inputPort);
657
658            for (int i = 0; i < inputPort.getWidth(); i++) {
659                if (rate[i] >= 0 && !inputPort.hasToken(i, rate[i])) {
660                    if (_debugging) {
661                        _debug("Channel " + i + " of port "
662                                + inputPort.getFullName()
663                                + " does not have enough tokens: " + rate[i]
664                                + ". Prefire returns false.");
665                    }
666                    if (_debugging) {
667                        _debug("DDFDirector.prefire() returns false.");
668                    }
669                    return false;
670                }
671            }
672        }
673
674        Iterator actors = _actorsToCheckNumberOfFirings.iterator();
675
676        while (actors.hasNext()) {
677            Actor actor = (Actor) actors.next();
678            ActorInfo actorInfo = (ActorInfo) _actorsInfo.get(actor);
679            actorInfo.numberOfFirings = 0;
680        }
681        if (_debugging) {
682            _debug("DDFDirector.prefire() returns true.");
683        }
684        return true;
685    }
686
687    /** Return an array of suggested directors to use with an embedded
688     *  ModalModel. Each director is specified by its full class
689     *  name.  The first director in the array will be the default
690     *  director used by a modal model.
691     *  @return An array of suggested directors to be used with ModalModel.
692     *  @see ptolemy.actor.Director#suggestedModalModelDirectors()
693     */
694    @Override
695    public String[] suggestedModalModelDirectors() {
696        String[] defaultSuggestions = {
697                "ptolemy.domains.modal.kernel.MultirateFSMDirector",
698                "ptolemy.domains.hdf.kernel.HDFFSMDirector",
699                "ptolemy.domains.modal.kernel.FSMDirector",
700                "ptolemy.domains.modal.kernel.NonStrictFSMDirector" };
701        return defaultSuggestions;
702    }
703
704    /** Return true to indicate that a ModalModel under control
705     *  of this director supports multirate firing.
706     *  @return True indicating a ModalModel under control of this director
707     *  supports multirate firing.
708     */
709    @Override
710    public boolean supportMultirateFiring() {
711        return true;
712    }
713
714    /** Override the base class method to transfer enough tokens to complete
715     *  an internal iteration. If the token consumption rate is defined for
716     *  the port and there are not enough tokens, throw an exception. If the
717     *  token consumption rate is not defined for the port, transfer all tokens
718     *  (if there are any) contained by the port. Finally it updates enabling
719     *  status for all inside opaque actors that receive data from this port.
720     *  @param port The port to transfer tokens from.
721     *  @return True if data are transferred.
722     *  @exception IllegalActionException If the port is not an opaque
723     *  input port, or if there are not enough input tokens available.
724     */
725    @Override
726    public boolean transferInputs(IOPort port) throws IllegalActionException {
727        if (_debugging) {
728            _debug("Calling transferInputs on port: " + port.getFullName());
729        }
730
731        if (!port.isInput() || !port.isOpaque()) {
732            throw new IllegalActionException(this, port,
733                    "Attempted to transferInputs on a port is not an "
734                            + "opaque input port.");
735        }
736
737        boolean wasTransferred = false;
738
739        int[] rate = _getTokenConsumptionRate(port);
740
741        for (int i = 0; i < port.getWidth(); i++) {
742            try {
743                // If the parameter tokenConsumptionRate is defined,
744                // _getTokenConsumptionRate(port) returns an array
745                // of non-negative int.
746                if (rate[i] >= 0) {
747                    for (int k = 0; k < rate[i]; k++) {
748                        if (port.hasToken(i)) {
749                            Token t = port.get(i);
750
751                            if (_debugging) {
752                                _debug(getName(),
753                                        "transferring input from channel " + i
754                                                + " of input port "
755                                                + port.getName());
756                            }
757
758                            port.sendInside(i, t);
759                            wasTransferred = true;
760                        } else {
761                            throw new IllegalActionException(this, port,
762                                    "Channel " + i + "should consume " + rate[i]
763                                            + " tokens, but there were only "
764                                            + k
765                                            + " tokens available. Maybe the rate"
766                                            + " is set wrong?");
767                        }
768                    }
769
770                    // If the parameter tokenConsumptionRate is not defined,
771                    // _getTokeConsumptionRate(port) returns an array of int
772                    // each with vaule -1.
773                } else {
774                    // If no rate was specified, then we transfer at most
775                    // one token.
776                    if (port.hasToken(i)) {
777                        Token token = port.get(i);
778
779                        if (_debugging) {
780                            _debug(getName(), "transferring input from channel "
781                                    + i + " of port " + port.getName());
782                        }
783
784                        port.sendInside(i, token);
785                        wasTransferred = true;
786                    }
787                }
788            } catch (NoTokenException ex) {
789                // this shouldn't happen.
790                throw new InternalErrorException(this, ex, null);
791            }
792        }
793
794        // Update enabling status for all inside opaque actors that receive
795        // data from this port.
796        Iterator insideSinkPorts = port.insideSinkPortList().iterator();
797
798        while (insideSinkPorts.hasNext()) {
799            IOPort insideSinkPort = (IOPort) insideSinkPorts.next();
800            Actor actor = (Actor) insideSinkPort.getContainer();
801
802            // Skip it if the actor to be checked contains this director.
803            // In other words, the data directly go to output port instead
804            // of any inside actors.
805            if (getContainer() != actor) {
806                ActorInfo actorInfo = (ActorInfo) _actorsInfo.get(actor);
807                if (actorInfo == null) {
808                    actorInfo = new ActorInfo();
809                    _actorsInfo.put(actor, actorInfo);
810                }
811                actorInfo.status = _getActorStatus(actor);
812            }
813        }
814
815        return wasTransferred;
816    }
817
818    /** Override the base class method to transfer enough tokens to
819     *  fulfill the output production rate. If the token production rate
820     *  is defined for the port and there are not enough tokens, throw an
821     *  exception. If the token production rate is not defined for the port,
822     *  transfer all tokens (if there are any) contained on the inside by
823     *  the port.
824     *  @param port The port to transfer tokens from.
825     *  @return True if data are transferred.
826     *  @exception IllegalActionException If the port is not an opaque
827     *  output port, or if there are not enough output tokens available.
828     */
829    @Override
830    public boolean transferOutputs(IOPort port) throws IllegalActionException {
831        if (_debugging) {
832            _debug("Calling transferOutputs on port: " + port.getFullName());
833        }
834
835        if (!port.isOutput() || !port.isOpaque()) {
836            throw new IllegalActionException(this, port,
837                    "Attempted to transferOutputs on a port that "
838                            + "is not an opaque output port.");
839        }
840
841        boolean wasTransferred = false;
842
843        int[] rate = _getTokenProductionRate(port);
844
845        for (int i = 0; i < port.getWidthInside(); i++) {
846            try {
847                // If the parameter tokenProductionRate is defined,
848                // _getTokenProductionRate(port) returns an array
849                // of non-negative int.
850                if (rate[i] >= 0) {
851                    for (int k = 0; k < rate[i]; k++) {
852                        if (port.hasTokenInside(i)) {
853                            Token token = port.getInside(i);
854
855                            if (_debugging) {
856                                _debug(getName(),
857                                        "transferring output from channel " + i
858                                                + " of port " + port.getName());
859                            }
860
861                            port.send(i, token);
862                            wasTransferred = true;
863                        } else {
864                            throw new IllegalActionException(this, port,
865                                    "Channel " + i + " should produce "
866                                            + rate[i]
867                                            + " tokens, but there were only "
868                                            + k
869                                            + " tokens available. Maybe the rate"
870                                            + " is set wrong?");
871                        }
872                    }
873
874                    // If the parameter tokenProductionRate is not defined,
875                    // _getTokenProductionRate(port) returns an array of int
876                    // each with value -1.
877                } else {
878                    while (port.hasNewTokenInside(i)) {
879                        Token token = port.getInside(i);
880
881                        if (_debugging) {
882                            _debug(getName(),
883                                    "transferring output from channel " + i
884                                            + " of port " + port.getName());
885                        }
886
887                        port.send(i, token);
888                        wasTransferred = true;
889                    }
890                }
891            } catch (NoTokenException ex) {
892                // this shouldn't happen.
893                throw new InternalErrorException(this, ex, null);
894            }
895        }
896
897        return wasTransferred;
898    }
899
900    ///////////////////////////////////////////////////////////////////
901    ////                         protected methods                 ////
902
903    /** Iterate the actor once. Increment the firing number for it.
904     *  Update the enabling status for each connected actor as well
905     *  as itself.
906     *  @param actor The actor to be fired.
907     *  @return true if the actor is actually fired, false if not.
908     *  @exception IllegalActionException If any called method throws
909     *   IllegalActionException or the actor is not ready.
910     */
911    protected boolean _fireActor(Actor actor) throws IllegalActionException {
912        if (_debugging) {
913            _debug(new FiringEvent(this, actor, FiringEvent.BEFORE_ITERATE));
914        }
915
916        // Iterate once.
917        int returnValue = actor.iterate(1);
918
919        if (_debugging) {
920            _debug(new FiringEvent(this, actor, FiringEvent.AFTER_ITERATE));
921        }
922
923        _updateConnectedActorsStatus(actor);
924
925        if (returnValue == STOP_ITERATING) {
926            if (_debugging) {
927                _debug("Actor " + ((NamedObj) actor).getFullName()
928                        + " is disabled.");
929            }
930
931            _disabledActors.add(actor);
932            _actorsToCheckNumberOfFirings.remove(actor);
933        }
934
935        // If the returnValue is NOT_READY, this method returns false.
936        // Because the token consumption rates of input ports provide only
937        // a guideline for firing instead of a contract, we allow an enabled
938        // (as determined by the director) actor to return false in its
939        // prefire().
940        boolean fired = false;
941
942        if (returnValue != NOT_READY) {
943            // At least one actor has been fired in this basic iteration.
944            fired = true;
945
946            // Increment the firing number.
947            if (_actorsToCheckNumberOfFirings.contains(actor)) {
948                ActorInfo actorInfo = (ActorInfo) _actorsInfo.get(actor);
949                actorInfo.numberOfFirings++;
950            }
951        }
952
953        return fired;
954    }
955
956    /** Determine actor enabling status. It must be one of the three:
957     *  NOT_ENABLED, ENABLED_DEFERRABLE, ENABLED_NOT_DEFERRABLE.
958     *  @param actor The actor to be checked.
959     *  @return An int indicating actor enabling status.
960     *  @exception IllegalActionException If any called method throws
961     *   IllegalActionException.
962     */
963    protected ActorEnablingStatus _getActorStatus(Actor actor)
964            throws IllegalActionException {
965        if (!_isEnabled(actor)) {
966            if (_debugging) {
967                _debug(((NamedObj) actor).getName() + ": "
968                        + ActorEnablingStatus.NOT_ENABLED);
969            }
970
971            return ActorEnablingStatus.NOT_ENABLED;
972        }
973
974        if (_isDeferrable(actor)) {
975            if (_debugging) {
976                _debug(((NamedObj) actor).getName() + ": "
977                        + ActorEnablingStatus.ENABLED_DEFERRABLE);
978            }
979
980            return ActorEnablingStatus.ENABLED_DEFERRABLE;
981        }
982
983        if (_debugging) {
984            _debug(((NamedObj) actor).getName() + ": "
985                    + ActorEnablingStatus.ENABLED_NOT_DEFERRABLE);
986        }
987
988        return ActorEnablingStatus.ENABLED_NOT_DEFERRABLE;
989    }
990
991    /** Check each remote receiver to see whether the number of tokens
992     *  in the receiver is greater than or equal to the
993     *  <i>tokenConsumptionRate</i> of the containing port. The actor
994     *  is deferrable if the above test is true for any receiver. At
995     *  the same time, find the maximum number of tokens in all
996     *  receivers, which is used to find minimax actors later on.
997     *  @param actor The actor to be checked.
998     *  @return true if the actor is deferrable, false if not.
999     *  @exception IllegalActionException If any called method throws
1000     *  IllegalActionException.
1001     */
1002    protected boolean _isDeferrable(Actor actor) throws IllegalActionException {
1003        boolean deferrable = false;
1004        int maxSize = 0;
1005
1006        Iterator outputPorts = actor.outputPortList().iterator();
1007
1008        while (outputPorts.hasNext()) {
1009            IOPort outputPort = (IOPort) outputPorts.next();
1010            Receiver[][] farReceivers = outputPort.getRemoteReceivers();
1011
1012            for (Receiver[] farReceiver2 : farReceivers) {
1013                if (farReceiver2 == null) {
1014                    continue;
1015                }
1016                for (int j = 0; j < farReceiver2.length; j++) {
1017                    QueueReceiver farReceiver = (QueueReceiver) farReceiver2[j];
1018                    IOPort port = farReceiver.getContainer();
1019
1020                    // Having a self-loop doesn't make it deferrable.
1021                    if (port.getContainer() == outputPort.getContainer()) {
1022                        continue;
1023                    }
1024
1025                    int tokenConsumptionRate = _getTokenConsumptionRate(
1026                            farReceiver);
1027
1028                    if (tokenConsumptionRate >= 0
1029                            && farReceiver.size() >= tokenConsumptionRate) {
1030                        deferrable = true;
1031
1032                        // Here we find the maximum of the token numbers for
1033                        // the actor's output channels which satisfy the demand
1034                        // of destination actors while checking deferrability.
1035                        // The advantage of this is that it only adds a small
1036                        // additional operation for now. If later on we need
1037                        // this information, we don't need to do traversing
1038                        // again. The disadvantage is that 1) we can return
1039                        // from this method as soon as deferrable == true if we
1040                        // don't perform this additional operation. 2) We will
1041                        // not need this information if it turns out not all
1042                        // enabled actors are deferrable. Therefore another
1043                        // approach is to perform this operation only when
1044                        // needed, i.e., when all enabled actor are deferrable.
1045                        if (farReceiver.size() > maxSize) {
1046                            maxSize = farReceiver.size();
1047                        }
1048                    }
1049                }
1050            }
1051        }
1052
1053        if (deferrable) {
1054            ActorInfo actorInfo = (ActorInfo) _actorsInfo.get(actor);
1055            actorInfo.maximumNumberOfTokens = maxSize;
1056        }
1057
1058        return deferrable;
1059    }
1060
1061    /** Check to see whether the actor is enabled. It is enabled if the
1062     *  tokenConsumptionRate on each input port is satisfied by all
1063     *  receivers contained by this port.
1064     *  @param actor The actor to be checked.
1065     *  @return true if the actor is enabled, false if not.
1066     *  @exception IllegalActionException If any called method throws
1067     *   IllegalActionException.
1068     */
1069    protected boolean _isEnabled(Actor actor) throws IllegalActionException {
1070        Iterator inputPorts = actor.inputPortList().iterator();
1071
1072        while (inputPorts.hasNext()) {
1073            IOPort inputPort = (IOPort) inputPorts.next();
1074            int[] rate = _getTokenConsumptionRate(inputPort);
1075
1076            for (int i = 0; i < inputPort.getWidth(); i++) {
1077                if (rate[i] > 0 && !inputPort.hasToken(i, rate[i])) {
1078                    return false;
1079                }
1080            }
1081        }
1082
1083        return true;
1084    }
1085
1086    /** Update the enabling status of the given actor and all actors
1087     *  connected to this actor. This method gets called after the given
1088     *  actor gets initialized or fired.
1089     * @param actor The actor to te checked.
1090     * @exception IllegalActionException If _getActorStatus(Actor) throws
1091     *  IllegalActionException.
1092     */
1093    protected void _updateConnectedActorsStatus(Actor actor)
1094            throws IllegalActionException {
1095        // Update enabling status for each connected actor.
1096        Iterator ports = ((Entity) actor).portList().iterator();
1097
1098        while (ports.hasNext()) {
1099            ComponentPort port = (ComponentPort) ports.next();
1100            Iterator deepConnectedPorts = port.deepConnectedPortList()
1101                    .iterator();
1102
1103            while (deepConnectedPorts.hasNext()) {
1104                Port deepConnectedPort = (Port) deepConnectedPorts.next();
1105                Actor connectedActor = (Actor) deepConnectedPort.getContainer();
1106
1107                // Skip it if the connectedActor to be checked contains
1108                // this director.
1109                if (getContainer() != connectedActor) {
1110                    // Get an array of actor flags from HashMap.
1111                    // Create it if none found.
1112                    ActorInfo actorInfo;
1113
1114                    if (_actorsInfo.containsKey(connectedActor)) {
1115                        actorInfo = (ActorInfo) _actorsInfo.get(connectedActor);
1116                    } else {
1117                        actorInfo = new ActorInfo();
1118                        _actorsInfo.put(connectedActor, actorInfo);
1119                    }
1120
1121                    actorInfo.status = _getActorStatus(connectedActor);
1122                }
1123            }
1124        }
1125
1126        // Update enabling status for this actor.
1127        ActorInfo actorInfo;
1128
1129        if (_actorsInfo.containsKey(actor)) {
1130            actorInfo = (ActorInfo) _actorsInfo.get(actor);
1131        } else {
1132            actorInfo = new ActorInfo();
1133            _actorsInfo.put(actor, actorInfo);
1134        }
1135
1136        actorInfo.status = _getActorStatus(actor);
1137    }
1138
1139    ///////////////////////////////////////////////////////////////////
1140    ////                         private methods                   ////
1141
1142    /** Get token consumption rate for the given port. If the port is an
1143     *  input port of an actor controlled by this director, the default
1144     *  rate is 1 unless explicitly specified by a <i>tokenConsumptionRate</i>
1145     *  parameter. If the port is an input port of the container of this
1146     *  director, the default value is -1 unless explicitly specified by
1147     *  a <i>tokenConsumptionRate</i> parameter. The value -1 means consuming
1148     *  all tokens (if there are any) contained by the port.
1149     *  @param port The port to get token consumption rate.
1150     *  @return An int array of token consumption rates.
1151     *  @exception IllegalActionException If parameter throws it or the
1152     *   length of tokenConsumptionRate array is less than port width.
1153     */
1154    private int[] _getTokenConsumptionRate(IOPort port)
1155            throws IllegalActionException {
1156        int[] rate = new int[port.getWidth()];
1157
1158        if (port.getContainer() != getContainer()) {
1159            Arrays.fill(rate, 1);
1160        } else {
1161            Arrays.fill(rate, -1);
1162        }
1163
1164        Variable rateVariable = DFUtilities.getRateVariable(port,
1165                "tokenConsumptionRate");
1166
1167        if (rateVariable != null) {
1168            Token token = rateVariable.getToken();
1169
1170            if (token != null) {
1171                // If token is ArrayToken, then each channel has a
1172                // corresponding input rate in the array.
1173                if (token instanceof ArrayToken) {
1174                    Token[] tokens = ((ArrayToken) token).arrayValue();
1175
1176                    for (int i = 0; i < port.getWidth(); i++) {
1177                        if (i < tokens.length) {
1178                            rate[i] = ((IntToken) tokens[i]).intValue();
1179                        }
1180                    }
1181                } else { // All the channels in the port have same
1182
1183                    // tokenConsumptionRate.
1184                    Arrays.fill(rate, ((IntToken) token).intValue());
1185                }
1186            }
1187        }
1188
1189        return rate;
1190    }
1191
1192    /** Get token consumption rate for the given receiver. The port
1193     *  containing the receiver can be an input port of an actor
1194     *  controlled by this director or an output port of the container
1195     *  of this director. In the latter case, it actually returns the
1196     *  production rate.
1197     *  @param receiver The receiver to get token consumption rate.
1198     *  @return The token consumption rate of the given receiver.
1199     *  @exception IllegalActionException If any called method throws
1200     *   IllegalActionException.
1201     */
1202    private int _getTokenConsumptionRate(Receiver receiver)
1203            throws IllegalActionException {
1204        int tokenConsumptionRate;
1205
1206        IOPort port = receiver.getContainer();
1207        Variable rateVariable = null;
1208        Token token = null;
1209        Receiver[][] portReceivers = null;
1210
1211        // If DDF domain is inside another domain and the
1212        // receiver is contained by an opaque output port...
1213        // The default production rate is -1 which means all
1214        // tokens in the receiver are transferred to the outside.
1215        if (port.isOutput()) {
1216            rateVariable = DFUtilities.getRateVariable(port,
1217                    "tokenProductionRate");
1218            portReceivers = port.getInsideReceivers();
1219
1220            if (rateVariable == null) {
1221                tokenConsumptionRate = -1;
1222                return tokenConsumptionRate;
1223            } else {
1224                token = rateVariable.getToken();
1225
1226                if (token == null) {
1227                    tokenConsumptionRate = -1;
1228                    return tokenConsumptionRate;
1229                }
1230            }
1231        }
1232
1233        if (port.isInput()) {
1234            rateVariable = DFUtilities.getRateVariable(port,
1235                    "tokenConsumptionRate");
1236            portReceivers = port.getReceivers();
1237
1238            if (rateVariable == null) {
1239                tokenConsumptionRate = 1;
1240                return tokenConsumptionRate;
1241            } else {
1242                token = rateVariable.getToken();
1243
1244                if (token == null) {
1245                    tokenConsumptionRate = 1;
1246                    return tokenConsumptionRate;
1247                }
1248            }
1249        }
1250
1251        if (token instanceof ArrayToken) {
1252            Token[] tokens = ((ArrayToken) token).arrayValue();
1253
1254            // Scan the contained receivers of the port to find
1255            // out channel index.
1256            int channelIndex = 0;
1257            foundChannelIndex: for (int m = 0; m < portReceivers.length; m++) {
1258                for (int n = 0; n < portReceivers[m].length; n++) {
1259                    if (receiver == portReceivers[m][n]) {
1260                        channelIndex = m;
1261                        break foundChannelIndex;
1262                    }
1263                }
1264            }
1265
1266            tokenConsumptionRate = ((IntToken) tokens[channelIndex]).intValue();
1267        } else {
1268            tokenConsumptionRate = ((IntToken) token).intValue();
1269        }
1270
1271        return tokenConsumptionRate;
1272    }
1273
1274    /** Get token production rate for the given port. The port argument
1275     *  should always be an output port of the container of this director.
1276     *  The convention is that if a parameter named <i>tokenproductionRate</i>
1277     *  is defined, return the value in that parameter. Otherwise, return
1278     *  an array of int each with value -1 which means the director should
1279     *  transfer all tokens contained on the inside by the port to the outside.
1280     *  Note the difference from SDF domain where the default rate is 1.
1281     *  @param port The port to get token production rate.
1282     *  @return An int array of token production rate.
1283     *  @exception IllegalActionException If parameter throws it
1284     *   or the length of tokenProductionRate array is less
1285     *   than port inside width or the port in the argument is
1286     *   not an output port of the container of this director.
1287     */
1288    private int[] _getTokenProductionRate(IOPort port)
1289            throws IllegalActionException {
1290        if (port.getContainer() != getContainer()) {
1291            throw new IllegalActionException(this, "The port in the "
1292                    + "argument is not an output port of the container of "
1293                    + getName());
1294        }
1295
1296        int[] rate = new int[port.getWidthInside()];
1297        Arrays.fill(rate, -1);
1298
1299        Variable rateVariable = DFUtilities.getRateVariable(port,
1300                "tokenProductionRate");
1301
1302        if (rateVariable != null) {
1303            Token token = rateVariable.getToken();
1304
1305            if (token != null) {
1306                // If token is ArrayToken, then each channel has a
1307                // corresponding output rate in the array.
1308                if (token instanceof ArrayToken) {
1309                    Token[] tokens = ((ArrayToken) token).arrayValue();
1310
1311                    if (tokens.length < port.getWidthInside()) {
1312                        throw new IllegalActionException(this, "The length of "
1313                                + "tokenProductionRate array is less than "
1314                                + "the port inside width.");
1315                    }
1316
1317                    for (int i = 0; i < port.getWidthInside(); i++) {
1318                        if (i < tokens.length) {
1319                            rate[i] = ((IntToken) tokens[i]).intValue();
1320                        }
1321                    }
1322                } else { // All the channels in the port has same
1323
1324                    // tokenProductionRate.
1325                    Arrays.fill(rate, ((IntToken) token).intValue());
1326                }
1327            }
1328        }
1329
1330        return rate;
1331    }
1332
1333    /** Initialize the object. In this case, we give the DDFDirector
1334     *  an <i>iterations</i> parameter with default value zero,
1335     *  a <i>maximumReceiverCapacity</i> parameter with default value zero
1336     *  and a <i>runUntilDeadlockInOneIteration</i> parameter with default
1337     *  value false.
1338     */
1339    private void _init()
1340            throws IllegalActionException, NameDuplicationException {
1341        iterations = new Parameter(this, "iterations");
1342        iterations.setTypeEquals(BaseType.INT);
1343        iterations.setToken(new IntToken(0));
1344
1345        maximumReceiverCapacity = new Parameter(this,
1346                "maximumReceiverCapacity");
1347        maximumReceiverCapacity.setTypeEquals(BaseType.INT);
1348        maximumReceiverCapacity.setToken(new IntToken(0));
1349
1350        runUntilDeadlockInOneIteration = new Parameter(this,
1351                "runUntilDeadlockInOneIteration");
1352        runUntilDeadlockInOneIteration.setTypeEquals(BaseType.BOOLEAN);
1353        runUntilDeadlockInOneIteration.setToken(new BooleanToken(false));
1354    }
1355
1356    ///////////////////////////////////////////////////////////////////
1357    ////                         private variables                 ////
1358
1359    /** A flag indicating whether type resolution is disabled.
1360     */
1361    private boolean _isTypeResolutionDisabled = false;
1362
1363    /** A flag indicating whether at least one actor has been fired so far.
1364     */
1365    private boolean _firedOne = false;
1366
1367    /** The number of iterations.
1368     */
1369    private int _iterationCount = 0;
1370
1371    /** A boolean initialized with value in the parameter
1372     *  runUntilDeadlockInOneIteration.
1373     */
1374    private boolean _runUntilDeadlock;
1375
1376    /** A HashMap containing actors' information. Each actor is mapped to
1377     *  an ActorInfo object.
1378     */
1379    private HashMap _actorsInfo = new HashMap();
1380
1381    /** A list to store those actors for which positive
1382     *  requiredFiringsPerIteration has been defined.
1383     */
1384    private LinkedList _actorsToCheckNumberOfFirings = new LinkedList();
1385
1386    /** The set of actors that have returned false in their postfire()
1387     *  methods and therefore become disabled.
1388     */
1389    private Set _disabledActors = new HashSet();
1390
1391    ///////////////////////////////////////////////////////////////////
1392    ////                         inner classes                     ////
1393
1394    /** This private class is data structure for recording an actor's
1395     *  information during the execution.
1396     */
1397    private static class ActorInfo {
1398
1399        // FindBugs suggests making this class static so as to decrease
1400        // the size of instances and avoid dangling references.
1401
1402        /** This field records the enabling status of the actor.
1403         */
1404        public ActorEnablingStatus status;
1405
1406        /** This field records the number of firings of the actor.
1407         *  It is reset to 0 at the beginning of each iteration of
1408         *  the model the actor is in.
1409         */
1410        public int numberOfFirings;
1411
1412        /** This field records the maximum number of tokens on the actor's
1413         *  output channels which satisfy the demand of destination actors.
1414         *  It is used to find minimax actors.
1415         */
1416        public int maximumNumberOfTokens;
1417
1418        /** This field records the actor's required number of firings
1419         *  per iteration of the model the actor is in.
1420         */
1421        public int requiredFiringsPerIteration;
1422    }
1423}