001/* A BranchController manages the execution of a set of branch objects by
002 monitoring whether the branches have blocked.
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
029 */
030package ptolemy.actor.process;
031
032import java.util.Iterator;
033import java.util.LinkedList;
034
035import ptolemy.actor.CompositeActor;
036import ptolemy.actor.IOPort;
037import ptolemy.actor.Receiver;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.Nameable;
040
041///////////////////////////////////////////////////////////////////
042//// BranchController
043
044/**
045 A BranchController manages the execution of a set of branch objects by
046 monitoring whether the branches have blocked. A branch blocks when it is
047 either unable to get data from its producer receiver or put data into its
048 consumer receiver. When a branch blocks, it registers the block with its
049 branch controller by passing the specific receiver that is blocked. If all
050 of a branch controllers branches are blocked, then the branch controller
051 informs the director associated with its containing composite actors.
052 <P>
053 Branches are assigned to a branch controller by the director associated
054 with the controller's composite actor via the addBranches() method. This
055 method takes an io port and determines the port's receivers. Branches
056 are then instantiated and assigned to the receivers according to whether
057 the receivers are producer or consumer receivers.
058
059 @author John S. Davis II
060 @version $Id$
061 @since Ptolemy II 1.0
062 @Pt.ProposedRating Red (davisj)
063 @Pt.AcceptedRating Red (davisj)
064 */
065public class BranchController implements Runnable {
066    /** Construct a branch controller in the specified composite actor
067     *  container.
068     *
069     *  @param container The parent actor that contains this object.
070     */
071    public BranchController(CompositeActor container) {
072        _parentActor = container;
073        //_parentName = ((Nameable) container).getName();
074    }
075
076    ///////////////////////////////////////////////////////////////////
077    ////                         public methods                    ////
078
079    /** Activate the branches that are managed by this branch
080     *  controller. This method should be invoked once when
081     *  a branch controller first starts the branches it controls.
082     *  Invocation of this method will cause the branches to
083     *  begin transferring tokens between their assigned producer
084     *  and consumer receiver. Each branch executes in its own
085     *  thread.
086     */
087    public void activateBranches() {
088        // Copy the list of branches within a synchronized block,
089        // then activate them. We do not want to hold the lock
090        // on this controller during the run, as this can cause
091        // deadlock.
092        LinkedList branchesCopy;
093
094        synchronized (this) {
095            if (!hasBranches()) {
096                return;
097            }
098
099            setActive(true);
100            branchesCopy = new LinkedList(_branches);
101        }
102
103        Iterator branches = branchesCopy.iterator();
104
105        while (branches.hasNext()) {
106            Branch branch = (Branch) branches.next();
107            Thread thread = new Thread(branch);
108            _getDirector().addThread(thread);
109            thread.start();
110        }
111    }
112
113    /** Add branches corresponding to the channels of the port
114     *  argument. The port must be contained by the same actor
115     *  that contains this controller. If branches corresponding
116     *  to the specified port have already been added to this
117     *  controller, then an IllegalActionException will be thrown.
118     *  If the input/output polarity of this port does not match that
119     *  of ports for whom branches have been previously added
120     *  to this controller, then throw an IllegalActionException.
121     *  @param port The port for which branches will be added to this
122     *   controller.
123     *  @exception IllegalActionException If branches for the
124     *   port have been previously added to this controller or
125     *   if the port input/output polarity does not match that
126     *   of ports for whom branches were previously add to this
127     *   controller.
128     */
129    public void addBranches(IOPort port) throws IllegalActionException {
130        if (port.getContainer() != getParent()) {
131            throw new IllegalActionException(
132                    "Can not contain " + "a port that is not contained by this "
133                            + "BranchController's container.");
134        }
135
136        if (_ports.contains(port)) {
137            throw new IllegalActionException(port, "This port "
138                    + "is already controlled by this " + "BranchController");
139        }
140
141        // Careful; maintain order of following test in case
142        // Java is like C
143        if (_hasInputPorts() && !port.isInput()) {
144            throw new IllegalActionException("BranchControllers "
145                    + "must contain only input ports or only output "
146                    + "ports; not both");
147        }
148
149        if (_hasOutputPorts() && !port.isOutput()) {
150            throw new IllegalActionException("BranchControllers "
151                    + "must contain only input ports or only output "
152                    + "ports; not both");
153        }
154
155        _ports.add(port);
156
157        Branch branch = null;
158        ProcessReceiver producerReceiver = null;
159        ProcessReceiver consumerReceiver = null;
160        Receiver[][] producerReceivers = null;
161        Receiver[][] consumerReceivers = null;
162
163        for (int i = 0; i < port.getWidth(); i++) {
164            if (port.isInput()) {
165                producerReceivers = port.getReceivers();
166                consumerReceivers = port.deepGetReceivers();
167            } else if (port.isOutput()) {
168                producerReceivers = port.getInsideReceivers();
169                consumerReceivers = port.getRemoteReceivers();
170            } else {
171                throw new IllegalActionException("Bad news");
172            }
173
174            // If the port lacks either producer or consumer
175            // receivers, then there is no point in creating a branch.
176            if (producerReceivers.length > i && consumerReceivers.length > i) {
177                try {
178                    producerReceiver = (ProcessReceiver) producerReceivers[i][0];
179                    consumerReceiver = (ProcessReceiver) consumerReceivers[i][0];
180                } catch (ClassCastException ex) {
181                    // See [Bug 5] and pn/test/PNInsideDE.xml
182                    throw new IllegalActionException(port, ex,
183                            "At the current time, process-oriented domains "
184                                    + "(PN and CSP) cannot be nested inside "
185                                    + "firing-based domains (SDF, DE, CT, etc.).");
186                }
187
188                branch = new Branch(producerReceiver, consumerReceiver, this);
189                _branches.add(branch);
190            }
191        }
192    }
193
194    /** Deactivate the branches assigned to this branch controller.
195     */
196    public synchronized void deactivateBranches() {
197        setActive(false);
198
199        Iterator branches = _branches.iterator();
200        Branch branch = null;
201
202        while (branches.hasNext()) {
203            branch = (Branch) branches.next();
204            branch.setActive(false);
205
206            Receiver receiver = branch.getConsumerReceiver();
207
208            synchronized (receiver) {
209                receiver.notifyAll();
210            }
211
212            receiver = branch.getProducerReceiver();
213
214            synchronized (receiver) {
215                receiver.notifyAll();
216            }
217        }
218
219        notifyAll();
220    }
221
222    /** Return the list of branches controlled by this controller.
223     *  @return The list of branches controlled by this controller.
224     */
225    public LinkedList getBranchList() {
226        return _branches;
227    }
228
229    /** Return the composite actor that contains this branch
230     *  controller.
231     *  @return The composite actor that contains this controller.
232     */
233    public CompositeActor getParent() {
234        return _parentActor;
235    }
236
237    /** Return true if this branch controller controls one or more
238     *  branches; return false otherwise.
239     *  @return True if this controller controls one or more branches;
240     *   return false otherwise.
241     */
242    public boolean hasBranches() {
243        return _branches.size() > 0;
244    }
245
246    /** Return true if this controller is active; return false
247     *  otherwise.
248     * @return True if this controller is active; false otherwise.
249     */
250    public synchronized boolean isActive() {
251        return _isActive;
252    }
253
254    /** Return true if all of the branches assigned to this branch
255     *  controller are blocked or if this branch controller has no
256     *  branches; return false otherwise.
257     *  @return True if all branches controlled by this branch
258     *   controller are blocked or if this branch controller has
259     *   no branches; return false otherwise.
260     */
261    public synchronized boolean isBlocked() {
262        if (!hasBranches()) {
263            return true;
264        }
265
266        if (_branchesBlocked >= _branches.size()) {
267            if (_branchesBlocked > 0) {
268                return true;
269            }
270        }
271
272        return false;
273    }
274
275    /** Begin executing the branches associated with this branch
276     *  controller so that they will begin transferring data in
277     *  their assigned channels. If all of the branches become
278     *  blocked then the director associated with this branch
279     *  branch controller is notified.
280     */
281    @Override
282    public void run() {
283        try {
284            activateBranches();
285
286            // After starting the runs, acquire a lock
287            // on this object.
288            synchronized (this) {
289                while (isActive()) {
290                    while (!isBlocked() && isActive()) {
291                        wait();
292                    }
293
294                    while (isBlocked() && isActive()) {
295                        _getDirector()._controllerBlocked(this);
296                        wait();
297                    }
298
299                    _getDirector()._controllerUnBlocked(this);
300                }
301            }
302        } catch (InterruptedException e) {
303            // FIXME: Do something
304        }
305    }
306
307    /** Set this branch controller active if the active parameter is
308     *  true; set this branch controller to inactive otherwise.
309     *  @param active The indicator of whether this branch controller
310     *   will be set active or inactive.
311     */
312    public synchronized void setActive(boolean active) {
313        _isActive = active;
314    }
315
316    ///////////////////////////////////////////////////////////////////
317    ////                         protected methods                 ////
318
319    /** Return the director that controls the execution of this
320     *  branch controller's containing composite actor.
321     *  @return The composite process director that is associated
322     *   with this branch controller's container.
323     */
324    protected CompositeProcessDirector _getDirector() {
325        try {
326            return (CompositeProcessDirector) _parentActor.getDirector();
327        } catch (NullPointerException ex) {
328            // If a thread has a reference to a receiver with no director it
329            // is an error so terminate the process.
330            String name = ((Nameable) getParent()).getName();
331            throw new TerminateProcessException("Error: " + name
332                    + " contains a branch controller that has a "
333                    + "receiver that does not have a director");
334        }
335    }
336
337    ///////////////////////////////////////////////////////////////////
338    ////                         private methods                   ////
339
340    /** Return true if this branch controller has input ports associated
341     *  with it; return false otherwise.
342     *  @return True if this branch controller has input ports associated
343     *  with it. False otherwise.
344     */
345    private boolean _hasInputPorts() {
346        if (_ports.size() == 0) {
347            return false;
348        }
349
350        Iterator ports = _ports.iterator();
351
352        while (ports.hasNext()) {
353            IOPort port = (IOPort) ports.next();
354            return port.isInput();
355        }
356
357        return false;
358    }
359
360    /** Return true if this branch controller has output ports associated
361     *  with it; return false otherwise.
362     *  @return True if this branch controller has output ports associated
363     *  with it. False otherwise.
364     */
365    private boolean _hasOutputPorts() {
366        if (_ports.size() == 0) {
367            return false;
368        }
369
370        Iterator ports = _ports.iterator();
371
372        while (ports.hasNext()) {
373            IOPort port = (IOPort) ports.next();
374            return port.isOutput();
375        }
376
377        return false;
378    }
379
380    ///////////////////////////////////////////////////////////////////
381    ////                         private variables                 ////
382    // The number of branches that are blocked
383    private int _branchesBlocked = 0;
384
385    // The CompositeActor that owns this controller object.
386    private CompositeActor _parentActor;
387
388    private LinkedList _branches = new LinkedList();
389
390    private LinkedList _ports = new LinkedList();
391
392    private boolean _isActive = false;
393
394    //private String _parentName = null;
395}