001/* A base class for schedulers.
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 FIXME: Caching should move into StaticSchedulingDirector.
028 */
029package ptolemy.actor.sched;
030
031import java.util.Iterator;
032import java.util.List;
033
034import ptolemy.actor.Actor;
035import ptolemy.actor.CompositeActor;
036import ptolemy.actor.Director;
037import ptolemy.kernel.util.Attribute;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InternalErrorException;
040import ptolemy.kernel.util.KernelException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Nameable;
043import ptolemy.kernel.util.NamedObj;
044import ptolemy.kernel.util.Workspace;
045
046///////////////////////////////////////////////////////////////////
047//// Scheduler
048
049/**
050 The base class for schedulers. A scheduler schedules the execution
051 order of the containees of a CompositeActor.  <p>
052
053 A scheduler is contained by a StaticSchedulingDirector, and provides
054 the schedule for it.  The director will use this schedule to govern
055 the execution of a CompositeActor. <p>
056
057 A schedule is represented by the Schedule class, and determines the
058 order of the firing of the actors in a particular composite actor.  In
059 this base class, the default schedule fires the deeply
060 contained actors in the order of their construction.  A domain specific
061 scheduler will override this to provide a different order. <p>
062
063 The schedule, once constructed, is cached and reused as long as the
064 schedule is still valid.  The validity of the schedule is set by the
065 setValid() method.  If the current schedule is not valid, then the
066 schedule will be recomputed the next time the getSchedule() method is
067 called.  However, derived classes will usually override only the
068 protected _getSchedule() method. <p>
069
070 The scheduler does not perform any mutations, and it does not listen
071 for changes in the model.  Directors that use this scheduler should
072 normally invalidate the schedule when mutations occur.
073
074 @author Jie Liu, Steve Neuendorffer
075 @version $Id$
076 @since Ptolemy II 0.2
077 @Pt.ProposedRating Green (neuendor)
078 @Pt.AcceptedRating Yellow (neuendor)
079 @see ptolemy.actor.sched.Schedule
080 */
081public class Scheduler extends Attribute {
082    /** Construct a scheduler with no container(director)
083     *  in the default workspace, the name of the scheduler is
084     *  "Scheduler".
085     */
086    public Scheduler() {
087        super();
088
089        try {
090            setName(_DEFAULT_SCHEDULER_NAME);
091        } catch (KernelException ex) {
092            // Should not be thrown.
093            throw new InternalErrorException(this, ex, null);
094        }
095    }
096
097    /** Construct a scheduler in the given workspace with the name
098     *  "Scheduler".
099     *  If the workspace argument is null, use the default workspace.
100     *  The scheduler is added to the list of objects in the workspace.
101     *  Increment the version number of the workspace.
102     *  @param workspace Object for synchronization and version tracking.
103     */
104    public Scheduler(Workspace workspace) {
105        super(workspace);
106
107        try {
108            setName(_DEFAULT_SCHEDULER_NAME);
109        } catch (KernelException ex) {
110            // Should not be thrown.
111            throw new InternalErrorException(this, ex, null);
112        }
113    }
114
115    /** Construct a scheduler in the given container with the given name.
116     *  The container argument must not be null, or a
117     *  NullPointerException will be thrown.  This attribute will use the
118     *  workspace of the container for synchronization and version counts.
119     *  If the name argument is null, then the name is set to the empty string.
120     *  Increment the version of the workspace.
121     *  @param container The container.
122     *  @param name The name of this attribute.
123     *  @exception IllegalActionException If the attribute is not of an
124     *   acceptable class for the container, or if the name contains a period.
125     *  @exception NameDuplicationException If the name coincides with
126     *   an attribute already in the container.
127     */
128    public Scheduler(Director container, String name)
129            throws IllegalActionException, NameDuplicationException {
130        super(container, name);
131    }
132
133    ///////////////////////////////////////////////////////////////////
134    ////                         public methods                    ////
135
136    /** Clone the scheduler into the specified workspace. The new object is
137     *  <i>not</i> added to the directory of that workspace (you must do this
138     *  yourself if you want it there).
139     *  The result is a new scheduler with no container, and no valid schedule.
140     *  @param workspace The workspace for the cloned object.
141     *  @exception CloneNotSupportedException If one of the attributes
142     *   cannot be cloned.
143     *  @return The new Scheduler.
144     */
145    @Override
146    public Object clone(Workspace workspace) throws CloneNotSupportedException {
147        Scheduler newObject = (Scheduler) super.clone(workspace);
148        newObject._valid = false;
149        newObject._cachedGetSchedule = null;
150        return newObject;
151    }
152
153    /** Return the scheduling sequence as an instance of the Schedule class.
154     *  For efficiency, this method returns a cached version of the
155     *  schedule, if it is valid.  Otherwise, it calls the protected
156     *  method _getSchedule() to update the schedule.  Derived classes
157     *  normally override the protected method, not this one.
158     *  The validity of the current schedule is set by the setValid()
159     *  method.  This method is read-synchronized on the workspace.
160     *  @return The Schedule returned by the _getSchedule() method.
161     *  @exception IllegalActionException If the scheduler has no container
162     *  (a director), or the director has no container (a CompositeActor),
163     *  or the scheduling algorithm throws it.
164     *  @exception NotSchedulableException If the _getSchedule() method
165     *  throws it. Not thrown in this base class, but may be needed
166     *  by the derived schedulers.
167     */
168    public Schedule getSchedule()
169            throws IllegalActionException, NotSchedulableException {
170        try {
171            workspace().getReadAccess();
172
173            StaticSchedulingDirector director = (StaticSchedulingDirector) getContainer();
174
175            if (director == null) {
176                throw new IllegalActionException(this,
177                        "Scheduler has no director.");
178            }
179
180            CompositeActor compositeActor = (CompositeActor) director
181                    .getContainer();
182
183            if (compositeActor == null) {
184                throw new IllegalActionException(this,
185                        "Director has no container.");
186            }
187
188            if (!isValid() || _cachedGetSchedule == null) {
189                _cachedGetSchedule = _getSchedule();
190                _workspaceVersion = workspace().getVersion();
191            }
192
193            return _cachedGetSchedule;
194        } finally {
195            workspace().doneReading();
196        }
197    }
198
199    /** Return true if the current schedule is valid.
200     *  @return true if the current schedule is valid.
201     */
202    public boolean isValid() {
203        if (_workspaceVersion != workspace().getVersion()) {
204            return false;
205        }
206        return _valid;
207    }
208
209    /** Specify the container.  If the specified container is an instance
210     *  of Director, then this becomes the active scheduler for
211     *  that director.  Otherwise, this is an attribute like any other within
212     *  the container. If the container is not in the same
213     *  workspace as this director, throw an exception.
214     *  If this scheduler is already an attribute of the container,
215     *  then this has the effect only of making it the active scheduler.
216     *  If this scheduler already has a container, remove it
217     *  from that container first.  Otherwise, remove it from
218     *  the directory of the workspace, if it is present.
219     *  If the argument is null, then remove it from its container.
220     *  This director is not added to the workspace directory, so calling
221     *  this method with a null argument could result in
222     *  this director being garbage collected.
223     *  <p>
224     *  If this method results in removing this director from a container
225     *  that is a Director, then this scheduler ceases to be the active
226     *  scheduler for that CompositeActor.  Moreover, if the director
227     *  contains any other schedulers, then the most recently added of those
228     *  schedulers becomes the active scheduler.
229     *  <p>
230     *  This method is write-synchronized
231     *  to the workspace and increments its version number.
232     *  @param container The proposed container.
233     *  @exception IllegalActionException If the action would result in a
234     *   recursive containment structure, or if
235     *   this scheduler and container are not in the same workspace, or
236     *   if the protected method _checkContainer() throws it.
237     *  @exception NameDuplicationException If the name of this scheduler
238     *   collides with a name already in the container.  This will not
239     *   be thrown if the container argument is an instance of
240     *   CompositeActor.
241     */
242    @Override
243    public void setContainer(NamedObj container)
244            throws IllegalActionException, NameDuplicationException {
245        try {
246            _workspace.getWriteAccess();
247
248            Nameable oldContainer = getContainer();
249
250            if (oldContainer instanceof Director && oldContainer != container) {
251                // Need to remove this scheduler as the active one of the
252                // old container. Search for another scheduler contained
253                // by the composite.  If it contains more than one,
254                // use the most recently added one.
255                Scheduler previous = null;
256                StaticSchedulingDirector castContainer = (StaticSchedulingDirector) oldContainer;
257                Iterator schedulers = castContainer
258                        .attributeList(Scheduler.class).iterator();
259
260                while (schedulers.hasNext()) {
261                    Scheduler altScheduler = (Scheduler) schedulers.next();
262
263                    // Since we haven't yet removed this director, we have
264                    // to be sure to not just set it to the active
265                    // director again.
266                    if (altScheduler != this) {
267                        previous = altScheduler;
268                    }
269                }
270
271                castContainer._setScheduler(previous);
272            }
273
274            super.setContainer(container);
275
276            if (container instanceof StaticSchedulingDirector) {
277                // Set cached value in director
278                ((StaticSchedulingDirector) container)._setScheduler(this);
279            }
280        } finally {
281            _workspace.doneWriting();
282        }
283    }
284
285    /** Validate/invalidate the current schedule by giving a
286     *  true/false argument.  A true argument will indicate that the
287     *  current schedule is valid and can be returned immediately when
288     *  schedule() is called without running the scheduling
289     *  algorithm. A false argument will invalidate it.
290     *  @param valid True to set the current schedule to valid.
291     */
292    public void setValid(boolean valid) {
293        _valid = valid;
294        if (valid == false) {
295            _cachedGetSchedule = null;
296        } else {
297            _workspaceVersion = workspace().getVersion();
298        }
299    }
300
301    ///////////////////////////////////////////////////////////////////
302    ////                         protected variables               ////
303
304    /** The default name.
305     */
306    protected static final String _DEFAULT_SCHEDULER_NAME = "Scheduler";
307
308    ///////////////////////////////////////////////////////////////////
309    ////                         protected methods                 ////
310
311    /** Reschedule the model.  In this base class, this method returns
312     *  the actors contained by the CompositeActor in the order of
313     *  their construction, i.e. the same order as returned by the
314     *  CompositeActor.deepGetEntities() method.  Derived classes
315     *  should override this method to provide a domain-specific
316     *  scheduling algorithm.  This method is not intended to be
317     *  called directly, but is called in turn by the getSchedule()
318     *  method.  This method is not synchronized on the workspace, because
319     *  the getSchedule() method is.
320     *
321     *  @return A Schedule of the deeply contained opaque entities
322     *  in the firing order.
323     *  @exception IllegalActionException If the scheduling algorithm
324     *  throws it. Not thrown in this base class, but may be thrown
325     *  by derived classes.
326     *  @exception NotSchedulableException If the CompositeActor is not
327     *  schedulable. Not thrown in this base class, but may be thrown
328     *  by derived classes.
329     *  @see ptolemy.kernel.CompositeEntity#deepEntityList()
330     */
331    protected Schedule _getSchedule()
332            throws IllegalActionException, NotSchedulableException {
333        StaticSchedulingDirector director = (StaticSchedulingDirector) getContainer();
334        CompositeActor compositeActor = (CompositeActor) director
335                .getContainer();
336        List actors = compositeActor.deepEntityList();
337        Schedule schedule = new Schedule();
338        Iterator actorIterator = actors.iterator();
339
340        while (actorIterator.hasNext()) {
341            Actor actor = (Actor) actorIterator.next();
342            Firing firing = new Firing();
343            firing.setActor(actor);
344            schedule.add(firing);
345        }
346
347        return schedule;
348    }
349
350    ///////////////////////////////////////////////////////////////////
351    ////                         private variables                 ////
352
353    // The cached schedule for getSchedule().
354    private Schedule _cachedGetSchedule = null;
355
356    // The flag that indicate whether the current schedule is valid.
357    private boolean _valid = false;
358
359    // Workspace version when the last schedule was constructed.
360    private long _workspaceVersion = -1L;
361}