001/* A director that uses a static schedule.
002
003 Copyright (c) 1998-2014 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.actor.sched;
029
030import java.util.Iterator;
031
032import ptolemy.actor.Actor;
033import ptolemy.actor.CompositeActor;
034import ptolemy.actor.Director;
035import ptolemy.actor.FiringEvent;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.DebugListener;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InvalidStateException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.NamedObj;
042import ptolemy.kernel.util.Workspace;
043
044///////////////////////////////////////////////////////////////////
045//// StaticSchedulingDirector
046
047/**
048 A director that uses static scheduling to govern the execution of the
049 CompositeActor it belongs to. <p>
050
051 This class does not directly implement a scheduling algorithm, but
052 defers to its contained scheduler.  The contained scheduler creates an
053 instance of the Schedule class which determines the number of times
054 each actor should be fired and their firing order.  This allows new
055 scheduling algorithms to be easily created for existing domains.<p>
056
057 This class is generally useful for statically scheduled domains where
058 a schedule can be constructed once and used to repeatedly execute the
059 model.  The Scheduler class caches the schedule until the model changes
060 so that the schedule does not have to be recomputed.
061
062 @author Jie Liu, Steve Neuendorffer
063 @version $Id$
064 @since Ptolemy II 0.2
065 @Pt.ProposedRating Green (neuendor)
066 @Pt.AcceptedRating Yellow (neuendor)
067 @see ptolemy.actor.Director
068 @see Scheduler
069 @see Schedule
070 */
071public class StaticSchedulingDirector extends Director {
072    /** Construct a director in the default workspace with an empty string
073     *  as its name. The director is added to the list of objects in
074     *  the workspace. Increment the version number of the workspace.
075     *  @exception NameDuplicationException If construction of Time objects fails.
076     *  @exception IllegalActionException If construction of Time objects fails.
077     */
078    public StaticSchedulingDirector()
079            throws IllegalActionException, NameDuplicationException {
080        super();
081    }
082
083    /** Construct a director in the  workspace with an empty name.
084     *  The director is added to the list of objects in the workspace.
085     *  Increment the version number of the workspace.
086     *  @param workspace The workspace of this object.
087     *  @exception NameDuplicationException If construction of Time objects fails.
088     *  @exception IllegalActionException If construction of Time objects fails.
089     */
090    public StaticSchedulingDirector(Workspace workspace)
091            throws IllegalActionException, NameDuplicationException {
092        super(workspace);
093    }
094
095    /** Construct a director in the given container with the given name.
096     *  If the container argument must not be null, or a
097     *  NullPointerException will be thrown.
098     *  If the name argument is null, then the name is set to the
099     *  empty string. Increment the version number of the workspace.
100     *
101     *  @param container The container of this director.
102     *  @param name Name of this director.
103     *  @exception IllegalActionException Not thrown in this base class.
104     *   May be thrown in the derived classes if the director
105     *  is not compatible with the specified container.
106     *  @exception NameDuplicationException If the name collides with
107     *  an attribute that already exists in the given container.
108     */
109    public StaticSchedulingDirector(CompositeEntity container, String name)
110            throws IllegalActionException, NameDuplicationException {
111        super(container, name);
112    }
113
114    ///////////////////////////////////////////////////////////////////
115    ////                         public methods                    ////
116
117    /** Override the base class to also listen to the scheduler, if there
118     *  is one.
119     *  @param listener The listener to which to send debug messages.
120     *  @see #removeDebugListener(DebugListener)
121     */
122    @Override
123    public synchronized void addDebugListener(DebugListener listener) {
124        super.addDebugListener(listener);
125
126        Scheduler scheduler = getScheduler();
127
128        if (scheduler != null) {
129            scheduler.addDebugListener(listener);
130        }
131    }
132
133    /** Clone the object into the specified workspace. The new object is
134     *  <i>not</i> added to the directory of that workspace (you must do this
135     *  yourself if you want it there).
136     *  @param workspace The workspace for the cloned object.
137     *  @exception CloneNotSupportedException Not thrown in this base class
138     *  @return The new Attribute.
139     */
140    @Override
141    public Object clone(Workspace workspace) throws CloneNotSupportedException {
142        StaticSchedulingDirector newObject = (StaticSchedulingDirector) super.clone(
143                workspace);
144        Scheduler scheduler = getScheduler();
145        if (scheduler == null) {
146            newObject._setScheduler(null);
147        } else {
148            newObject._setScheduler((Scheduler) newObject
149                    .getAttribute(getScheduler().getName()));
150        }
151        return newObject;
152    }
153
154    /** Initialize local variables.
155     *  @exception IllegalActionException Thrown by super class.
156     */
157    @Override
158    public void initialize() throws IllegalActionException {
159        super.initialize();
160
161        _savedSchedule = null;
162        _savedSchedulePosition = -1;
163        _savedIterationCount = 0;
164
165        _actorFinished = false;
166    };
167
168    /** Calculate the current schedule, if necessary, and iterate the
169     *  contained actors in the order given by the schedule.
170     *  Iterating an actor involves calling the actor's iterate() method,
171     *  which is equivalent to calling the actor's prefire(), fire() and
172     *  postfire() methods in succession.  If iterate() returns NOT_READY,
173     *  indicating that the actor is not ready to execute, then an
174     *  IllegalActionException will be thrown. The values returned from
175     *  iterate() are recorded and are used to determine the value that
176     *  postfire() will return at the end of the director's iteration.
177     *  NOTE: This method does not conform with the strict actor semantics
178     *  because it calls postfire() of actors. Thus, it should not be used
179     *  in domains that require a strict actor semantics, such as SR or
180     *  Continuous.
181     *  @exception IllegalActionException If any actor executed by this
182     *   actor return false in prefire.
183     *  @exception InvalidStateException If this director does not have a
184     *   container.
185     */
186    @Override
187    public void fire() throws IllegalActionException {
188        // Don't call "super.fire();" here because if you do then
189        // everything happens twice.
190        Iterator firings = null;
191
192        Scheduler scheduler = getScheduler();
193
194        if (scheduler == null) {
195            throw new IllegalActionException(
196                    "Attempted to fire " + "system with no scheduler");
197        }
198
199        // This will throw IllegalActionException if this director
200        // does not have a container.
201        Schedule schedule = scheduler.getSchedule();
202        firings = schedule.firingIterator();
203
204        Firing firing = null;
205        while (firings.hasNext() && !_stopRequested) {
206            firing = (Firing) firings.next();
207            Actor actor = firing.getActor();
208
209            int iterationCount = firing.getIterationCount();
210
211            if (_debugging) {
212                _debug(new FiringEvent(this, actor, FiringEvent.BEFORE_ITERATE,
213                        iterationCount));
214            }
215
216            int returnValue = actor.iterate(iterationCount);
217
218            if (returnValue == STOP_ITERATING) {
219                _postfireReturns = false;
220                if (_debugging) {
221                    _debug("Actor requests no more firings: "
222                            + actor.getFullName());
223                }
224            } else if (returnValue == NOT_READY) {
225                // See de/test/auto/knownFailedTests/DESDFClockTest.xml
226                throw new IllegalActionException(this, actor, "Actor "
227                        + "is not ready to fire.  Perhaps " + actor.getName()
228                        + ".prefire() returned false? "
229                        + "Try debugging the actor by selecting "
230                        + "\"Listen to Actor\".  Also, for SDF check moml for "
231                        + "tokenConsumptionRate on input.");
232            }
233
234            if (_debugging) {
235                _debug(new FiringEvent(this, actor, FiringEvent.AFTER_ITERATE,
236                        iterationCount));
237            }
238        }
239    }
240
241    /** Return the scheduler that is responsible for scheduling the
242     *  directed actors.  This method is read-synchronized on the
243     *  workspace.
244     *
245     *  @return The contained scheduler.
246     *  @see #setScheduler(Scheduler)
247     */
248    public Scheduler getScheduler() {
249        try {
250            workspace().getReadAccess();
251            return _scheduler;
252        } finally {
253            workspace().doneReading();
254        }
255    }
256
257    /** Indicate that a schedule for the model may no longer be valid.
258     *  This method should be called when topology changes are made,
259     *  or for that matter when any change that may invalidate the
260     *  schedule is made.  In this base class, this method sets a flag
261     *  that forces scheduling to be redone at the next opportunity.
262     *  If there is no scheduler, do nothing.
263     */
264    @Override
265    public void invalidateSchedule() {
266        _debug("Invalidating schedule.");
267        if (_scheduler != null) {
268            _scheduler.setValid(false);
269        }
270    }
271
272    /** Return true if the current (cached) schedule is valid.
273     *  This calls the valid() method of Scheduler.
274     *  @return true if the schedule is valid.
275     *  @exception IllegalActionException If there's no scheduler.
276     */
277    public boolean isScheduleValid() throws IllegalActionException {
278        if (_scheduler == null) {
279            throw new IllegalActionException(this, "has no scheduler.");
280        }
281
282        return _scheduler.isValid();
283    }
284
285    /** Return true if the director wishes to be scheduled for another
286     *  iteration. This base class returns true if all of the actors
287     *  iterated since the last call to prefire returned true from their
288     *  postfire() method and if stop() has not been called. Subclasses
289     *  may override this method to perform additional
290     *  domain-specific behavior.
291     *  @return True if the Director wants to be fired again in the
292     *  future.
293     *  @exception IllegalActionException Not thrown in this base class.
294     */
295    @Override
296    public boolean postfire() throws IllegalActionException {
297        boolean result = super.postfire() && _postfireReturns;
298        if (_debugging) {
299            _debug("Postfire returns: " + result);
300        }
301        return result;
302    }
303
304    /** Resume the execution of an actor that was previously blocked because
305     *  it didn't have all the resources it needed for execution.
306     *  @param actor The actor that resumes execution.
307     *  @exception IllegalActionException Not thrown here but in derived classes.
308     */
309    @Override
310    public void resumeActor(NamedObj actor) throws IllegalActionException {
311        _actorFinished = true;
312    }
313
314    /** Return true if the director is ready to fire. This method is
315     *  called by the container of this director to determine whether
316     *  the director is ready to execute. It does <i>not</i> call
317     *  prefire() on the contained actors.  If this director is not at
318     *  the top level of the hierarchy, and the current time of the
319     *  enclosing model is greater than the current time of this
320     *  director, then this base class updates current time to match
321     *  that of the enclosing model.  <p>
322     *
323     *  In this base class, assume that the director is always ready
324     *  to be fired, and so return true.  Domain directors should
325     *  probably override this method to provide domain-specific
326     *  operation.  However, they should call super.prefire() if they
327     *  wish to propagate time as done here.
328     *
329     *  @return True.
330     *  @exception IllegalActionException Not thrown in this base class.
331     */
332    @Override
333    public boolean prefire() throws IllegalActionException {
334        _postfireReturns = true;
335        _prefire = super.prefire();
336        if (_aspectsPresent && _prefire) {
337
338            Iterator firings = null;
339            if (_savedSchedule == null) {
340                Scheduler scheduler = getScheduler();
341                Schedule schedule = scheduler.getSchedule();
342                _savedSchedule = schedule;
343                _savedSchedulePosition = 0;
344                firings = schedule.firingIterator();
345            } else {
346                firings = _savedSchedule.firingIterator();
347                for (int i = 0; i < _savedSchedulePosition; i++) {
348                    firings.next();
349                }
350            }
351
352            Firing firing = null;
353            while ((_savedIterationCount > 0 || firings.hasNext())
354                    && !_stopRequested) {
355
356                if (firing == null || _savedIterationCount == 0) {
357                    firing = (Firing) firings.next();
358                }
359                Actor actor = firing.getActor();
360
361                if (!_actorFinished) {
362                    if (_tokenSentToCommunicationAspect) {
363                        _tokenSentToCommunicationAspect = false;
364                        if (((CompositeActor) getContainer())
365                                .getContainer() != null) {
366                            ((CompositeActor) getContainer())
367                                    .getExecutiveDirector().fireAtCurrentTime(
368                                            (CompositeActor) getContainer());
369                        }
370                        _prefire = false;
371                        return false;
372                    }
373                    boolean finished = _schedule((NamedObj) actor,
374                            getModelTime());
375                    if (!finished) {
376                        _prefire = false;
377                        return false;
378                    }
379                }
380                _actorFinished = false;
381
382                if (_savedIterationCount == 0) {
383                    _savedIterationCount = firing.getIterationCount();
384                }
385
386                _savedIterationCount--;
387                if (_savedIterationCount == 0) {
388                    _savedSchedulePosition++;
389                }
390
391            }
392            if (_savedSchedule.size() <= _savedSchedulePosition) {
393                _savedSchedule = null;
394            }
395        }
396        return _prefire;
397    }
398
399    /** Override the base class to also remove the listener from the scheduler,
400     *  if there is one.
401     *  @param listener The listener to remove from the list of listeners
402     *   to which debug messages are sent.
403     *  @see #addDebugListener(DebugListener)
404     */
405    @Override
406    public synchronized void removeDebugListener(DebugListener listener) {
407        super.removeDebugListener(listener);
408
409        Scheduler scheduler = getScheduler();
410
411        if (scheduler != null) {
412            scheduler.removeDebugListener(listener);
413        }
414    }
415
416    /** Set the scheduler for this StaticSchedulingDirector.
417     *  The container of the specified scheduler is set to this director.
418     *  If there was a previous scheduler, the container of that scheduler
419     *  is set to null. This method is write-synchronized on the workspace.
420     *  If the scheduler is not compatible with the director, an
421     *  IllegalActionException is thrown.
422     *  @param scheduler The scheduler that this director will use.
423     *  @exception IllegalActionException Not thrown in this base class,
424     *   but derived classes may throw it if the scheduler is not compatible.
425     *  @exception NameDuplicationException Not thrown in this base class,
426     *   but derived classes may throw it if the scheduler is not compatible.
427     *  @see #getScheduler()
428     */
429    public void setScheduler(Scheduler scheduler)
430            throws IllegalActionException, NameDuplicationException {
431        if (scheduler != null) {
432            scheduler.setContainer(this);
433        } else {
434            if (_scheduler != null) {
435                _scheduler.setContainer(null);
436            }
437        }
438        _setScheduler(scheduler);
439    }
440
441    ///////////////////////////////////////////////////////////////////
442    ////                         protected methods                 ////
443
444    /** Set the local scheduler for execution of this Director.
445     *  This should not be called be directly.  Instead, call setContainer()
446     *  on the scheduler.  This method removes any previous scheduler
447     *  from this container, and caches a local reference to the scheduler
448     *  so that this composite does not need to search its attributes each
449     *  time the scheduler is accessed.
450     *  @param scheduler The Scheduler responsible for execution.
451     */
452    protected void _setScheduler(Scheduler scheduler) {
453        // If the scheduler is not changed, do nothing.
454        if (_scheduler != scheduler) {
455            _scheduler = scheduler;
456            invalidateSchedule();
457        }
458    }
459
460    ///////////////////////////////////////////////////////////////////
461    ////                         protected variables               ////
462
463    /** The value that the postfire method will return. */
464    protected boolean _postfireReturns;
465
466    ///////////////////////////////////////////////////////////////////
467    ////                         private variables                 ////
468
469    /** The value returned by the prefire() method. */
470    protected boolean _prefire = false;
471
472    private boolean _actorFinished;
473
474    /** Computed schedule that has not been fully executed because this
475     *  director is waiting for resources.
476     */
477    private Schedule _savedSchedule;
478
479    /** Saved position in the fixed schedule. Resume execution from there.
480     */
481    private int _savedSchedulePosition;
482
483    /** Number of iterations that have been performed already.
484     */
485    private int _savedIterationCount;
486
487    /** The scheduler. */
488    private Scheduler _scheduler;
489}