/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/signals/eval/DataflowEngine.java
http://ambienttalk.googlecode.com/ · Java · 199 lines · 85 code · 34 blank · 80 comment · 8 complexity · 8a940fc4d7391bd0f5f4f055f797f969 MD5 · raw file
- /**
- * AmbientTalk/2 Project
- * DataflowEngine.java created on Jun 14, 2008 at 6:48:44 PM
- * (c) Programming Technology Lab, 2006 - 2007
- * Authors: Tom Van Cutsem & Stijn Mostinckx
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
- package edu.vub.at.signals.eval;
- import java.util.Enumeration;
- import com.objectspace.jgl.BinaryPredicate;
- import com.objectspace.jgl.Pair;
- import com.objectspace.jgl.PriorityQueue;
- import com.objectspace.jgl.Stack;
- import edu.vub.at.exceptions.InterpreterException;
- import edu.vub.at.objects.ATMessage;
- import edu.vub.at.signals.ATSignal;
- import edu.vub.at.signals.SignalListener;
- import edu.vub.at.util.logging.Logging;
- /**
- * The dataflow engine is responsible for the propagation of signal updates to all dependent
- * computations. It interacts with the event loop actor in which it is run, which calls the
- * propagatePendingUpdates method of the engine at the end of each event loop turn.
- * <p>
- *
- * @author smostinc
- *
- */
- public class DataflowEngine {
- /**
- * When a new signal is created in the context of a dependent computation, its value is
- * not computed immediately since some of the signals on which it depends may already be
- * updated, while others still contain stale information. In order to allow the dataflow
- * engine to avoid incorrect updates, these dependent signals register that they need to
- * be initialized. This is achieved internally by requesting the _INITIALIZER_ object to
- * send them a default message.
- */
-
- /**
- * A prioritized queue of SignalListeners which need to be updated. The listeners are
- * sorted according to their dependency height, to avoid update glitches.
- */
- private PriorityQueue theSignalsToBeUpdated = new PriorityQueue(new BinaryPredicate() {
- public boolean execute(Object first, Object second) {
- Pair firstPair = (Pair) first;
- Pair secondPair = (Pair) second;
-
- SignalListener firstL = (SignalListener) firstPair.first;
- SignalListener secondL = (SignalListener) secondPair.first;
-
- return (firstL.dependencyHeight() > secondL.dependencyHeight());
- }
- });
-
- private int currentlyReachedStratum = 0;
-
- public void propagatePendingUpdates() {
- while(theSignalsToBeUpdated.size() != 0) {
- // Previously propagated updates may have caused signals to update their height.
- if(should_reprioritize) do_reprioritize();
-
- Pair theUpdateTuple = (Pair) theSignalsToBeUpdated.pop();
- SignalListener listener = (SignalListener) theUpdateTuple.first;
- Pair theMessageTuple = (Pair) theUpdateTuple.second;
- ATSignal propagator = (ATSignal) theMessageTuple.first;
- ATMessage message = (ATMessage) theMessageTuple.second;
-
- Logging.ReactiveProgramming_LOG.debug("Performing update: " + message + " from: " + propagator + " to: " + listener + " (" + theSignalsToBeUpdated.size() + " others remaining)");
- assert(currentlyReachedStratum <= listener.dependencyHeight(), "Dataflow engine asked to lower the stratum currently being served.");
-
- currentlyReachedStratum = listener.dependencyHeight();
-
- if(! listener.performUpdate(propagator, message))
- try {
- propagator.meta_removeDependent(listener);
- } catch (InterpreterException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- currentlyReachedStratum = 0;
- }
-
- public void schedulePendingUpdate(ATSignal thePropagator, SignalListener theDependent, ATMessage theUpdate) {
- Logging.ReactiveProgramming_LOG.debug("Signal: " + thePropagator + " schedules update: " + theUpdate + " for: " + theDependent);
-
- assert(thePropagator.dependencyHeight() >= currentlyReachedStratum, "Signal schedules update after the dataflow engine has reached a higher stratum.");
- assert(theDependent.dependencyHeight() > currentlyReachedStratum, "Signal update scheduled with lower or equal stratum than the one currently being served");
-
- theSignalsToBeUpdated.add(new Pair(theDependent, new Pair(thePropagator, theUpdate)));
- }
-
- /**
- * This method is called when the stratum in which a signal listener is allocated has to
- * change. It ensures that the priority queue of the dataflow engine will eventually be
- * updated to reflect the correct heights. This update is guaranteed to occur before the
- * next scheduled update is performed.
- *
- * @param theListener
- */
- public void reprioritize(SignalListener theListener) {
- should_reprioritize = true;
- }
-
- /*
- * Flag indicating that the queue should be sorted before proceeding with the propagation
- * of signal updates.
- */
- private boolean should_reprioritize = false;
-
- /*
- * Auxiliary method which is called when the dataflow engine detects that the dependency
- * height of one of more signals has changed. The resorting of the priority queue doesn't
- * occur immediately as dependency height updates tend to cascade.
- */
- private void do_reprioritize() {
- PriorityQueue theReprioritizedQueue = new PriorityQueue(theSignalsToBeUpdated.getComparator());
-
- for(Enumeration pairs = theSignalsToBeUpdated.elements();
- pairs.hasMoreElements();) {
- theReprioritizedQueue.add(pairs.nextElement());
- }
-
- theSignalsToBeUpdated = theReprioritizedQueue;
-
- should_reprioritize = false;
- }
-
- /**
- * theSurroundingSignal implements a dynamic variable to keep track of whether dataflow
- * graph elements are constructed in the dynamic extent of a method with a time-varying
- * receiver or argument. If this is the case, the constructed network implicitly depends
- * on the execution of the method. In other words, if the method is triggered again, the
- * network needs to be deconstructed.
- * <p>
- * This method allows the interpreter to keep track of such implicit dependencies, such
- * that when the method is triggered again, the dataflow graph which implicitly depends
- * on it is deconstructed. To aid in the deconstruction, the dataflow nodes that need to
- * be removed should be in a higher strata such that they have not computed yet when the
- * method is triggered.
- */
- private final Stack theSurroundingSignal = new Stack();
-
- public EncapsulatingSignal getEncapsulatingSignal() {
- if(theSurroundingSignal.size() == 0) return null;
-
- return (EncapsulatingSignal) theSurroundingSignal.top();
- }
-
- public void setEncapsulatingSignal(EncapsulatingSignal theSignal) {
- theSurroundingSignal.push(theSignal);
- }
-
- public void resetEncapsulatingSignal() {
- theSurroundingSignal.pop();
- }
- public int getCurrentlyReachedStratum() {
- return currentlyReachedStratum;
- }
-
- public boolean hasPendingUpdates() {
- return theSignalsToBeUpdated.size() != 0;
- }
-
-
- private void assert(boolean condition, String message) {
- if(! condition) {
- Logging.ReactiveProgramming_LOG.fatal(message);
-
- throw new RuntimeException(message);
- }
- }
- }