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}