PageRenderTime 38ms CodeModel.GetById 29ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  1/**
  2 * AmbientTalk/2 Project
  3 * DataflowEngine.java created on Jun 14, 2008 at 6:48:44 PM
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.signals.eval;
 29
 30import java.util.Enumeration;
 31
 32import com.objectspace.jgl.BinaryPredicate;
 33import com.objectspace.jgl.Pair;
 34import com.objectspace.jgl.PriorityQueue;
 35import com.objectspace.jgl.Stack;
 36
 37import edu.vub.at.exceptions.InterpreterException;
 38import edu.vub.at.objects.ATMessage;
 39import edu.vub.at.signals.ATSignal;
 40import edu.vub.at.signals.SignalListener;
 41import edu.vub.at.util.logging.Logging;
 42
 43/**
 44 * The dataflow engine is responsible for the propagation of signal updates to all dependent
 45 * computations. It interacts with the event loop actor in which it is run,  which calls the
 46 * propagatePendingUpdates method of the engine at the end of each event loop turn. 
 47 * <p>
 48 *
 49 * @author smostinc
 50 *
 51 */
 52public class DataflowEngine {
 53
 54	/**
 55	 * When a new signal is created in the context of a dependent computation,  its value is
 56	 * not computed immediately since some of the signals on which it depends may already be
 57	 * updated, while others still contain stale information. In order to allow the dataflow
 58	 * engine to avoid incorrect updates, these dependent signals register that they need to
 59	 * be initialized. This is achieved internally by requesting the _INITIALIZER_ object to
 60	 * send them a default message.  
 61	 */
 62		
 63	/**
 64	 * A prioritized queue of SignalListeners which need to be updated.   The listeners are
 65	 * sorted according to their dependency height, to avoid update glitches.
 66	 */
 67	private PriorityQueue 		theSignalsToBeUpdated	= new PriorityQueue(new BinaryPredicate() {
 68		public boolean execute(Object first, Object second) {
 69			Pair firstPair			= (Pair) first;
 70			Pair secondPair			= (Pair) second;
 71			
 72			SignalListener firstL	= (SignalListener) firstPair.first;
 73			SignalListener secondL	= (SignalListener) secondPair.first;
 74								
 75			return (firstL.dependencyHeight() > secondL.dependencyHeight());
 76		}
 77	});
 78	
 79	private int currentlyReachedStratum = 0;
 80	
 81	public void propagatePendingUpdates() {
 82		while(theSignalsToBeUpdated.size() != 0) {
 83			// Previously propagated updates may have caused signals to update their height.
 84			if(should_reprioritize) do_reprioritize();
 85			
 86			Pair 				theUpdateTuple 	= (Pair) theSignalsToBeUpdated.pop();
 87			SignalListener		listener		= (SignalListener) theUpdateTuple.first;
 88			Pair 				theMessageTuple = (Pair) theUpdateTuple.second;
 89			ATSignal 			propagator		= (ATSignal) theMessageTuple.first;
 90			ATMessage			message			= (ATMessage) theMessageTuple.second;
 91			
 92			Logging.ReactiveProgramming_LOG.debug("Performing update: " + message + " from: " + propagator + " to: " + listener + " (" + theSignalsToBeUpdated.size() + " others remaining)");
 93			assert(currentlyReachedStratum <= listener.dependencyHeight(), "Dataflow engine asked to lower the stratum currently being served.");
 94			
 95			currentlyReachedStratum = listener.dependencyHeight();
 96			
 97			if(! listener.performUpdate(propagator, message))
 98				try {
 99					propagator.meta_removeDependent(listener);
100				} catch (InterpreterException e) {
101					// TODO Auto-generated catch block
102					e.printStackTrace();
103				}
104		}
105		
106		currentlyReachedStratum = 0;
107	}
108	
109	public void schedulePendingUpdate(ATSignal thePropagator, SignalListener theDependent, ATMessage theUpdate) {
110		Logging.ReactiveProgramming_LOG.debug("Signal: " + thePropagator + " schedules update: " + theUpdate + " for: " + theDependent);
111		
112		assert(thePropagator.dependencyHeight() >= currentlyReachedStratum, "Signal schedules update after the dataflow engine has reached a higher stratum.");
113		assert(theDependent.dependencyHeight()  >  currentlyReachedStratum, "Signal update scheduled with lower or equal stratum than the one currently being served");
114				
115		theSignalsToBeUpdated.add(new Pair(theDependent, new Pair(thePropagator, theUpdate)));
116	}
117	
118	/**
119	 * This method is called when the stratum in which a signal listener is allocated has to
120	 * change.  It ensures that the priority queue of the dataflow engine will eventually be
121	 * updated to reflect the correct heights. This update is guaranteed to occur before the
122	 * next scheduled update is performed. 
123	 *  
124	 * @param theListener
125	 */
126	public void reprioritize(SignalListener theListener) {
127		should_reprioritize	= true;
128	}
129	
130	/* 
131	 * Flag indicating that the queue should be sorted before proceeding with the propagation
132	 * of signal updates.
133	 */
134	private boolean should_reprioritize = false;
135	
136	/*
137	 * Auxiliary method which is called when the dataflow engine detects  that the dependency
138	 * height of one of more signals has changed. The resorting of the priority queue doesn't
139	 * occur immediately as dependency height updates tend to cascade. 
140	 */
141	private void do_reprioritize() {
142		PriorityQueue theReprioritizedQueue = new PriorityQueue(theSignalsToBeUpdated.getComparator());
143		
144		for(Enumeration pairs = theSignalsToBeUpdated.elements();
145						pairs.hasMoreElements();) {
146			theReprioritizedQueue.add(pairs.nextElement());
147		}
148		
149		theSignalsToBeUpdated = theReprioritizedQueue;
150		
151		should_reprioritize	= false;
152	}
153	
154	/**
155	 * theSurroundingSignal implements a dynamic variable  to keep track of whether dataflow
156	 * graph elements are constructed in the dynamic extent of a method  with a time-varying
157	 * receiver or argument. If this is the case, the constructed network implicitly depends
158	 * on the execution of the method. In other words, if the method is triggered again, the
159	 * network needs to be deconstructed.
160	 * <p>
161	 * This method allows the interpreter to keep track of such implicit dependencies,  such
162	 * that when the method is triggered again,  the dataflow graph which implicitly depends
163	 * on it is deconstructed. To aid in the deconstruction, the dataflow nodes that need to
164	 * be removed should be in a higher strata such that they have not computed yet when the
165	 * method is triggered. 
166	 */
167	private final Stack 			theSurroundingSignal	= new Stack();
168	
169	public EncapsulatingSignal getEncapsulatingSignal() {
170		if(theSurroundingSignal.size() == 0) return null;
171		
172		return (EncapsulatingSignal) theSurroundingSignal.top();
173	}
174	
175	public void setEncapsulatingSignal(EncapsulatingSignal theSignal) {
176		theSurroundingSignal.push(theSignal);
177	}
178	
179	public void resetEncapsulatingSignal() {
180		theSurroundingSignal.pop();
181	}
182
183	public int getCurrentlyReachedStratum() {
184		return currentlyReachedStratum;
185	}
186	
187	public boolean hasPendingUpdates() {
188		return theSignalsToBeUpdated.size() != 0;
189	}
190	
191	
192	private void assert(boolean condition, String message) {
193		if(! condition) {
194			Logging.ReactiveProgramming_LOG.fatal(message);
195			
196			throw new RuntimeException(message);
197		}
198	}
199}