001/* Director for the synchronous dataflow model of computation. 002 003 Copyright (c) 1997-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 PT_COPYRIGHT_VERSION_2 024 COPYRIGHTENDKEY 025 026 */ 027package ptolemy.domains.sdf.kernel; 028 029import java.util.Iterator; 030 031import ptolemy.actor.Actor; 032import ptolemy.actor.CompositeActor; 033import ptolemy.actor.IOPort; 034import ptolemy.actor.NoTokenException; 035import ptolemy.actor.Receiver; 036import ptolemy.actor.TypedCompositeActor; 037import ptolemy.actor.parameters.ParameterPort; 038import ptolemy.actor.sched.NotSchedulableException; 039import ptolemy.actor.sched.Schedule; 040import ptolemy.actor.sched.StaticSchedulingDirector; 041import ptolemy.actor.util.DFUtilities; 042import ptolemy.actor.util.PeriodicDirector; 043import ptolemy.actor.util.PeriodicDirectorHelper; 044import ptolemy.actor.util.Time; 045import ptolemy.data.BooleanToken; 046import ptolemy.data.DoubleToken; 047import ptolemy.data.IntToken; 048import ptolemy.data.Token; 049import ptolemy.data.expr.Parameter; 050import ptolemy.data.type.BaseType; 051import ptolemy.kernel.CompositeEntity; 052import ptolemy.kernel.util.Attribute; 053import ptolemy.kernel.util.IllegalActionException; 054import ptolemy.kernel.util.InternalErrorException; 055import ptolemy.kernel.util.NameDuplicationException; 056import ptolemy.kernel.util.Settable; 057import ptolemy.kernel.util.Workspace; 058 059/////////////////////////////////////////////////////////////////// 060//// SDFDirector 061 062/** 063 Director for the synchronous dataflow (SDF) model of computation. 064 065 <h1>SDF overview</h1> 066 The Synchronous Dataflow(SDF) domain supports the efficient 067 execution of Dataflow graphs that 068 lack control structures. Dataflow graphs that contain control structures 069 should be executed using the Process Networks(PN) domain instead. 070 SDF allows efficient execution, with very little overhead at runtime. It 071 requires that the rates on the ports of all actors be known before hand. 072 SDF also requires that the rates on the ports not change during 073 execution. In addition, in some cases (namely systems with feedback) delays, 074 which are represented by initial tokens on relations must be explicitly 075 noted. SDF uses this rate and delay information to determine 076 the execution sequence of the actors before execution begins. 077 <h2>Schedule Properties</h2> 078 <ul> 079 <li>The number of tokens accumulated on every relation is bounded, given 080 an infinite number of executions of the schedule.</li> 081 <li>Deadlock will never occur, given an infinite number of executions of 082 the schedule.</li> 083 </ul> 084 <h1>Class comments</h1> 085 An SDFDirector is the class that controls execution of actors under the 086 SDF domain. By default, actor scheduling is handled by the SDFScheduler 087 class. Furthermore, the newReceiver method creates Receivers of type 088 SDFReceiver, which extends QueueReceiver to support optimized gets 089 and puts of arrays of tokens. 090 <p> 091 Actors are assumed to consume and produce exactly one token per channel on 092 each firing. Actors that do not follow this convention should set 093 the appropriate parameters on input and output ports to declare the number 094 of tokens they produce or consume. See the 095 {@link ptolemy.domains.sdf.kernel.SDFScheduler} for more information. 096 The {@link ptolemy.domains.sdf.lib.SampleDelay} actor is usually used 097 in a model to specify the delay across a relation. 098 </p><p> 099 The <i>allowDisconnectedGraphs</i> parameter of this director determines 100 whether disconnected graphs are permitted. 101 A model may have two or more graphs of actors that 102 are not connected. The schedule can jump from one graph to 103 another among the disconnected graphs. There is nothing to 104 force the scheduler to finish executing all actors on one 105 graph before firing actors on another graph. However, the 106 order of execution within an graph should be correct. 107 Usually, disconnected graphs in an SDF model indicates an 108 error. 109 The default value of the allowDisconnectedGraphs parameter is a 110 BooleanToken with the value false. 111 </p><p> 112 The <i>iterations</i> parameter of this director corresponds to a 113 limit on the number of times the director will fire its hierarchy 114 before it returns false in postfire. If this number is not greater 115 than zero, then no limit is set and postfire will always return true. 116 The default value of the iterations parameter is an IntToken with value one. 117 </p><p> 118 If any actor's postfire() method returns false during an iteration, 119 then at the conclusion of the iteration, this director's postfire() method 120 will return false. This will normally result in termination of the execution. 121 The reasoning for this behavior is that the model cannot continue executing 122 without the participation of all actors, and if any actor returns false 123 in postfire(), then it is indicating that it wishes to not continue executing. 124 </p><p> 125 The <i>vectorizationFactor</i> parameter of this director sets the number 126 of times that the basic schedule is executed during each firing of this 127 director. This might allow the director to execute the model more efficiently, 128 by combining multiple firings of each actor. The default value of the 129 vectorizationFactor parameter is an IntToken with value one. 130 </p><p> 131 The SDF director has a <i>period</i> parameter which specifies the 132 amount of model time that elapses per iteration. If the value of 133 <i>period</i> is 0.0 (the default), then it has no effect, and 134 this director never increments time nor calls fireAt() on the 135 enclosing director. If the period is greater than 0.0, then 136 if this director is at the top level, it increments 137 time by this amount in each invocation of postfire(). 138 If it is not at the top level, then it calls 139 fireAt(currentTime + period) in postfire(). 140 </p><p> 141 This behavior gives an interesting use of SDF within DE: 142 You can "kick start" an SDF submodel with a single 143 event, and then if the director of that SDF submodel 144 has a period greater than 0.0, then it will continue to fire 145 periodically with the specified period. 146 </p><p> 147 If <i>period</i> is greater than 0.0 and the parameter 148 <i>synchronizeToRealTime</i> is set to <code>true</code>, 149 then the prefire() method stalls until the real time elapsed 150 since the model started matches the period multiplied by 151 the iteration count. 152 This ensures that the director does not get ahead of real time. However, 153 of course, this does not ensure that the director keeps up with real time. 154 </p> 155 @see ptolemy.domains.sdf.kernel.SDFScheduler 156 @see ptolemy.domains.sdf.kernel.SDFReceiver 157 158 @author Steve Neuendorffer, Contributor: Christopher Brooks 159 @version $Id$ 160 @since Ptolemy II 0.2 161 @Pt.ProposedRating Yellow (cxh) 162 @Pt.AcceptedRating Yellow (cxh) 163 */ 164public class SDFDirector extends StaticSchedulingDirector 165 implements PeriodicDirector { 166 /** Construct a director in the default workspace with an empty string 167 * as its name. The director is added to the list of objects in 168 * the workspace. Increment the version number of the workspace. 169 * 170 * The SDFDirector will have a default scheduler of type SDFScheduler. 171 * @exception IllegalActionException If the name has a period in it, or 172 * the director is not compatible with the specified container. 173 * @exception NameDuplicationException If the container already contains 174 * an entity with the specified name. 175 */ 176 public SDFDirector() 177 throws IllegalActionException, NameDuplicationException { 178 super(); 179 _init(); 180 } 181 182 /** Construct a director in the workspace with an empty name. 183 * The director is added to the list of objects in the workspace. 184 * Increment the version number of the workspace. 185 * The SDFDirector will have a default scheduler of type SDFScheduler. 186 * 187 * @param workspace The workspace for this object. 188 * @exception IllegalActionException If the name has a period in it, or 189 * the director is not compatible with the specified container. 190 * @exception NameDuplicationException If the container already contains 191 * an entity with the specified name. 192 */ 193 public SDFDirector(Workspace workspace) 194 throws IllegalActionException, NameDuplicationException { 195 super(workspace); 196 _init(); 197 } 198 199 /** Construct a director in the given container with the given name. 200 * The container argument must not be null, or a 201 * NullPointerException will be thrown. 202 * If the name argument is null, then the name is set to the 203 * empty string. Increment the version number of the workspace. 204 * The SDFDirector will have a default scheduler of type 205 * SDFScheduler. 206 * 207 * @param container Container of the director. 208 * @param name Name of this director. 209 * @exception IllegalActionException If the director is not compatible 210 * with the specified container. May be thrown in a derived class. 211 * @exception NameDuplicationException If the container is not a 212 * CompositeActor and the name collides with an entity in the container. 213 */ 214 public SDFDirector(CompositeEntity container, String name) 215 throws IllegalActionException, NameDuplicationException { 216 super(container, name); 217 _init(); 218 } 219 220 /////////////////////////////////////////////////////////////////// 221 //// public variables //// 222 223 /////////////////////////////////////////////////////////////////// 224 //// parameters //// 225 226 /** A parameter representing whether disconnected graphs are 227 * permitted. A model may have two or more graphs of actors that 228 * are not connected. The schedule can jump from one graph to 229 * another among the disconnected graphs. There is nothing to 230 * force the scheduler to finish executing all actors on one 231 * graph before firing actors on another graph. However, the 232 * order of execution within an graph should be correct. 233 * Usually, disconnected graphs in an SDF model indicates an 234 * error. The default value is a BooleanToken with the value 235 * false. 236 */ 237 public Parameter allowDisconnectedGraphs; 238 239 /** A parameter representing whether dynamic rate changes are 240 * permitted. An SDF model may constructed such that the values 241 * of rate parameters are modified during the execution of the 242 * system. If this parameter is true, then such models are 243 * valid and this class dynamically computes a new schedule at 244 * runtime. If this parameter is false, then the SDF domain 245 * performs a static check to disallow such models. Note that in 246 * order to generate code from an SDF model, this parameter must 247 * be set to false. This is a boolean with default 248 * value false. 249 */ 250 public Parameter allowRateChanges; 251 252 /** If true, then buffer sizes are fixed according to the schedule, 253 * and attempts to write to the buffer that cause the buffer to 254 * exceed the schedule size result in an exception. This method 255 * works by setting the capacity of the receivers if the value is 256 * true. This parameter is a boolean that defaults to true. 257 */ 258 public Parameter constrainBufferSizes; 259 260 /** A Parameter representing the number of times that postfire may be 261 * called before it returns false. If the value is less than or 262 * equal to zero, then the execution will never return false in postfire, 263 * and thus the execution can continue forever. Note that the amount 264 * of data processed by the SDF model is a function of both this 265 * parameter and the value of parameter <i>vectorizationFactor</i>, since 266 * <i>vectorizationFactor</i> can influence the choice of schedule. 267 * 268 * <p>If the number of iterations is -1, which is the value of 269 * the AUTO choice in the UI, then if the container of the 270 * director is the the top level then one iteration will occur 271 * before postfire() returns false.</p> 272 * 273 * <p>If the number of iterations is -1 and and the container of 274 * the director is <b>not</b> at the top level then postfire() 275 * will always return true and execution will continue 276 * forever.</p> 277 * 278 * The default value is an IntToken with the value AUTO, which 279 * is -1. The UI has a second choice: UNBOUNDED, which is 0. 280 */ 281 public Parameter iterations; 282 283 /** The time period of each iteration. This parameter has type double 284 * and default value 0.0, which means that this director does not 285 * increment model time and does not request firings by calling 286 * fireAt() on any enclosing director. If the value is set to 287 * something greater than 0.0, then if this director is at the 288 * top level, it will increment model time by the specified 289 * amount in its postfire() method. If it is not at the top 290 * level, then it will call fireAt() on the enclosing executive 291 * director with the argument being the current time plus the 292 * specified period. 293 */ 294 public Parameter period; 295 296 /** Specify whether the execution should synchronize to the 297 * real time. This parameter has type boolean and defaults 298 * to false. If set to true, then this director stalls in the 299 * prefire() method until the elapsed real real time matches 300 * the product of the <i>period</i> parameter value and the 301 * iteration count. If the <i>period</i> parameter has value 302 * 0.0 (the default), then changing this parameter to true 303 * has no effect. 304 */ 305 public Parameter synchronizeToRealTime; 306 307 /** A Parameter representing the requested vectorization factor. 308 * The director will attempt to construct a schedule where each 309 * actor fires <i>vectorizationFactor</i> times more often than 310 * it would in a minimal schedule. This can allow actor executions 311 * to be grouped together, resulting in faster execution. This is 312 * more likely to be possible in graphs without tight feedback. 313 * This parameter must be a positive integer. 314 * The default value is an IntToken with the value one. 315 */ 316 public Parameter vectorizationFactor; 317 318 /** The value used to signify special behavior for the 319 * iterations parameter. 320 */ 321 public static final IntToken AUTO_INTTOKEN = new IntToken(-1); 322 323 /** The name of the AUTO iterations parameter choice: "AUTO". */ 324 public static final String AUTO_NAME = "AUTO"; 325 326 /** The UNBOUNDED iterations choice is equivalent to IntToken.ZERO. */ 327 public static final IntToken UNBOUNDED_INTTOKEN = IntToken.ZERO; 328 329 /** The name of the UNBOUNDED iterations parameter choice: "UNBOUNDED". */ 330 public static final String UNBOUNDED_NAME = "UNBOUNDED"; 331 332 /** The name of the iterations parameter: "iterations". */ 333 public static final String ITERATIONS_NAME = "iterations"; 334 335 /////////////////////////////////////////////////////////////////// 336 //// public methods //// 337 338 /** React to a change in an attribute. If the changed attribute 339 * matches a parameter of the director, then the corresponding 340 * local copy of the parameter value will be updated. 341 * @param attribute The changed parameter. 342 * @exception IllegalActionException If the parameter set is not valid. 343 */ 344 @Override 345 public void attributeChanged(Attribute attribute) 346 throws IllegalActionException { 347 // NOTE: Invalidate the schedules only if the values of these 348 // parameters have changed. 349 if (attribute == allowDisconnectedGraphs) { 350 Token token = allowDisconnectedGraphs.getToken(); 351 boolean newValue = ((BooleanToken) token).booleanValue(); 352 if (newValue != _allowDisconnectedGraphs) { 353 _allowDisconnectedGraphs = newValue; 354 invalidateSchedule(); 355 } 356 } else if (attribute == vectorizationFactor) { 357 Token token = vectorizationFactor.getToken(); 358 int newValue = ((IntToken) token).intValue(); 359 if (newValue != _vectorizationFactor) { 360 _vectorizationFactor = newValue; 361 invalidateSchedule(); 362 } 363 } 364 365 super.attributeChanged(attribute); 366 } 367 368 /** Clone the object into the specified workspace. The new object is 369 * <i>not</i> added to the directory of that workspace (you must do this 370 * yourself if you want it there). 371 * @param workspace The workspace for the cloned object. 372 * @exception CloneNotSupportedException Not thrown in this base class 373 * @return The new Attribute. 374 */ 375 @Override 376 public Object clone(Workspace workspace) throws CloneNotSupportedException { 377 SDFDirector newObject = (SDFDirector) super.clone(workspace); 378 379 // Subclasses may set this to null and handle this themselves. 380 try { 381 newObject._periodicDirectorHelper = new PeriodicDirectorHelper( 382 newObject); 383 } catch (IllegalActionException e) { 384 throw new CloneNotSupportedException( 385 "Failed to create PeriodicDirectorHelper."); 386 } 387 388 return newObject; 389 } 390 391 /** Create the SDF schedule for this director. 392 */ 393 @Override 394 public void createSchedule() throws IllegalActionException { 395 BaseSDFScheduler scheduler = (BaseSDFScheduler) getScheduler(); 396 397 if (scheduler == null) { 398 throw new IllegalActionException("Attempted to initialize " 399 + "SDF system with no scheduler"); 400 } 401 402 // force the schedule to be computed. 403 if (_debugging) { 404 _debug("### Schedule:"); 405 } 406 407 try { 408 Schedule schedule = scheduler.getSchedule(); 409 if (_debugging) { 410 _debug(schedule.toString()); 411 _debug("### End schedule"); 412 } 413 } catch (NotSchedulableException ex) { 414 // Capt. Robbins suggested that we show which actors are connected 415 // or disconnected at the top, rather than burying it. 416 throw ex; 417 } catch (Exception ex) { 418 throw new IllegalActionException(this, ex, 419 "Failed to compute schedule:"); 420 } 421 422 // Declare the dependencies of rate parameters of external 423 // ports. Note that this must occur after scheduling, since 424 // rate parameters are assumed to exist. 425 scheduler.declareRateDependency(); 426 } 427 428 /** Return the number of iterations. 429 * 430 * <p>The number of iterations returned depends on the value of 431 * the <i>iterations</i> parameter and whether the container 432 * of the director is at the top level. 433 * See the {@link #iterations} documentation for details.</p> 434 * 435 * <p>Code that uses SDFDirector should call getIterations() 436 * instead of directly referring to the value of the 437 * <i>iterations</i> parameter.</p> 438 * 439 * @return the number of iterations 440 * @exception IllegalActionException If thrown while getting the 441 * value of the iterations parameter. 442 */ 443 public int getIterations() throws IllegalActionException { 444 // See "SDF director iterations parameter default of 0 is unfriendly" 445 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5546 446 IntToken token = ((IntToken) iterations.getToken()); 447 int iterationsValue = 0; 448 if (token != null) { 449 iterationsValue = token.intValue(); 450 } 451 if (iterationsValue > 0) { 452 return iterationsValue; 453 } 454 // The director should call isEmbedded() 455 // instead of seeing whether the container's container is null. 456 // The reason for this is RunCompositeActor, where the container's 457 // container is not null, but you still want the model to behave 458 // as if it were at the top level... 459 if (!isEmbedded()) { 460 // The container of this director is at the toplevel 461 if (iterations.getToken().equals(AUTO_INTTOKEN)) { 462 return 1; 463 } 464 } 465 return 0; 466 } 467 468 /** Return the time value of the next iteration. 469 * If this director is at the top level, then the returned value 470 * is the current time plus the period. Otherwise, this method 471 * delegates to the executive director. 472 * @return The time of the next iteration. 473 * @exception IllegalActionException If time objects cannot be created. 474 */ 475 @Override 476 public Time getModelNextIterationTime() throws IllegalActionException { 477 if (!_isTopLevel()) { 478 return super.getModelNextIterationTime(); 479 } 480 try { 481 double periodValue = periodValue(); 482 483 if (periodValue > 0.0) { 484 return getModelTime().add(periodValue); 485 } else { 486 return getModelTime(); 487 } 488 } catch (IllegalActionException exception) { 489 // This should have been caught by now. 490 throw new InternalErrorException(exception); 491 } 492 } 493 494 /** Call super.fire() and reset the _prefire flag. 495 * @exception IllegalActionException Thrown by super class. 496 */ 497 @Override 498 public void fire() throws IllegalActionException { 499 _prefire = false; 500 super.fire(); 501 } 502 503 /** Request a firing of the given actor at the given absolute 504 * time, and return the time at which the specified will be 505 * fired. If the <i>period</i> is 0.0 and there is no enclosing 506 * director, then this method returns the current time. If 507 * the period is 0.0 and there is an enclosing director, then 508 * this method delegates to the enclosing director, returning 509 * whatever it returns. If the <i>period</i> is not 0.0, then 510 * this method checks to see whether the 511 * requested time is equal to the current time plus an integer 512 * multiple of the period. If so, it returns the requested time. 513 * If not, it returns current time plus the period. 514 * @param actor The actor scheduled to be fired. 515 * @param time The requested time. 516 * @param microstep The microstep (ignored by this director). 517 * @exception IllegalActionException If the operation is not 518 * permissible (e.g. the given time is in the past). 519 * @return Either the requested time or the current time plus the 520 * period. 521 */ 522 @Override 523 public Time fireAt(Actor actor, Time time, int microstep) 524 throws IllegalActionException { 525 if (_periodicDirectorHelper != null) { 526 return _periodicDirectorHelper.fireAt(actor, time); 527 } 528 return super.fireAt(actor, time); 529 } 530 531 /** Initialize the actors associated with this director and then 532 * set the iteration count to zero. The order in which the 533 * actors are initialized is arbitrary. In addition, if actors 534 * connected directly to output ports have initial production, 535 * then copy that initial production to the outside of the 536 * composite actor. 537 * @exception IllegalActionException If the initialize() method of 538 * one of the associated actors throws it, or if there is no 539 * scheduler. 540 */ 541 @Override 542 public void initialize() throws IllegalActionException { 543 544 super.initialize(); 545 _iterationCount = 0; 546 547 if (_periodicDirectorHelper != null) { 548 _periodicDirectorHelper.initialize(); 549 } 550 551 CompositeActor container = (CompositeActor) getContainer(); 552 553 for (Iterator ports = container.outputPortList().iterator(); ports 554 .hasNext();) { 555 IOPort port = (IOPort) ports.next(); 556 557 // Create external initial production. 558 int rate = DFUtilities.getTokenInitProduction(port); 559 560 for (int i = 0; i < port.getWidthInside(); i++) { 561 try { 562 for (int k = 0; k < rate; k++) { 563 if (port.hasTokenInside(i)) { 564 Token t = port.getInside(i); 565 566 if (_debugging) { 567 _debug(getName(), "transferring output from " 568 + port.getName()); 569 } 570 571 port.send(i, t); 572 } else { 573 throw new IllegalActionException(this, port, 574 "Port should produce " + rate 575 + " tokens, but there were only " 576 + k + " tokens available."); 577 } 578 } 579 } catch (NoTokenException ex) { 580 // this shouldn't happen. 581 throw new InternalErrorException(this, ex, null); 582 } 583 } 584 } 585 } 586 587 /** Return a new receiver consistent with the SDF domain. 588 * @return A new SDFReceiver. 589 */ 590 @Override 591 public Receiver newReceiver() { 592 return new SDFReceiver(); 593 } 594 595 /** Return the value of the period as a double. 596 * @return The value of the period as a double. 597 * @exception IllegalActionException If the period parameter 598 * cannot be evaluated 599 */ 600 @Override 601 public double periodValue() throws IllegalActionException { 602 return ((DoubleToken) period.getToken()).doubleValue(); 603 } 604 605 /** Check the input ports of the container composite actor (if there 606 * are any) to see whether they have enough tokens, and return true 607 * if they do. If there are no input ports, then also return true. 608 * Otherwise, return false. Note that this does not call prefire() 609 * on the contained actors. 610 * <p> 611 * This method also implements the functionality of 612 * <i>synchronizeToRealTime</i> by waiting for real time 613 * to elapse if the parameter value is true. 614 * @exception IllegalActionException If port methods throw it. 615 * @return true If all of the input ports of the container of this 616 * director have enough tokens. 617 */ 618 @Override 619 public boolean prefire() throws IllegalActionException { 620 // Set current time based on the enclosing model. 621 622 // If prefire returns true and prefire is called again 623 // without calling fire in between, 624 // which can happen when resourceScheduling is enabled, 625 // then return true again. Otherwise check prefire 626 // conditions. 627 if (_aspectsPresent && _prefire) { 628 return true; 629 } 630 _prefire = super.prefire(); 631 632 if (!_prefire) { 633 return false; 634 } 635 636 double periodValue = periodValue(); 637 boolean synchronizeValue = ((BooleanToken) synchronizeToRealTime 638 .getToken()).booleanValue(); 639 640 if (periodValue > 0.0 && synchronizeValue) { 641 int depth = 0; 642 try { 643 synchronized (this) { 644 while (true) { 645 long elapsedTime = elapsedTimeSinceStart(); 646 647 // NOTE: We assume that the elapsed time can be 648 // safely cast to a double. This means that 649 // the SDF domain has an upper limit on running 650 // time of Double.MAX_VALUE milliseconds. 651 double elapsedTimeInSeconds = elapsedTime / 1000.0; 652 double currentTime = getModelTime().getDoubleValue(); 653 654 if (currentTime <= elapsedTimeInSeconds) { 655 break; 656 } 657 658 long timeToWait = (long) ((currentTime 659 - elapsedTimeInSeconds) * 1000.0); 660 661 if (_debugging) { 662 _debug("Waiting for real time to pass: " 663 + timeToWait); 664 } 665 666 try { 667 // NOTE: The built-in Java wait() method 668 // does not release the 669 // locks on the workspace, which would block 670 // UI interactions and may cause deadlocks. 671 // SOLUTION: explicitly release read permissions. 672 if (timeToWait > 0) { 673 // Bug fix from J. S. Senecal: 674 // 675 // The problem was that sometimes, the 676 // method Object.wait(timeout) was called 677 // with timeout = 0. According to java 678 // documentation: 679 // 680 // " If timeout is zero, however, then 681 // real time is not taken into 682 // consideration and the thread simply 683 // waits until notified." 684 depth = _workspace.releaseReadPermission(); 685 wait(timeToWait); 686 } 687 } catch (InterruptedException ex) { 688 // Continue executing. 689 } 690 } 691 } 692 } finally { 693 if (depth > 0) { 694 _workspace.reacquireReadPermission(depth); 695 } 696 } 697 } 698 699 // Refuse to fire if the period is greater than zero and the current 700 // time is not a multiple of the period. 701 if (_periodicDirectorHelper != null 702 && !_periodicDirectorHelper.prefire()) { 703 if (_debugging) { 704 _debug("Current time is not a multiple of the period or the microstep is 0. Returning false.\n" 705 + "Current time: " + getModelTime() + " Period: " 706 + periodValue); 707 } 708 return false; 709 } 710 711 // Check to see whether the input ports have enough data. 712 TypedCompositeActor container = (TypedCompositeActor) getContainer(); 713 Iterator inputPorts = container.inputPortList().iterator(); 714 while (inputPorts.hasNext()) { 715 IOPort inputPort = (IOPort) inputPorts.next(); 716 717 // NOTE: If the port is a ParameterPort, then we should not 718 // insist on there being an input. 719 if (inputPort instanceof ParameterPort) { 720 continue; 721 } 722 723 int threshold = DFUtilities.getTokenConsumptionRate(inputPort); 724 725 if (_debugging) { 726 _debug("checking input " + inputPort.getFullName()); 727 _debug("Threshold = " + threshold); 728 } 729 730 for (int channel = 0; channel < inputPort.getWidth(); channel++) { 731 if (threshold > 0 && !inputPort.hasToken(channel, threshold)) { 732 if (_debugging) { 733 _debug("Port " + inputPort.getFullName() 734 + " does not have enough tokens: " + threshold 735 + " Prefire returns false."); 736 } 737 738 return false; 739 } 740 } 741 } 742 743 if (_debugging) { 744 _debug("Director prefire returns true."); 745 } 746 747 return true; 748 } 749 750 /** Preinitialize the actors associated with this director and 751 * compute the schedule. The schedule is computed during 752 * preinitialization so that hierarchical opaque composite actors 753 * can be scheduled properly, since the act of computing the 754 * schedule sets the rate parameters of the external ports. In 755 * addition, performing scheduling during preinitialization 756 * enables it to be present during code generation. The order in 757 * which the actors are preinitialized is arbitrary. 758 * @exception IllegalActionException If the preinitialize() method of 759 * one of the associated actors throws it. 760 */ 761 @Override 762 public void preinitialize() throws IllegalActionException { 763 super.preinitialize(); 764 createSchedule(); 765 } 766 767 /** Return false if the system has finished executing, either by 768 * reaching the iteration limit, or having an actor in the system return 769 * false in postfire. Increment the number of iterations. 770 * If the "iterations" parameter is greater than zero, then 771 * see if the limit has been reached. If so, return false. 772 * Otherwise return true if all of the fired actors since the last 773 * call to prefire returned true. 774 * If the <i>period</i> parameter is greater than 0.0, then 775 * if this director is at the top level, then increment time 776 * by the specified period, and otherwise request a refiring 777 * at the current time plus the period. 778 * @return True if the Director wants to be fired again in the 779 * future. 780 * @exception IllegalActionException If the iterations parameter 781 * does not contain a legal value. 782 */ 783 @Override 784 public boolean postfire() throws IllegalActionException { 785 int iterationsValue = getIterations(); 786 _iterationCount++; 787 788 if (iterationsValue > 0 && _iterationCount >= iterationsValue) { 789 _iterationCount = 0; 790 if (_debugging) { 791 _debug("Reached specified number of iterations: " 792 + iterationsValue); 793 } 794 return false; 795 } 796 797 boolean result = super.postfire(); 798 if (_periodicDirectorHelper != null) { 799 _periodicDirectorHelper.postfire(); 800 } 801 return result; 802 } 803 804 /** Return an array of suggested ModalModel directors to use with 805 * SDFDirector. The default director is HDFFSMDirector, which supports 806 * multirate actors and only allows state transitions on each iteration. 807 * This is the most safe director to use with SDF models. 808 * MultirateFSMDirector supports multirate actors and allows state 809 * transitions on each firing of the modal model. MultirateFSMDirector 810 * can be used with SDF if rate signatures for all the states in the 811 * modal model are same. If rate signatures change during an iteration, 812 * the SDFDirector will throw an exception. 813 * FSMDirector can be used with SDFDirector only when rate signatures 814 * for modal model are all 1. 815 * @return An array of suggested directors to be used with ModalModel. 816 * @see ptolemy.actor.Director#suggestedModalModelDirectors() 817 */ 818 @Override 819 public String[] suggestedModalModelDirectors() { 820 return new String[] { "ptolemy.domains.modal.kernel.FSMDirector", 821 "ptolemy.domains.modal.kernel.MultirateFSMDirector", 822 "ptolemy.domains.hdf.kernel.HDFFSMDirector" }; 823 } 824 825 /** Return true to indicate that a ModalModel under control 826 * of this director supports multirate firing. 827 * @return True indicating a ModalModel under control of this director 828 * supports multirate firing. 829 */ 830 @Override 831 public boolean supportMultirateFiring() { 832 return true; 833 } 834 835 /** Override the base class method to transfer enough tokens to 836 * complete an internal iteration. If there are not enough tokens, 837 * then throw an exception. If the port is not connected on the 838 * inside, or has a narrower width on the inside than on the outside, 839 * then consume exactly one token from the corresponding outside 840 * channels and discard it. Thus, a port connected on the outside 841 * but not on the inside can be used as a trigger for an SDF 842 * composite actor. 843 * 844 * @param port The port to transfer tokens from. 845 * @return True if data are transferred. 846 * @exception IllegalActionException If the port is not an opaque 847 * input port, or if there are not enough input tokens available. 848 */ 849 @Override 850 public boolean transferInputs(IOPort port) throws IllegalActionException { 851 if (!port.isInput() || !port.isOpaque()) { 852 throw new IllegalActionException(this, port, 853 "Attempted to transferInputs on a port is not an opaque" 854 + "input port."); 855 } 856 857 // The number of tokens depends on the schedule, so make sure 858 // the schedule is valid. 859 getScheduler().getSchedule(); 860 861 int rate = DFUtilities.getTokenConsumptionRate(port); 862 boolean wasTransferred = false; 863 864 for (int i = 0; i < port.getWidth(); i++) { 865 try { 866 if (i < port.getWidthInside()) { 867 for (int k = 0; k < rate; k++) { 868 if (port.hasToken(i)) { 869 Token t = port.get(i); 870 871 if (_debugging) { 872 _debug(getName(), "transferring input from " 873 + port.getName()); 874 } 875 876 port.sendInside(i, t); 877 wasTransferred = true; 878 } else { 879 throw new IllegalActionException(this, port, 880 "Port should consume " + rate 881 + " tokens, but there were only " 882 + k + " tokens available."); 883 } 884 } 885 } else if (port.isKnown(i)) { 886 // No inside connection to transfer tokens to. 887 // Tolerate an unknown input, but if it is known, then 888 // transfer the input token if there is one. 889 // In this case, consume one input token if there is one. 890 if (_debugging) { 891 _debug(getName(), 892 "Dropping single input from " + port.getName()); 893 } 894 895 if (port.hasToken(i)) { 896 port.get(i); 897 } 898 } 899 } catch (NoTokenException ex) { 900 // this shouldn't happen. 901 throw new InternalErrorException(this, ex, null); 902 } 903 } 904 905 return wasTransferred; 906 } 907 908 /** Override the base class method to transfer enough tokens to 909 * fulfill the output production rate. 910 * This behavior is required to handle the case of non-homogeneous 911 * opaque composite actors. The port argument must be an opaque 912 * output port. If any channel of the output port has no data, then 913 * that channel is ignored. 914 * 915 * @param port The port to transfer tokens from. 916 * @return True if data are transferred. 917 * @exception IllegalActionException If the port is not an opaque 918 * output port. 919 */ 920 @Override 921 public boolean transferOutputs(IOPort port) throws IllegalActionException { 922 if (_debugging) { 923 _debug("Calling transferOutputs on port: " + port.getFullName()); 924 } 925 926 if (!port.isOutput() || !port.isOpaque()) { 927 throw new IllegalActionException(this, port, 928 "Attempted to transferOutputs on a port that " 929 + "is not an opaque output port."); 930 } 931 932 int rate = DFUtilities.getTokenProductionRate(port); 933 boolean wasTransferred = false; 934 935 for (int i = 0; i < port.getWidthInside(); i++) { 936 try { 937 for (int k = 0; k < rate; k++) { 938 if (port.hasTokenInside(i)) { 939 Token t = port.getInside(i); 940 941 if (_debugging) { 942 _debug(getName(), "transferring output from " 943 + port.getName()); 944 } 945 946 port.send(i, t); 947 wasTransferred = true; 948 } else { 949 throw new IllegalActionException(this, port, 950 "Port should produce " + rate 951 + " tokens, but there were only " + k 952 + " tokens available."); 953 } 954 } 955 } catch (NoTokenException ex) { 956 // this shouldn't happen. 957 throw new InternalErrorException(this, ex, null); 958 } 959 } 960 961 return wasTransferred; 962 } 963 964 /////////////////////////////////////////////////////////////////// 965 //// protected variables //// 966 967 /** The iteration count. */ 968 protected int _iterationCount = 0; 969 970 /** Helper class supporting the <i>period</i> parameter. */ 971 protected PeriodicDirectorHelper _periodicDirectorHelper; 972 973 /////////////////////////////////////////////////////////////////// 974 //// private methods //// 975 976 /** Initialize the object. In this case, we give the SDFDirector a 977 * default scheduler of the class SDFScheduler, an iterations 978 * parameter and a vectorizationFactor parameter. 979 */ 980 private void _init() 981 throws IllegalActionException, NameDuplicationException { 982 983 // AUTO and UNBOUNDED are used to set the value of iterations, 984 // see the getIterations() method. 985 986 Parameter AUTO = new Parameter(this, AUTO_NAME); 987 AUTO.setToken(AUTO_INTTOKEN); 988 AUTO.setVisibility(Settable.EXPERT); 989 AUTO.setPersistent(false); 990 991 Parameter UNBOUNDED = new Parameter(this, UNBOUNDED_NAME); 992 UNBOUNDED.setToken(UNBOUNDED_INTTOKEN); 993 UNBOUNDED.setVisibility(Settable.EXPERT); 994 UNBOUNDED.setPersistent(false); 995 996 iterations = new Parameter(this, ITERATIONS_NAME); 997 iterations.setTypeEquals(BaseType.INT); 998 iterations.addChoice(AUTO_NAME); 999 iterations.addChoice(UNBOUNDED_NAME); 1000 iterations.setExpression(AUTO_NAME); 1001 1002 vectorizationFactor = new Parameter(this, "vectorizationFactor"); 1003 vectorizationFactor.setTypeEquals(BaseType.INT); 1004 vectorizationFactor.setExpression("1"); 1005 1006 allowDisconnectedGraphs = new Parameter(this, 1007 "allowDisconnectedGraphs"); 1008 allowDisconnectedGraphs.setTypeEquals(BaseType.BOOLEAN); 1009 allowDisconnectedGraphs.setExpression("false"); 1010 1011 allowRateChanges = new Parameter(this, "allowRateChanges"); 1012 allowRateChanges.setTypeEquals(BaseType.BOOLEAN); 1013 allowRateChanges.setExpression("false"); 1014 1015 constrainBufferSizes = new Parameter(this, "constrainBufferSizes"); 1016 constrainBufferSizes.setTypeEquals(BaseType.BOOLEAN); 1017 constrainBufferSizes.setExpression("true"); 1018 1019 period = new Parameter(this, "period", new DoubleToken(1.0)); 1020 period.setTypeEquals(BaseType.DOUBLE); 1021 period.setExpression("0.0"); 1022 1023 synchronizeToRealTime = new Parameter(this, "synchronizeToRealTime"); 1024 synchronizeToRealTime.setExpression("false"); 1025 synchronizeToRealTime.setTypeEquals(BaseType.BOOLEAN); 1026 1027 startTime.moveToLast(); 1028 stopTime.moveToLast(); 1029 1030 SDFScheduler scheduler = new SDFScheduler(this, 1031 uniqueName("Scheduler")); 1032 scheduler.constrainBufferSizes.setExpression("constrainBufferSizes"); 1033 setScheduler(scheduler); 1034 1035 // Subclasses may set this to null and handle this themselves. 1036 _periodicDirectorHelper = new PeriodicDirectorHelper(this); 1037 } 1038 1039 /////////////////////////////////////////////////////////////////// 1040 //// package friendly variables //// 1041 1042 /** Cache of the value of allowDisconnectedGraphs. */ 1043 boolean _allowDisconnectedGraphs = false; 1044 1045 /////////////////////////////////////////////////////////////////// 1046 //// private variables //// 1047 1048 /** Cache of the most recent value of vectorizationFactor. */ 1049 private int _vectorizationFactor = 1; 1050 1051}