001/* A baseclass for directors in process oriented domains that 002 incorporates hierarchical, heterogeneity. 003 004 Copyright (c) 1998-2014 The Regents of the University of California. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 */ 029package ptolemy.actor.process; 030 031import java.util.HashSet; 032import java.util.Iterator; 033 034import ptolemy.actor.Actor; 035import ptolemy.actor.CompositeActor; 036import ptolemy.actor.Director; 037import ptolemy.actor.IOPort; 038import ptolemy.actor.Receiver; 039import ptolemy.actor.util.Time; 040import ptolemy.kernel.CompositeEntity; 041import ptolemy.kernel.util.IllegalActionException; 042import ptolemy.kernel.util.InternalErrorException; 043import ptolemy.kernel.util.InvalidStateException; 044import ptolemy.kernel.util.NameDuplicationException; 045import ptolemy.kernel.util.Workspace; 046 047/////////////////////////////////////////////////////////////////// 048//// CompositeProcessDirector 049 050/** 051 A baseclass for directors in process oriented domains that incorporate 052 hierarchical heterogeneity. As with ProcessDirector 053 CompositeProcessDirectors need to keep a count of the number of active 054 processes and the number of processes that are blocked for any reason 055 (e.g., trying to read from an empty channel in PN). 056 CompositeProcessDirector is a subclass of ProcessDirector to facilitate 057 models that consist of non-atomic actors. 058 <P> 059 A composite process director can be contained by an opaque composite 060 actor that is contained by a composite actor. Ports contained by opaque 061 composite actors are called opaque ports and such ports facilitate data 062 transfer across the composite actor boundaries. A composite process 063 director allocates two branch controllers to monitor data transfer in 064 channels associated with opaque ports. The <I>input</I> branch controller 065 monitors data transfer for channels associated with input opaque ports. 066 The <I>output</I> branch controller monitors data transfer for channels 067 associated with output opaque ports. 068 <P> 069 Associated with the channels of each opaque port is a pair of process 070 receivers. The <I>producer receiver</I> serves as the channel source 071 and the <I>consumer receiver</I> serves as the channel destination. 072 Each branch controller allocates a branch for each process receiver 073 pair and when executing, a branch repeatedly attempts to transfer a 074 single token from its producer receiver to its consumer receiver. 075 <P> 076 When a branch blocks while attempting to transfer data, it informs its 077 branch controller by passing the branch controller the blocked receiver. 078 If all of the branches of a controller have blocked, then we say that 079 the branch controller is blocked and the branch controller informs the 080 composite process director. In addition to monitoring the status of its 081 branch controllers, a composite process director keeps track of the 082 state of the actors that it contains. Actors can be internally or 083 externally blocked. We say that an actor is externally blocked if it 084 is blocked waiting to transfer tokens to or from a boundary port of 085 its container actor. Actors that are blocked but not externally are 086 said to be internally blocked. 087 <P> 088 Composite process directors monitor the state of the branch controllers 089 and contained actors and when necessary invoke the _resolveDeadlock() 090 method to deal with deadlocks. In the remainder of this paragraph we 091 consider the case of a process-oriented opaque composite actor that is 092 contained by another process-oriented opaque composite actor. If the 093 actors contained by the inner composite actor are not blocked, then 094 execution of the inner composite actor is allowed to continue 095 independent of the state of the branch controllers. If the actors 096 contained by the inner composite actor are internally blocked, then 097 after the branch controllers have been deactivated, execution of the 098 composite actor ends and postfire returns false indicating that 099 successive iterations are not allowed. If the actors contained by the 100 inner composite actor are externally blocked, then the composite 101 process director waits until the branch controllers block (an 102 inevitable condition) and registers the block with the containing 103 (outer) composite director of the actor. 104 <P> 105 In this paragraph we consider the case of a process-oriented opaque 106 composite actor that is contained by a schedule-oriented (non process) 107 opaque composite actor. If the actors contained by the inner composite 108 actor are not blocked, then execution of the inner composite actor is 109 allowed to continue independent of the state of the branch controllers. 110 If the actors contained by the inner composite actor are internally 111 blocked, then after the branch controllers have been deactivated, 112 execution of the composite actor ends and postfire returns false 113 indicating that successive iterations are not allowed. If the actors 114 contained by the inner composite actor are externally blocked, then 115 the composite process director waits until the branch controllers 116 block (an inevitable condition) and ends the iteration with postfire() 117 returning true indicating that successive iterations are allowed. 118 119 @author John S. Davis II 120 @version $Id$ 121 @since Ptolemy II 1.0 122 @Pt.ProposedRating Green (mudit) 123 @Pt.AcceptedRating Yellow (davisj) 124 @see Director 125 */ 126public class CompositeProcessDirector extends ProcessDirector { 127 /** Construct a director in the default workspace with an empty 128 * string as its name. The director is added to the list of 129 * objects in the workspace. Increment the version number of 130 * the workspace. 131 * @exception NameDuplicationException If construction of Time objects fails. 132 * @exception IllegalActionException If construction of Time objects fails. 133 */ 134 public CompositeProcessDirector() 135 throws IllegalActionException, NameDuplicationException { 136 super(); 137 } 138 139 /** Construct a director in the workspace with an empty name. 140 * The director is added to the list of objects in the workspace. 141 * Increment the version number of the workspace. 142 * 143 * @param workspace The workspace of this object. 144 * @exception NameDuplicationException If construction of Time objects fails. 145 * @exception IllegalActionException If construction of Time objects fails. 146 */ 147 public CompositeProcessDirector(Workspace workspace) 148 throws IllegalActionException, NameDuplicationException { 149 super(workspace); 150 } 151 152 /** Construct a director in the given container with the given name. 153 * If the container argument must not be null, or a 154 * NullPointerException will be thrown. If the name argument is null, 155 * then the name is set to the empty string. Increment the version 156 * number of the workspace. 157 * @param container The container. 158 * @param name Name of this director. 159 * @exception IllegalActionException If the name contains a period, 160 * or if the director is not compatible with the specified container. 161 * @exception NameDuplicationException If the container not a 162 * CompositeActor and the name collides with an entity in the container. 163 */ 164 public CompositeProcessDirector(CompositeEntity container, String name) 165 throws IllegalActionException, NameDuplicationException { 166 super(container, name); 167 } 168 169 /////////////////////////////////////////////////////////////////// 170 //// public methods //// 171 172 /** Clone the director into the specified workspace. The new object is 173 * <i>not</i> added to the directory of that workspace (It must be added 174 * by the user if he wants it to be there). 175 * The result is a new director with no container, no pending mutations, 176 * and no topology listeners. The count of active processes is zero. 177 * @param workspace The workspace for the cloned object. 178 * @exception CloneNotSupportedException If one of the attributes 179 * cannot be cloned. 180 * @return The new ProcessDirector. 181 */ 182 @Override 183 public Object clone(Workspace workspace) throws CloneNotSupportedException { 184 CompositeProcessDirector newObj = (CompositeProcessDirector) super.clone( 185 workspace); 186 newObj._onFirstIteration = true; 187 newObj._inputBranchController = null; 188 newObj._outputBranchController = null; 189 190 // Findbugs: 191 // [M M IS] Inconsistent synchronization [IS2_INCONSISTENT_SYNC] 192 // Actually this is not a problem since the object is 193 // being created and hence nobody else has access to it. 194 195 newObj._blockedReceivers = new HashSet(); 196 newObj._branchControllerLock = new Object(); 197 return newObj; 198 } 199 200 /** Create a input and/or output branch controllers according to 201 * whether the ports passed in as arguments are input or output 202 * ports. If any of the ports are input (output) ports, then they 203 * will be added to the input (output) branch controller. 204 * @param ports The ports for which branches will be assigned. 205 * @exception IllegalActionException If any of the ports are 206 * not opaque. 207 */ 208 public void createBranchController(Iterator ports) 209 throws IllegalActionException { 210 IOPort port = null; 211 212 while (ports.hasNext()) { 213 port = (IOPort) ports.next(); 214 215 if (!port.isOpaque()) { 216 throw new IllegalActionException(this, port, 217 "port argument is not an opaque port."); 218 } 219 220 if (port.isInput()) { 221 _inputBranchController.addBranches(port); 222 } 223 224 if (port.isOutput()) { 225 _outputBranchController.addBranches(port); 226 } 227 } 228 } 229 230 /** Return the input branch controller of this director. If 231 * this method is called prior to the invocation of 232 * initialize(), then this method will return null. 233 * @return The input branch controller of this director. 234 */ 235 public BranchController getInputController() { 236 return _inputBranchController; 237 } 238 239 /** Return the output branch controller of this director. If 240 * this method is called prior to the invocation of 241 * initialize(), then this method will return null. 242 * @return The output branch controller of this director. 243 */ 244 public BranchController getOutputController() { 245 return _outputBranchController; 246 } 247 248 /** Invoke the initialize() methods of all the deeply contained 249 * actors in the container (a composite actor) of this director. 250 * These are expected to call initialize(Actor), which will 251 * result in the creation of a new thread for each actor. 252 * Also, set current time to 0.0, or to the current time of 253 * the executive director of the container, if there is one. 254 * 255 * @exception IllegalActionException If the initialize() method 256 * of one of the deeply contained actors throws it. 257 */ 258 @Override 259 public void initialize() throws IllegalActionException { 260 CompositeActor container = (CompositeActor) getContainer(); 261 262 if (container != null) { 263 CompositeActor containersContainer = (CompositeActor) container 264 .getContainer(); 265 266 if (containersContainer == null) { 267 // Use the overridden setCurrentTime() method 268 // to set time backwards. 269 setModelTime(new Time(this)); 270 } else { 271 Time currentTime = containersContainer.getDirector() 272 .getModelTime(); 273 274 // Use the overridden setCurrentTime() method 275 // to set time backwards. 276 setModelTime(currentTime); 277 } 278 } 279 resume(); 280 281 _blockedReceivers.clear(); 282 283 _inputBranchController = new BranchController(container); 284 _outputBranchController = new BranchController(container); 285 286 // Instantiate Input/Output Branch Controllers 287 if (container != null) { 288 Iterator ports = container.portList().iterator(); 289 createBranchController(ports); 290 } 291 292 _inputControllerIsBlocked = _inputBranchController.isBlocked(); 293 _outputControllerIsBlocked = _outputBranchController.isBlocked(); 294 295 // Make sure we initialize the actors AFTER creating the 296 // branch controllers, otherwise initial values will break the 297 // model. 298 super.initialize(); 299 } 300 301 /** Return a new receiver of a type compatible with this director. 302 * In this base class, this returns an instance of 303 * MailboxBoundaryReceiver. 304 * 305 * @return A new MailboxBoundaryReceiver. 306 */ 307 @Override 308 public Receiver newReceiver() { 309 return new MailboxBoundaryReceiver(); 310 } 311 312 /** If there are input or output ports, and this is the first iteration, 313 * then start threads to handle the inputs and outputs. 314 * @return True. 315 * @exception IllegalActionException If a derived class throws it. 316 */ 317 @Override 318 public boolean prefire() throws IllegalActionException { 319 super.prefire(); 320 321 Thread thread = null; 322 323 // FIXME: This will not support dynamically changing 324 // connections on the outside of a composite. 325 if (_inputBranchController.hasBranches() && _onFirstIteration) { 326 thread = new Thread(_inputBranchController); 327 thread.start(); 328 } 329 330 if (_outputBranchController.hasBranches() && _onFirstIteration) { 331 thread = new Thread(_outputBranchController); 332 thread.start(); 333 } 334 335 _onFirstIteration = false; 336 return true; 337 } 338 339 /** Stop the input branch controller of this director. This 340 * method will block until the input branch controller 341 * has stopped due to all of the branches it controls 342 * stopping, or until the calling thread is interrupted. 343 */ 344 public void stopInputBranchController() { 345 Workspace workspace = workspace(); 346 347 if (_inputBranchController == null) { 348 // This happens under DDE Zeno under IE 5 with Java Plug-in 1.3 349 return; 350 } 351 352 if (!_inputBranchController.hasBranches()) { 353 return; 354 } 355 356 _inputBranchController.deactivateBranches(); 357 358 while (!_inputBranchController.isBlocked()) { 359 try { 360 workspace.wait(this); 361 } catch (InterruptedException e) { 362 // Exit the loop. 363 // FIXME: Is this the right thing to do? 364 break; 365 } 366 } 367 } 368 369 /** Stop the output branch controller of this director. This 370 * method will block until the output branch controller 371 * has stopped due to all of the branches it controls 372 * stopping. 373 */ 374 public void stopOutputBranchController() { 375 Workspace workspace = workspace(); 376 377 if (_outputBranchController == null) { 378 return; 379 } 380 381 if (!_outputBranchController.hasBranches()) { 382 return; 383 } 384 385 _outputBranchController.deactivateBranches(); 386 387 while (!_outputBranchController.isBlocked()) { 388 try { 389 workspace.wait(this); 390 } catch (InterruptedException e) { 391 // Exit the loop. 392 // FIXME: Is this the right thing to do? 393 break; 394 } 395 } 396 } 397 398 /** Notify the director that the specified thread is blocked 399 * on an I/O operation. If the thread has 400 * not been registered with addThread(), then this call is 401 * ignored. This overrides the base class to keep track of 402 * the receiver in case it is on the boundary of the 403 * containing composite actor. 404 * @param thread The thread. 405 * @param receiver The receiver handling the I/O operation, 406 * or null if it is not a specific receiver. 407 * @see #addThread(Thread) 408 */ 409 @Override 410 public synchronized void threadBlocked(Thread thread, 411 ProcessReceiver receiver) { 412 // In case the receiver is on the boundary, add this to the 413 // blocked receivers list. 414 if (receiver != null) { 415 _blockedReceivers.add(receiver); 416 } 417 418 super.threadBlocked(thread, receiver); 419 } 420 421 /** Notify the director that the specified thread is unblocked 422 * on an I/O operation. If the thread has 423 * not been registered with threadBlocked(), then this call is 424 * ignored. This overrides the base class to keep track of 425 * the receiver in case it is on the boundary of the 426 * containing composite actor. 427 * @param thread The thread. 428 * @param receiver The receiver handling the I/O operation, 429 * or null if it is not a specific receiver. 430 * @see #threadBlocked(Thread, ProcessReceiver) * 431 */ 432 @Override 433 public synchronized void threadUnblocked(Thread thread, 434 ProcessReceiver receiver) { 435 // In case the receiver is on the boundary, add this to the 436 // blocked receivers list. 437 if (receiver != null) { 438 _blockedReceivers.remove(receiver); 439 } 440 441 super.threadUnblocked(thread, receiver); 442 } 443 444 /** End the execution of the model under the control of this 445 * director. A flag is set in all of the receivers that causes 446 * each process to terminate at the earliest communication point. 447 * <P> 448 * Prior to setting receiver flags, this method wakes up the 449 * threads if they all are stopped. 450 * <P> 451 * This method is not synchronized on the workspace, so the 452 * caller should be. 453 * 454 * @exception IllegalActionException If an error occurs while 455 * accessing the receivers of all actors under the control of 456 * this director. 457 */ 458 @Override 459 public void wrapup() throws IllegalActionException { 460 // Kill all branch controllers. 461 stopInputBranchController(); 462 stopOutputBranchController(); 463 464 if (_debugging) { 465 _debug("Finished deactivating branches."); 466 } 467 468 super.wrapup(); 469 } 470 471 /////////////////////////////////////////////////////////////////// 472 //// protected methods //// 473 474 /** Return true if one or more contained actor is externally 475 * blocked; return false otherwise. We say an actor is 476 * externally blocked if it is blocked attempting data transfer 477 * through a boundary port of its containing actor. Note that 478 * a true return value for this method does not imply that the 479 * contained actors are deadlocked. 480 * 481 * @return true If one or more contained actors are externally 482 * blocked; return false otherwise. 483 * @exception IllegalActionException 484 * @exception InvalidStateException 485 */ 486 protected boolean _areActorsExternallyBlocked() 487 throws InvalidStateException, IllegalActionException { 488 Iterator blockedReceivers = _blockedReceivers.iterator(); 489 490 while (blockedReceivers.hasNext()) { 491 ProcessReceiver receiver = (ProcessReceiver) blockedReceivers 492 .next(); 493 494 // FIXME: This seems like a kludgy way to do this... 495 // The receiver should only be added to the list if 496 // it is on the boundary! Perhaps this is more efficient? 497 if (receiver.isConnectedToBoundaryInside()) { 498 return true; 499 } 500 } 501 502 return false; 503 } 504 505 /** Return false if the number of blocked processes is less than 506 * the number of active actors; return true otherwise. Note that 507 * if the number of active actors is 0 then this method will 508 * return true. Derived classes may override this method to add 509 * domain specific functionality. Implementations of this method 510 * must be synchronized. 511 * 512 * @return false If the number of blocked processes is less than 513 * the number of active actors; return true otherwise. 514 */ 515 @Override 516 protected synchronized boolean _areThreadsDeadlocked() { 517 if (_debugging) { 518 _debug("Checking for deadlock:"); 519 _debug("There are " + _getBlockedThreadsCount() 520 + " Blocked actors, " + _getStoppedThreadsCount() 521 + " Stopped actors, and " + _getActiveThreadsCount() 522 + " active threads."); 523 } 524 525 if (_getBlockedThreadsCount() >= _getActiveThreadsCount()) { 526 return true; 527 } else { 528 return false; 529 } 530 } 531 532 /** Register that the specified controller is blocked. Pass the 533 * specified controller in as an argument. Note that if the 534 * controller passed in as an argument is not contained by this 535 * director or if the state of the controller is not blocked 536 * then no registration operation will be performed by this 537 * method. 538 * 539 * @param controller The controller for which registration of a 540 * blocked state will occur. 541 */ 542 protected synchronized void _controllerBlocked( 543 BranchController controller) { 544 if (controller == _inputBranchController) { 545 _inputControllerIsBlocked = controller.isBlocked(); 546 } 547 548 if (controller == _outputBranchController) { 549 _outputControllerIsBlocked = controller.isBlocked(); 550 } 551 552 notifyAll(); 553 } 554 555 /** Unregister the specified controller as being no longer 556 * blocked. Pass the specified controller in as an argument. 557 * Note that if the controller passed in as an argument is 558 * not contained by this director or if the state of the 559 * controller is blocked then no registration operation will 560 * be performed by this method. 561 * 562 * @param controller The controller for which registration of an 563 * unblocked state will occur. 564 */ 565 protected void _controllerUnBlocked(BranchController controller) { 566 synchronized (_branchControllerLock) { 567 if (controller == _inputBranchController) { 568 _inputControllerIsBlocked = controller.isBlocked(); 569 } 570 571 if (controller == _outputBranchController) { 572 _outputControllerIsBlocked = controller.isBlocked(); 573 } 574 } 575 } 576 577 /** Return true if the input controller of this director is 578 * blocked; return false otherwise. 579 * 580 * @return true If the input controller of this director is 581 * blocked; return false otherwise. 582 */ 583 protected synchronized boolean _isInputControllerBlocked() { 584 return _inputControllerIsBlocked; 585 } 586 587 /** Return true if the output controller of this director is 588 * blocked; return false otherwise. 589 * 590 * @return true If the output controller of this director is 591 * blocked; return false otherwise. 592 */ 593 protected synchronized boolean _isOutputControllerBlocked() { 594 return _outputControllerIsBlocked; 595 } 596 597 /** Attempt to resolve a deadlock and return true if the deadlock 598 * no longer exists and successive iterations are allowed; if 599 * the deadlock still exists then return false indicating that 600 * future iterations are not allowed. If the deadlock is internal 601 * then apply a domain specific algorithm to attempt deadlock 602 * resolution via the _resolveInternalDeadlock() method. If the 603 * algorithm is successful and deadlock no longer exists then 604 * return true. If the algorithm is unsuccessful and deadlock 605 * persists then end the iteration and return false. 606 * <P> 607 * If the deadlock is an external deadlock and the containing model 608 * of computation is process-oriented, then register the externally 609 * blocked receivers with the composite actor that contains this 610 * director's composite actor. If the deadlock is an external 611 * deadlock and the containing model of computation is 612 * schedule-oriented, then end this iteration and return true. 613 * <P> 614 * While in special cases it my be useful to override this method 615 * for domain specific functionality it is more likely that this 616 * method will remain the same and the _resolveInternalDeadlock() 617 * method will be overridden for particular models of computation. 618 * 619 * @return false If deadlock could not be resolved and successive 620 * iterations are not allowed; return true otherwise. 621 * @exception IllegalActionException Not thrown in this base class. 622 */ 623 @Override 624 protected boolean _resolveDeadlock() throws IllegalActionException { 625 if (_debugging) { 626 _debug("Resolving Deadlock"); 627 } 628 629 Director execDir = ((Actor) getContainer()).getExecutiveDirector(); 630 Workspace workspace = workspace(); 631 632 int depth = 0; 633 try { 634 synchronized (this) { 635 if (_areThreadsDeadlocked()) { 636 if (_areActorsExternallyBlocked()) { 637 // There are actors that are blocked on a communication 638 // (send or receive) to the outside world. 639 if (_inputBranchController.isBlocked()) { 640 while (!_outputBranchController.isBlocked()) { 641 try { 642 // NOTE: We cannot use workspace.wait(Object) here without 643 // introducing a race condition, because we have to release 644 // the lock on the _director before calling workspace.wait(_director). 645 if (depth == 0) { 646 depth = workspace 647 .releaseReadPermission(); 648 } 649 wait(); 650 } catch (InterruptedException e) { 651 // TODO: determine best way to handle the exception 652 throw new IllegalActionException(this, 653 "Interrupted."); 654 } 655 } 656 657 stopInputBranchController(); 658 stopOutputBranchController(); 659 660 if (execDir == null) { 661 // This is the top level director - problem!!! 662 throw new IllegalActionException(this, 663 "No executive director exists yet this " 664 + "director's composite actor is externally " 665 + "deadlocked."); 666 } else if (execDir instanceof CompositeProcessDirector) { 667 // This is contained by a process-oriented MoC 668 ((CompositeProcessDirector) execDir) 669 .threadBlocked(Thread.currentThread(), 670 null); 671 return true; 672 } else { 673 // This is contained by a schedule-oriented MoC 674 return true; 675 } 676 } else if (_outputBranchController.isBlocked()) { 677 stopInputBranchController(); 678 stopOutputBranchController(); 679 680 if (execDir == null) { 681 // This is the top level director - problem!!! 682 throw new IllegalActionException(this, 683 "No executive director exists yet this " 684 + "director's composite actor is externally " 685 + "deadlocked."); 686 } else if (execDir instanceof CompositeProcessDirector) { 687 // This is contained by a process-oriented MoC 688 ((CompositeProcessDirector) execDir) 689 .threadBlocked(Thread.currentThread(), 690 null); 691 return true; 692 } else { 693 // This is contained by a schedule-oriented MoC 694 return true; 695 } 696 } 697 } else { 698 // There are no actors that are blocked on a communication 699 // (send or receive) to the outside world. 700 if (_inputBranchController == null) { 701 throw new InternalErrorException(this, null, 702 "_inputBranchController was null? Perhaps initialize() " 703 + "was not called?"); 704 } 705 if (_inputBranchController.isBlocked()) { 706 while (!_outputBranchController.isBlocked()) { 707 try { 708 // NOTE: We cannot use workspace.wait(Object) here without 709 // introducing a race condition, because we have to release 710 // the lock on the _director before calling workspace.wait(_director). 711 if (depth == 0) { 712 depth = workspace 713 .releaseReadPermission(); 714 } 715 wait(); 716 } catch (InterruptedException e) { 717 // TODO: determine best way to handle the exception 718 throw new IllegalActionException(this, 719 "Interrupted."); 720 } 721 } 722 723 stopInputBranchController(); 724 stopOutputBranchController(); 725 return _resolveInternalDeadlock(); 726 } else if (_outputBranchController.isBlocked()) { 727 stopInputBranchController(); 728 stopOutputBranchController(); 729 return _resolveInternalDeadlock(); 730 } else { 731 while (!_outputBranchController.isBlocked()) { 732 try { 733 // NOTE: We cannot use workspace.wait(Object) here without 734 // introducing a race condition, because we have to release 735 // the lock on the _director before calling workspace.wait(_director). 736 if (depth == 0) { 737 depth = workspace 738 .releaseReadPermission(); 739 } 740 wait(); 741 } catch (InterruptedException e) { 742 //TODO: determine best way to handle the exception 743 throw new IllegalActionException(this, 744 "Interrupted."); 745 } 746 } 747 748 stopInputBranchController(); 749 stopOutputBranchController(); 750 return _resolveInternalDeadlock(); 751 } 752 } 753 } 754 } // synchronized(this) 755 } finally { 756 // This has to happen outside the synchronized block. 757 if (depth > 0) { 758 workspace.reacquireReadPermission(depth); 759 } 760 } 761 762 return false; 763 } 764 765 /** Return false indicating that resolution of an internal 766 * deadlock was unsuccessful and execution should discontinue. 767 * Subclasses may override this method for domain specific 768 * functionality. Domain specific functionality should 769 * include algorithms to resolve internal deadlock. 770 * Successful application of the algorithm should result in 771 * a return value of true; unsuccessful application should 772 * result in a return value of false. 773 * 774 * @return False indicating that internal deadlock was not 775 * resolved. 776 * @exception IllegalActionException Not thrown in this base class. 777 */ 778 protected boolean _resolveInternalDeadlock() throws IllegalActionException { 779 if (_debugging) { 780 _debug("Failed To Resolve Internal Deadlock: stopping"); 781 } 782 783 return false; 784 } 785 786 /////////////////////////////////////////////////////////////////// 787 //// private variables //// 788 789 /** Flag indicating whether we have executed the first iteration. */ 790 private boolean _onFirstIteration = true; 791 792 /** The controller that handles inputs to the composite. */ 793 private BranchController _inputBranchController; 794 795 /** The controller that handles outputs from the composite. */ 796 private BranchController _outputBranchController; 797 798 private volatile boolean _inputControllerIsBlocked = true; 799 800 private volatile boolean _outputControllerIsBlocked = true; 801 802 private HashSet _blockedReceivers = new HashSet(); 803 804 private Object _branchControllerLock = new Object(); 805}