PageRenderTime 14ms CodeModel.GetById 2ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist041108/src/edu/vub/at/actors/natives/ELFarReference.java

http://ambienttalk.googlecode.com/
Java | 361 lines | 139 code | 38 blank | 184 comment | 18 complexity | 5ec2f78315683d0b788bbf540f79d227 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * ELFarReference.java created on 28-dec-2006 at 10:45:41
  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.actors.natives;
 29
 30import java.util.Vector;
 31
 32import edu.vub.at.actors.ATAsyncMessage;
 33import edu.vub.at.actors.eventloops.BlockingFuture;
 34import edu.vub.at.actors.eventloops.Event;
 35import edu.vub.at.actors.eventloops.EventLoop;
 36import edu.vub.at.actors.eventloops.EventQueue;
 37import edu.vub.at.actors.id.ATObjectID;
 38import edu.vub.at.actors.net.cmd.CMDTransmitATMessage;
 39import edu.vub.at.actors.net.comm.Address;
 40import edu.vub.at.actors.net.comm.CommunicationBus;
 41import edu.vub.at.actors.net.comm.NetworkException;
 42import edu.vub.at.exceptions.InterpreterException;
 43import edu.vub.at.exceptions.XIOProblem;
 44import edu.vub.at.objects.ATObject;
 45import edu.vub.at.objects.ATTable;
 46import edu.vub.at.objects.natives.NATTable;
 47import edu.vub.at.util.logging.Logging;
 48
 49/**
 50 * An instance of the class ELFarReference represents the event loop processor for
 51 * a remote far reference. That is, the event queue of this event loop serves as 
 52 * an 'outbox' which is dedicated for storing all messages sent to a particular
 53 * receiver object hosted by a remote virtual machine.
 54 * 
 55 * This event loop handles events from its event queue by trying to transmit them
 56 * to a remote virtual machine.
 57 * 
 58 * Concurrent processing behaviour using a CSP-like nondeterministic select:
 59 *  execute = select {
 60 *       ?receive(event) => handle(event)
 61 *    [] ?interrupted() => retractOutbox()
 62 *  }
 63 *  handle(e) = select {
 64 *       connected => transmit(e)
 65 *    [] ?interrupted() => putBack(e); retractOutbox()
 66 *  }
 67 * 
 68 * @author tvcutsem
 69 */
 70public final class ELFarReference extends EventLoop {
 71	
 72	/**
 73	 * When the {@link ELActor} owning this far reference wants to reify the event queue
 74	 * as an outbox, it will 'interrupt' this event loop by setting this field to a
 75	 * non-null value.
 76	 * <p>
 77	 * The {@link #handle(Event)} method tests for the presence of such an interrupt and
 78	 * will call the {@link #handleRetractRequest()} method if this field is set.
 79	 * <p>
 80	 * Marked volatile because this variable is read/written by multiple threads without
 81	 * synchronizing.
 82	 */
 83	private volatile BlockingFuture outboxFuture_ = null;
 84	
 85	/**
 86	 * the actor to which this far ref belongs, i.e. the only event loop that will schedule
 87	 * transmission events into this event loop's event queue
 88	 */
 89	private final ELActor owner_;
 90
 91	/** the first-class AT language value wrapping this event loop */
 92	private final NATRemoteFarRef farRef_;
 93    //TODO MAKE HIS farRef weak. But then, we have a garbage cycle TO THINK ABOUT!!
 94	//private final WeakReference farRef_;
 95	
 96	/** the <i>wire representation</i> of the remote receiver of my messages */
 97	private final ATObjectID destination_;
 98	
 99	/** the network layer used to actually transmit an AmbientTalk message */
100	private final CommunicationBus dispatcher_;
101	
102	/**
103	 * A state variable denoting whether the far reference denoted by this
104	 * event loop is 'connected' or 'disconnected' from its remote object.
105	 * 
106	 * If this variable is set to <tt>false</tt>, the event loop will block
107	 * and stop processing new events until it is set to <tt>true</tt> again.
108	 */
109	private boolean connected_;
110		
111	public ELFarReference(ATObjectID destination, ELActor owner, NATRemoteFarRef ref) {
112		super("far reference " + destination);
113		
114		farRef_ = ref;
115	//	farRef_ = new WeakReference(ref);
116		destination_ = destination;
117		owner_ = owner;
118		connected_ = true;		
119		dispatcher_ = owner_.getHost().communicationBus_;
120	}
121	
122	public synchronized void setConnected(boolean newState){
123		connected_ = newState;
124		this.notify();
125	}
126	
127	/**
128	 * Acknowledges the interrupt set by this far reference's owning actor.
129	 * Resolves the {@link #outboxFuture_} interrupt future with the vector of transmission
130	 * events that are  in the event queue of the far reference. This is used to retrieve copies
131	 * of all messages being sent through this far reference. Note that this method performs no
132	 * deserialisation of the events into (copied) messages. Such deserialisation needs to be
133	 * done by an {@link ELActor}, not the ELFarReference which will execute this method.
134	 * 
135	 * After handling the retract request, the {@link #outboxFuture_} is reset to <tt>null</tt>.
136	 * This is extremely important because it signifies that there is no more pending retract request.
137	 */
138	public void handleRetractRequest() {
139		outboxFuture_.resolve(eventQueue_.flush());
140		// if the future is not reset to null, the event loop would continually
141		// resolve the future from this point on!
142		outboxFuture_ = null;
143	}
144	
145	/**
146	 * Process message transmission events only when the remote reference
147	 * is connected. Otherwise, wait until notified by the <tt>connected</tt>
148	 * callback.
149	 * 
150	 * When handling a transmission event from the event queue, the event
151	 * loop can only process the event if either:
152	 * <ul>
153	 *  <li>the far reference is currently <i>connected</i>. In this case,
154	 *      the incoming event is simply processed.
155	 *  <li>the far reference is disconnected, so blocked waiting to become
156	 *      reconnected, but is being interrupted by its owner to flush
157	 *      its outbox. In this case, the current event on which the event
158	 *      loop is blocked is re-inserted into the event queue (in front!)
159	 *      and the interrupt is honoured.
160	 * </ul>
161	 * 
162	 */
163	public void handle(Event event) {
164		synchronized (this) {
165			while (!connected_ && outboxFuture_ == null) {
166				try {
167					this.wait();
168				} catch (InterruptedException e) {
169					if (askedToStop_) {
170						return; // fall through and make the thread die
171					}
172				}
173			}
174			
175			if(outboxFuture_ != null) {
176				// re-enqueue the current event in its proper position
177				receivePrioritized(event);
178				
179				// flush the queue
180				handleRetractRequest();
181				
182			// else is strictly necessary as the handleInterrupt method has side effects, 
183			// removing the current event from the queue, such that it would be incorrect 
184			// to still send it 
185			} else { // if (connected_) {
186				event.process(this);
187			}
188		}
189	}
190	
191	/**
192	 * This is a named subclass of event, which allows access to the AmbientTalk message
193	 * that is being transmitted. The constructor is still executed by the {@link ELActor}
194	 * that schedules this transmission event. This ensures
195	 * that the serialization of the message happens in the correct thread.
196	 * 
197	 * @see {@link ELFarReference#event_transmit(ATObject, ATAsyncMessage)}
198	 * @author smostinc
199	 */
200	private class TransmissionEvent extends Event {
201		public final Packet serializedMessage_;
202		
203		// Called by ELActor
204		
205		/**
206		 * @param pair a pair of [ATObject receiver, ATAsyncMessage message]
207		 */
208		public TransmissionEvent(ATTable pair) throws XIOProblem {
209			super("transmit("+pair+")");
210			serializedMessage_ = new Packet(pair.toString(), pair);
211		}
212		
213		/**
214		 * This code is executed by the {@link ELFarReference} event loop.
215		 */
216		public void process(Object owner) {
217			Address destAddress = getDestinationVMAddress();
218
219			if (destAddress != null) {
220				try {
221					new CMDTransmitATMessage(destination_.getActorId(), serializedMessage_).send(
222							dispatcher_, destAddress);
223
224					// getting here means the message was succesfully transmitted
225					
226				} catch (NetworkException e) {
227					// TODO: the message MAY have been transmitted! (i.e. an orphan might have
228					// been created: should make this more explicit to the AT programmer)
229					Logging.RemoteRef_LOG.warn(this
230									+ ": timeout while trying to transmit message, retrying");
231					receivePrioritized(this);
232				}
233			} else {
234				Logging.RemoteRef_LOG.info(this + ": suspected a disconnection from " +
235						destination_ + " because destination VM ID was not found in address book");
236				connected_ = false;
237				receivePrioritized(this);
238			}
239		}
240	}
241	
242	/**
243	 * Inserts an AmbientTalk message into this far reference's outbox.
244	 */
245	public void event_transmit(ATObject receiver, final ATAsyncMessage msg) throws XIOProblem {
246		// the message will still be serialized in the actor's thread
247		receive(new TransmissionEvent(NATTable.of(receiver, msg)));
248		
249		// the reception of a new event may awaken a sleeping ELFarReference thread, 
250		// so we interrupt the processor, forcing it to reevaluate its conditions
251		// we don't use wait/notify because in order to notify the event loop, we
252		// would require a lock on it, which might cause this actor to block on
253		// remote communication. Therefore, we use the interrupt mechanism instead.
254		processor_.interrupt();
255	}
256	
257	/**
258	 * Interrupts this event loop by issuing a request for flushing
259	 * its event queue.
260	 * 
261	 * This code is executed by the event loop thread of the {@link #owner_}!
262	 * 
263	 * @return a table of copies for the messages currently in the outbox
264	 */
265	public ATTable retractUnsentMessages() throws InterpreterException {
266		BlockingFuture eventVectorF = setRetractingFuture();
267		Vector events = null;
268		
269		try {
270			// actor has to wait a bit until the event loop has stopped processing
271			events = (Vector) eventVectorF.get();
272		} catch(Exception e) {
273			// should never occur!
274			e.printStackTrace();
275			return NATTable.EMPTY;
276		}
277		
278		ATObject[] messages = new ATObject[events.size()];
279		
280		for(int i = 0; i < events.size(); i++) {
281			TransmissionEvent current = (TransmissionEvent)events.get(i);
282			messages[i] = current.serializedMessage_.unpack();
283		}
284			
285		return NATTable.atValue(messages);
286	}
287	
288	/**
289	 * Overrides the default event handling strategy of this event loop.
290	 * It is no longer possible to simply block and wait for a new event
291	 * by performing {@link EventQueue#dequeue()} because this event loop
292	 * can be triggered by two kinds of wake-up calls, either:
293	 * <ul>
294	 *  <li>a new event arrives in the event queue, or
295	 *  <li>the actor owning this far reference interrupts it to flush
296	 *  its event queue into a reified outbox representation
297	 * </ul>
298	 * 
299	 * Therefore, this event loop synchronises on two different boolean
300	 * conditions. When exiting the <tt>wait</tt>-loop, the event loop has
301	 * to check which condition woke it up:
302	 * <ul>
303	 *  <li>if it was an incoming event, handle it
304	 *  <li>if it was interrupted by the owning actor, honor the interrupt
305	 * </ul>
306	 * 
307	 * Note that while executing, the event loop takes a lock on itself!
308	 * This synchronizes event processing with state transition notifications
309	 * via the {@link #connected()} and {@link #disconnected()} methods.
310	 */
311	protected void execute() {
312		synchronized (this) {
313			while (eventQueue_.isEmpty() && outboxFuture_ == null) {
314				try {
315					this.wait();
316				} catch (InterruptedException e) {
317					if (askedToStop_) {
318						return; // fall through and make the thread die
319					}
320				}
321			}
322
323			if (outboxFuture_ != null) {
324				handleRetractRequest();
325			} else { // if(!eventQueue_.isEmpty()) {
326				try {
327					handle(eventQueue_.dequeue());
328				} catch (InterruptedException e) {
329					// fall through, perhaps the thread was notified
330					// that it had to stop
331				}
332			}
333		}
334	}
335	
336	private Address getDestinationVMAddress() {
337		return owner_.getHost().vmAddressBook_.getAddressOf(destination_.getVirtualMachineId());
338	}
339	
340	/**
341	 * Signals the far reference that its owning actor has requested to retract unsent
342	 * messages. The interrupt will be handled as soon as the processing of the current
343	 * event has finished.
344	 * @return a blocking future the ELActor thread can wait on.
345	 */
346	private BlockingFuture setRetractingFuture() {
347		// first assign future to a local variable to avoid race conditions on writing to the outboxFuture_ variable!
348		final BlockingFuture future = new BlockingFuture();
349		outboxFuture_ = future;
350		
351		// the reception of a new interrupt may awaken a sleeping ELFarReference 
352		// thread, so we interrupt them, forcing them to reevaluate their conditions
353		processor_.interrupt();
354		return future;
355	}
356
357	protected void finalize() throws Throwable{
358		Logging.RemoteRef_LOG.info(this + ": ELFARREF STOPPED!!");
359		
360	}
361}