001/* Thread class for process oriented domains.
002
003 Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.actor.process;
029
030import java.io.InterruptedIOException;
031
032import ptolemy.actor.Actor;
033import ptolemy.actor.FiringEvent;
034import ptolemy.actor.FiringsRecordable;
035import ptolemy.actor.Manager;
036import ptolemy.kernel.Entity;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.NamedObj;
039import ptolemy.kernel.util.PtolemyThread;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// ProcessThread
044
045/**
046 Thread class acting as a process for process oriented domains.
047 <P>
048 In process oriented domains, each actor acts as a separate process and
049 its execution is not centrally controlled by the director. Each process
050 runs concurrently with other processes and is responsible for calling
051 its execution methods.
052 <P>
053 This class provides the mechanism to implement the above.
054 An instance of this class can be created by passing an actor as an
055 argument to the constructor. This class runs as a separate thread on
056 being started and calls the execution methods on the actor, repeatedly.
057 Specifically, it calls initialize(), and then repeatedly calls
058 the prefire(), fire() and postfire() methods
059 of the actor. Before termination, this calls the wrapup() method of
060 the actor.
061 <P>
062 If an actor returns false in its postfire() methods, the actor is
063 never fired again and the thread or process terminates after calling
064 wrapup() on the actor.
065 <P>
066 An instance of this class is associated with an instance of
067 ProcessDirector as well as an instance of Actor. The
068 _increaseActiveCount() of the director is called from the constructor
069 of this class, and the _decreaseActiveCount() method is called at the
070 end of the run() method, just before the thread terminates.
071
072 @author Mudit Goel, Neil Smyth, John S. Davis II, Contributors: Efrat Jaeger, Daniel Crawl
073 @version $Id$
074 @since Ptolemy II 0.2
075 @Pt.ProposedRating Green (mudit)
076 @Pt.AcceptedRating Yellow (mudit)
077 */
078public class ProcessThread extends PtolemyThread {
079    /** Construct a thread to be used for the execution of the
080     *  iteration methods of the actor. This increases the count of active
081     *  actors in the director.
082     *  @param actor The actor to be executed.
083     *  @param director The director responsible for the execution of
084     *  the actor.
085     */
086    public ProcessThread(Actor actor, ProcessDirector director) {
087        super();
088        _actor = actor;
089        _director = director;
090        _manager = actor.getManager();
091
092        if (_actor instanceof NamedObj) {
093            _name = ((NamedObj) _actor).getFullName();
094            addDebugListener((NamedObj) _actor);
095        } else {
096            _name = "Unnamed";
097        }
098
099        // Set the name of the thread to the full name of the actor.
100        setName(_name);
101
102        // This method is called here and not in the run() method as the
103        // count should be incremented before any thread is started
104        // or made active. This is because the second started thread might
105        // block on a read or write to this process and increment the block
106        // count even before this thread has incremented the active count.
107        // This results in false deadlocks.
108        _director.addThread(this);
109    }
110
111    ///////////////////////////////////////////////////////////////////
112    ////                         public methods                    ////
113
114    /** Return the actor being executed by this thread.
115     *  @return The actor being executed by this thread.
116     */
117    public Actor getActor() {
118        return _actor;
119    }
120
121    /** Initialize the actor, iterate it through the execution cycle
122     *  until it terminates. At the end of the termination, calls wrapup
123     *  on the actor.
124     */
125    @Override
126    public void run() {
127        if (_debugging) {
128            _debug("-- Starting thread.");
129        }
130
131        Workspace workspace = _director.workspace();
132        boolean iterate = true;
133        Throwable thrownWhenIterate = null;
134        Throwable thrownWhenWrapup = null;
135
136        try {
137            // Initialize the actor.
138            _actor.initialize();
139
140            _actorInitialized();
141
142            // While postfire() returns true and stop() is not called.
143            while (iterate) {
144                // NOTE: Possible race condition... actor.stop()
145                // might be called before we get to this.
146                // This will cause postfire() on the actor
147                // to return false, which will stop its execution.
148                if (_director.isStopFireRequested()) {
149                    // And wait until the flag has been cleared.
150                    if (_debugging) {
151                        _debug("-- Thread pause requested. Get lock on director.");
152                    }
153
154                    int depth = 0;
155                    try {
156                        synchronized (_director) {
157                            // Tell the director we're stopped (necessary
158                            // for deadlock detection).
159                            _director.threadHasPaused(this);
160
161                            while (_director.isStopFireRequested()) {
162                                // If a stop has been requested, in addition
163                                // to a stopFire, then stop execution
164                                // altogether and skip to wrapup().
165                                if (_director.isStopRequested()) {
166                                    if (_debugging) {
167                                        _debug("-- Thread stop requested, "
168                                                + "so cancel iteration.");
169                                    }
170                                    break;
171                                }
172
173                                if (_debugging) {
174                                    _debug("-- Thread waiting for "
175                                            + "canceled pause request.");
176                                }
177
178                                // NOTE: We cannot use workspace.wait(Object) here without
179                                // introducing a race condition, because we have to release
180                                // the lock on the _director before calling workspace.wait(_director).
181                                if (depth == 0) {
182                                    depth = workspace.releaseReadPermission();
183                                }
184                                _director.wait();
185                            }
186                            _director.threadHasResumed(this);
187                            if (_debugging) {
188                                _debug("-- Thread resuming.");
189                            }
190                        }
191                    } catch (InterruptedException ex) {
192                        if (_debugging) {
193                            _debug("-- Thread interrupted, "
194                                    + "so cancel iteration.");
195                        }
196                        break;
197                    } finally {
198                        if (depth > 0) {
199                            // This has to happen outside the synchronized(_director)
200                            // block to prevent deadlock.
201                            workspace.reacquireReadPermission(depth);
202                        }
203                    }
204                }
205
206                if (_director.isStopRequested()) {
207                    break;
208                }
209
210                // container is checked for null to detect the
211                // deletion of the actor from the topology.
212                if (((Entity) _actor).getContainer() != null) {
213                    iterate = _iterateActor();
214                }
215            }
216        } catch (Throwable t) {
217            thrownWhenIterate = t;
218        } finally {
219
220            try {
221                // NOTE: Deadlock risk here if wrapup is done inside
222                // a block synchronized on the _director, as it used to be.
223                // Holding a lock on the _director during wrapup()
224                // might cause deadlock with hierarchical models where
225                // wrapup() waits for internal actors to conclude,
226                // doing a wait() on its own internal director,
227                // or trying to acquire a write lock on the workspace.
228                // Meanwhile, this thread will hold a lock on this
229                // outside director, which may prevent the other
230                // threads from releasing their write lock!
231                wrapup();
232            } catch (IllegalActionException e) {
233                thrownWhenWrapup = e;
234            } finally {
235                // Let the director know that this thread stopped.
236                // This must occur after the call to wrapup above.
237                synchronized (_director) {
238                    _director.removeThread(this);
239                }
240                if (_debugging) {
241                    _debug("-- Thread stopped.");
242                }
243
244                boolean rethrow = false;
245
246                if (thrownWhenIterate instanceof TerminateProcessException) {
247                    // Process was terminated.
248                    if (_debugging) {
249                        _debug("-- Blocked Receiver call "
250                                + "threw TerminateProcessException.");
251                    }
252                } else if (thrownWhenIterate instanceof InterruptedException) {
253                    // Process was terminated by call to stop();
254                    if (_debugging) {
255                        _debug("-- Thread was interrupted: "
256                                + thrownWhenIterate);
257                    }
258                } else if (thrownWhenIterate instanceof InterruptedIOException
259                        || thrownWhenIterate != null && thrownWhenIterate
260                                .getCause() instanceof InterruptedIOException) {
261                    // PSDF has problems here when run with JavaScope
262                    if (_debugging) {
263                        _debug("-- IO was interrupted: " + thrownWhenIterate);
264                    }
265                } else if (thrownWhenIterate instanceof IllegalActionException) {
266                    if (_debugging) {
267                        _debug("-- Exception: " + thrownWhenIterate);
268                    }
269                    _manager.notifyListenersOfException(
270                            (IllegalActionException) thrownWhenIterate);
271                } else if (thrownWhenIterate != null) {
272                    rethrow = true;
273                }
274
275                if (thrownWhenWrapup instanceof IllegalActionException) {
276                    if (_debugging) {
277                        _debug("-- Exception: " + thrownWhenWrapup);
278                    }
279                    _manager.notifyListenersOfException(
280                            (IllegalActionException) thrownWhenWrapup);
281                } else if (thrownWhenWrapup != null) {
282                    // Must be a runtime exception.
283                    // Call notifyListenerOfThrowable() here so that
284                    // the stacktrace appears in the UI and not in stderr.
285                    _manager.notifyListenersOfThrowable(thrownWhenWrapup);
286                } else if (rethrow) {
287                    _manager.notifyListenersOfThrowable(thrownWhenIterate);
288                }
289            }
290        }
291    }
292
293    /** End the execution of the actor under the control of this
294     *  thread. Subclasses are encouraged to override this method
295     *  as necessary for domain specific functionality.
296     *  @exception IllegalActionException If an error occurs while
297     *   ending execution of the actor under the control of this
298     *   thread.
299     */
300    public void wrapup() throws IllegalActionException {
301        if (_debugging) {
302            _debug("-- Thread wrapup() called.");
303        }
304        _actor.wrapup();
305    }
306
307    ///////////////////////////////////////////////////////////////////
308    ////                         protected methods                 ////
309
310    /** Notify that the actor has been initialized. This base class
311     *  does nothing.
312     */
313    protected void _actorInitialized() {
314    }
315
316    /** Iterate the actor associate with this thread.
317     *  @return True if either prefire() returns false
318     *   or postfire returns true.
319     *  @exception IllegalActionException If the actor throws it.
320     */
321    protected boolean _iterateActor() throws IllegalActionException {
322        FiringsRecordable firingsRecordable = null;
323        // FIXME: See SysMLSequentialDIrector.iterateActorOnce() for very similar code.
324        if (_actor instanceof FiringsRecordable) {
325            firingsRecordable = (FiringsRecordable) _actor;
326        }
327
328        if (firingsRecordable != null) {
329            firingsRecordable.recordFiring(FiringEvent.BEFORE_PREFIRE);
330        }
331        boolean result = true;
332        if (_actor.prefire()) {
333
334            if (firingsRecordable != null) {
335                firingsRecordable.recordFiring(FiringEvent.AFTER_PREFIRE);
336                firingsRecordable.recordFiring(FiringEvent.BEFORE_FIRE);
337            }
338
339            _actor.fire();
340
341            if (firingsRecordable != null) {
342                firingsRecordable.recordFiring(FiringEvent.AFTER_FIRE);
343                firingsRecordable.recordFiring(FiringEvent.BEFORE_POSTFIRE);
344            }
345
346            result = _actor.postfire();
347
348            if (firingsRecordable != null) {
349                firingsRecordable.recordFiring(FiringEvent.AFTER_POSTFIRE);
350            }
351        } else {
352            if (firingsRecordable != null) {
353                firingsRecordable.recordFiring(FiringEvent.AFTER_PREFIRE);
354            }
355        }
356        return result;
357    }
358
359    ///////////////////////////////////////////////////////////////////
360    ////                         protected variables               ////
361
362    /** The actor that to be executed. */
363    protected Actor _actor;
364
365    /** The director responsible for the execution of the actor. */
366    protected ProcessDirector _director;
367
368    /** The Manager of the actor. */
369    protected Manager _manager;
370
371    ///////////////////////////////////////////////////////////////////
372    ////                         private variables                 ////
373
374    private String _name;
375}