PageRenderTime 73ms CodeModel.GetById 33ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/signals/natives/NATSignal.java

http://ambienttalk.googlecode.com/
Java | 260 lines | 131 code | 50 blank | 79 comment | 14 complexity | d34b339d73c347e1137f2726f868e318 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * NATSignal.java created on Apr 14, 2008
  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.natives;
 29
 30import java.lang.reflect.Method;
 31import java.util.HashSet;
 32import java.util.Iterator;
 33
 34import edu.vub.at.actors.natives.ELActor;
 35import edu.vub.at.eval.Evaluator;
 36import edu.vub.at.exceptions.InterpreterException;
 37import edu.vub.at.exceptions.XIllegalOperation;
 38import edu.vub.at.objects.ATMessage;
 39import edu.vub.at.objects.ATNil;
 40import edu.vub.at.objects.ATNumber;
 41import edu.vub.at.objects.ATObject;
 42import edu.vub.at.objects.ATTable;
 43import edu.vub.at.objects.natives.NATMethodInvocation;
 44import edu.vub.at.objects.natives.NATNumber;
 45import edu.vub.at.objects.natives.NATTable;
 46import edu.vub.at.objects.natives.NativeATObject;
 47import edu.vub.at.signals.ATSignal;
 48import edu.vub.at.signals.SignalListener;
 49import edu.vub.at.signals.eval.DataflowEngine;
 50import edu.vub.at.signals.eval.EncapsulatingSignal;
 51import edu.vub.at.util.logging.Logging;
 52
 53/**
 54 * NATSignal is the abstract root class of all AmbientTalk signals.  It implements the complete
 55 * SignalListener interface and provides the required support to ensure that events originating
 56 * from a (primitive) event source outside of AmbientTalk's  event loop are properly translated
 57 * into asynchronous messages. 
 58 * 
 59 * Additionally, the signal keeps track of the context in which it was created and ensures that
 60 * if it was created in a reactive activation, it is properly unhooked before the activation is
 61 * triggered anew.
 62 * 
 63 * @author smostinc
 64 */
 65public abstract class NATSignal extends NativeATObject implements ATSignal, SignalListener {
 66	
 67	private 	int 			itsDependencyHeight;
 68	private 	boolean			hasBeenUnhooked;
 69	private 	ELActor			itsOwner;
 70
 71	protected 	HashSet			itsDependents	= new HashSet();
 72	protected 	final ATObject	itsConsumer;
 73	
 74	protected NATSignal(int initialDependencyHeight) {
 75		this(initialDependencyHeight, null);
 76	}
 77	
 78	protected NATSignal(int initialDependencyHeight, ATObject theConsumer) {
 79		// 1) Set the object that will consume all incoming signal updates
 80		
 81		itsConsumer = theConsumer;
 82		
 83		// 2) If this listener is created in the context of a reactive activation, the height
 84		// of the encapsulating activation should be taken into account, when determining the 
 85		// stratum in which this signal listener should be allocated.
 86		
 87		DataflowEngine theEngine 				= Evaluator.getDataflowEngine();
 88		EncapsulatingSignal theEncapsulator		= theEngine.getEncapsulatingSignal();
 89		
 90		int theEncapsulatorsDependencyHeight	= theEncapsulator!=null ? theEncapsulator.dependencyHeight() : 0;
 91		
 92		itsDependencyHeight = Math.max(initialDependencyHeight, theEncapsulatorsDependencyHeight);
 93		
 94		// 3) If this listener is created in the context of a reactive activation, updates to
 95		// that activation imply that this listener becomes invalid and should be unhooked.
 96		
 97		hasBeenUnhooked = false;
 98		
 99		if(theEncapsulator != null) theEncapsulator.registerDependency(this);
100		
101		// 4) To properly synchronize signal updates, external event sources should notify us
102		// of updates using asynchronous message sends.   To distinguish between internal and
103		// and external event sources,  we keep track of the actor that created the listener.
104		// Updates originating from another process can then be considered external.
105		
106		itsOwner = ELActor.currentActor();
107	}
108	
109	public int dependencyHeight() {
110		return itsDependencyHeight;
111	}
112	
113	public ATNumber meta_dependencyHeight() {
114		return NATNumber.atValue(itsDependencyHeight);
115	}
116	
117	public void updateDependencyHeight(int newDependencyHeight) {
118		Logging.ReactiveProgramming_LOG.debug("Updating stratum of signal: " + this + " from: " + dependencyHeight() + " to: " + newDependencyHeight);
119		// Conceptually, updating the height could be as simple as this :
120		// itsDependencyHeight 		= Math.max(itsDependencyHeight, newDependencyHeight);
121		
122		// However, it is important to ensure that if this listener has been enqueued for 
123		// recomputation, that it occurs in the correct stratum to avoid update glitches.
124		if(newDependencyHeight > itsDependencyHeight) {
125			itsDependencyHeight			= newDependencyHeight;
126			
127			DataflowEngine theEngine 	= Evaluator.getDataflowEngine();
128			
129			// This ensures  that the updated dependency height is reflected in the queue
130			// that is kept by the dataflow engine.
131			theEngine.reprioritize(this);
132			
133			// Notify all dependent signals
134			for(Iterator dependentComputationIt  = itsDependents.iterator(); 
135						 dependentComputationIt.hasNext();) {
136				SignalListener	currentDependentComputation = (SignalListener) dependentComputationIt.next();
137				
138				// The new height of all dependent computations should at least be our own
139				// dependency height plus one. 
140				currentDependentComputation.updateDependencyHeight(itsDependencyHeight + 1);
141			}
142		}
143	}
144	
145	
146	// Dependency Management
147	
148	public ATNil meta_addDependent(SignalListener dependent) throws InterpreterException {
149		if(dependent.dependencyHeight() > this.dependencyHeight()) {
150			itsDependents.add(dependent);
151		} else {
152			throw new XIllegalOperation("Attempted to add a dependent signal with a lower dependency height than my own");
153		}
154		
155		return Evaluator.getNil();
156	}
157
158	public ATNil meta_removeDependent(SignalListener dependent) throws InterpreterException {
159		itsDependents.remove(dependent);
160		
161		return Evaluator.getNil();
162	}
163
164	
165	public void unhook() {
166		hasBeenUnhooked = true;
167		
168		itsDependents.clear();
169	}
170	
171	// Update Handling
172	
173	/**
174	 * This method is declared final, since subclasses should override the protected method
175	 * inner_scheduleUpdate instead of overriding this method directly.
176	 */
177	public final void scheduleUpdate(ATSignal theParent, ATMessage theUpdate) {
178		if(	!shouldSynchronize(theParent, theUpdate))
179			inner_scheduleUpdate(theParent, theUpdate);
180	}
181	
182	protected void inner_scheduleUpdate(ATSignal theParent, ATMessage theUpdate) {
183		DataflowEngine theEngine = Evaluator.getDataflowEngine();
184		
185		theEngine.schedulePendingUpdate(theParent, this, theUpdate);
186	}
187
188	/**
189	 * This method is declared final, since subclasses should override the protected method
190	 * inner_performUpdate instead of overriding this method directly.
191	 */
192	public final boolean performUpdate(ATSignal theParent, ATMessage theUpdate) {
193		if(!hasBeenUnhooked)
194				inner_performUpdate(theParent, theUpdate);
195
196		return !hasBeenUnhooked;
197	}
198	
199	protected void inner_performUpdate(ATSignal theParent, ATMessage theUpdate) {
200		try {
201			if(itsConsumer == null) { 
202				propagateNewMessage(theUpdate);
203			} else {
204				theUpdate.base_sendTo(itsConsumer, theParent);
205			}
206		} catch (InterpreterException e) {
207			// Could not send the message succesfully
208			e.printStackTrace();
209		}
210	};
211	
212	// Auxiliary Methods
213	
214	/**
215	 * This method tests whether the signal update, that has to be scheduled, originates from
216	 * another process.  If the update is external, this method will schedule an asynchronous
217	 * message in the owning actors event loop.
218	 */
219	private boolean shouldSynchronize(ATSignal theParent, ATMessage theUpdate) {
220		try {
221			// Update is called from an external Thread : Synchronization is needed here
222			if(Thread.currentThread() != itsOwner.processor()) {
223				// Get the method to schedule a signal update
224				Method update = SignalListener.class.getDeclaredMethod(
225						"scheduleUpdate", new Class[]  { ATSignal.class, ATMessage.class });
226				
227				// And schedule it for execution within the owning event loop
228				itsOwner.event_symbioticInvocation(this, 
229						update, new ATObject[] { theParent, theUpdate });
230				
231				return true;
232			}
233		} catch (Exception e) {
234			// Could not select the method from this object;
235			e.printStackTrace();
236		}
237		
238		return false;
239	}
240	
241	protected void propagateNewMessage(ATMessage updateMessage) {
242		for(Iterator dependentComputationIt  = itsDependents.iterator(); 
243		 			 dependentComputationIt.hasNext();) {
244			SignalListener	currentDependentComputation = (SignalListener) dependentComputationIt.next();
245			
246			currentDependentComputation.scheduleUpdate(this, updateMessage);
247		}
248	}
249	
250	protected void propagateNewValues(ATTable computedValues) throws InterpreterException {
251		propagateNewMessage(new NATMethodInvocation(
252								Evaluator._APPLY_, 
253								NATTable.of(computedValues),
254								NATTable.EMPTY));
255	}
256	
257	protected void propagateNewValue(ATObject computedValue) throws InterpreterException {
258		propagateNewValues(NATTable.of(computedValue));
259	}
260}