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}