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