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

http://ambienttalk.googlecode.com/ · Java · 370 lines · 143 code · 39 blank · 188 comment · 18 complexity · 4dba0434f1de8cc694f47842424ce7ac 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, boolean isConnected) {
  102. super("far reference " + destination);
  103. farRef_ = ref;
  104. // farRef_ = new WeakReference(ref);
  105. destination_ = destination;
  106. owner_ = owner;
  107. connected_ = isConnected;
  108. dispatcher_ = owner_.getHost().communicationBus_;
  109. this.start();
  110. }
  111. public synchronized void setConnected(boolean newState){
  112. connected_ = newState;
  113. this.notify();
  114. }
  115. /**
  116. * Acknowledges the interrupt set by this far reference's owning actor.
  117. * Resolves the {@link #outboxFuture_} interrupt future with the vector of transmission
  118. * events that are in the event queue of the far reference. This is used to retrieve copies
  119. * of all messages being sent through this far reference. Note that this method performs no
  120. * deserialisation of the events into (copied) messages. Such deserialisation needs to be
  121. * done by an {@link ELActor}, not the ELFarReference which will execute this method.
  122. *
  123. * After handling the retract request, the {@link #outboxFuture_} is reset to <tt>null</tt>.
  124. * This is extremely important because it signifies that there is no more pending retract request.
  125. */
  126. public void handleRetractRequest() {
  127. outboxFuture_.resolve(eventQueue_.flush());
  128. // if the future is not reset to null, the event loop would continually
  129. // resolve the future from this point on!
  130. outboxFuture_ = null;
  131. }
  132. /**
  133. * Process message transmission events only when the remote reference
  134. * is connected. Otherwise, wait until notified by the <tt>connected</tt>
  135. * callback.
  136. *
  137. * When handling a transmission event from the event queue, the event
  138. * loop can only process the event if either:
  139. * <ul>
  140. * <li>the far reference is currently <i>connected</i>. In this case,
  141. * the incoming event is simply processed.
  142. * <li>the far reference is disconnected, so blocked waiting to become
  143. * reconnected, but is being interrupted by its owner to flush
  144. * its outbox. In this case, the current event on which the event
  145. * loop is blocked is re-inserted into the event queue (in front!)
  146. * and the interrupt is honoured.
  147. * </ul>
  148. *
  149. */
  150. public void handle(Event event) {
  151. synchronized (this) {
  152. while (!connected_ && outboxFuture_ == null) {
  153. try {
  154. this.wait();
  155. } catch (InterruptedException e) {
  156. if (askedToStop_) {
  157. return; // fall through and make the thread die
  158. }
  159. }
  160. }
  161. if(outboxFuture_ != null) {
  162. // re-enqueue the current event in its proper position
  163. receivePrioritized(event);
  164. // flush the queue
  165. handleRetractRequest();
  166. // else is strictly necessary as the handleInterrupt method has side effects,
  167. // removing the current event from the queue, such that it would be incorrect
  168. // to still send it
  169. } else { // if (connected_) {
  170. event.process(this);
  171. }
  172. }
  173. }
  174. /**
  175. * This is a named subclass of event, which allows access to the AmbientTalk message
  176. * that is being transmitted. The constructor is still executed by the {@link ELActor}
  177. * that schedules this transmission event. This ensures
  178. * that the serialization of the message happens in the correct thread.
  179. *
  180. * @see {@link ELFarReference#event_transmit(ATObject, ATAsyncMessage)}
  181. * @author smostinc
  182. */
  183. private class TransmissionEvent extends Event {
  184. public final Packet serializedMessage_;
  185. // an original message is a pair of [ATObject receiver, ATAsyncMessage message]
  186. // used to be able to retract a message without involving
  187. // serialization/desearialization because sometimes o != resolve(pass(o))
  188. public final ATTable originalMessage_;
  189. // Called by ELActor
  190. /**
  191. * @param pair a pair of [ATObject receiver, ATAsyncMessage message]
  192. */
  193. public TransmissionEvent(ATTable pair) throws XIOProblem {
  194. super("transmit("+pair+")");
  195. serializedMessage_ = new Packet(pair.toString(), pair);
  196. originalMessage_ = pair;
  197. }
  198. /**
  199. * This code is executed by the {@link ELFarReference} event loop.
  200. */
  201. public void process(Object owner) {
  202. Address destAddress = getDestinationVMAddress();
  203. if (destAddress != null) {
  204. try {
  205. new CMDTransmitATMessage(destination_.getActorId(), serializedMessage_).send(
  206. dispatcher_, destAddress);
  207. // getting here means the message was succesfully transmitted
  208. } catch (NetworkException e) {
  209. // TODO: the message MAY have been transmitted! (i.e. an orphan might have
  210. // been created: should make this more explicit to the AT programmer)
  211. Logging.RemoteRef_LOG.warn(this
  212. + ": timeout while trying to transmit message, retrying");
  213. receivePrioritized(this);
  214. }
  215. } else {
  216. Logging.RemoteRef_LOG.info(this + ": suspected a disconnection from " +
  217. destination_ + " because destination VM ID was not found in address book");
  218. connected_ = false;
  219. receivePrioritized(this);
  220. }
  221. }
  222. }
  223. /**
  224. * Inserts an AmbientTalk message into this far reference's outbox.
  225. */
  226. public void event_transmit(ATObject receiver, final ATAsyncMessage msg) throws XIOProblem {
  227. // the message will still be serialized in the actor's thread
  228. receive(new TransmissionEvent(NATTable.of(receiver, msg)));
  229. // the reception of a new event may awaken a sleeping ELFarReference thread,
  230. // so we interrupt the processor, forcing it to reevaluate its conditions
  231. // we don't use wait/notify because in order to notify the event loop, we
  232. // would require a lock on it, which might cause this actor to block on
  233. // remote communication. Therefore, we use the interrupt mechanism instead.
  234. processor_.interrupt();
  235. }
  236. /**
  237. * Interrupts this event loop by issuing a request for flushing
  238. * its event queue.
  239. *
  240. * This code is executed by the event loop thread of the {@link #owner_}!
  241. *
  242. * @return a table of copies for the messages currently in the outbox
  243. */
  244. public ATTable retractUnsentMessages() throws InterpreterException {
  245. BlockingFuture eventVectorF = setRetractingFuture();
  246. Vector events = null;
  247. try {
  248. // actor has to wait a bit until the event loop has stopped processing
  249. events = (Vector) eventVectorF.get();
  250. } catch(Exception e) {
  251. // should never occur!
  252. e.printStackTrace();
  253. return NATTable.EMPTY;
  254. }
  255. ATObject[] messages = new ATObject[events.size()];
  256. for(int i = 0; i < events.size(); i++) {
  257. TransmissionEvent current = (TransmissionEvent)events.get(i);
  258. // hand out the original version of the message prior to serialization.
  259. ATObject[] pair = current.originalMessage_.asNativeTable().elements_;
  260. messages[i] = pair[1].asAsyncMessage();
  261. }
  262. return NATTable.atValue(messages);
  263. }
  264. /**
  265. * Overrides the default event handling strategy of this event loop.
  266. * It is no longer possible to simply block and wait for a new event
  267. * by performing {@link EventQueue#dequeue()} because this event loop
  268. * can be triggered by two kinds of wake-up calls, either:
  269. * <ul>
  270. * <li>a new event arrives in the event queue, or
  271. * <li>the actor owning this far reference interrupts it to flush
  272. * its event queue into a reified outbox representation
  273. * </ul>
  274. *
  275. * Therefore, this event loop synchronises on two different boolean
  276. * conditions. When exiting the <tt>wait</tt>-loop, the event loop has
  277. * to check which condition woke it up:
  278. * <ul>
  279. * <li>if it was an incoming event, handle it
  280. * <li>if it was interrupted by the owning actor, honor the interrupt
  281. * </ul>
  282. *
  283. * Note that while executing, the event loop takes a lock on itself!
  284. * This synchronizes event processing with state transition notifications
  285. * via the {@link #setConnected(boolean)} method.
  286. */
  287. protected void execute() {
  288. synchronized (this) {
  289. while (eventQueue_.isEmpty() && outboxFuture_ == null) {
  290. try {
  291. this.wait();
  292. } catch (InterruptedException e) {
  293. if (askedToStop_) {
  294. return; // fall through and make the thread die
  295. }
  296. }
  297. }
  298. if (outboxFuture_ != null) {
  299. handleRetractRequest();
  300. } else { // if(!eventQueue_.isEmpty()) {
  301. try {
  302. handle(eventQueue_.dequeue());
  303. } catch (InterruptedException e) {
  304. // fall through, perhaps the thread was notified
  305. // that it had to stop
  306. }
  307. }
  308. }
  309. }
  310. private Address getDestinationVMAddress() {
  311. return owner_.getHost().vmAddressBook_.getAddressOf(destination_.getVirtualMachineId());
  312. }
  313. /**
  314. * Signals the far reference that its owning actor has requested to retract unsent
  315. * messages. The interrupt will be handled as soon as the processing of the current
  316. * event has finished.
  317. * @return a blocking future the ELActor thread can wait on.
  318. */
  319. private BlockingFuture setRetractingFuture() {
  320. // first assign future to a local variable to avoid race conditions on writing to the outboxFuture_ variable!
  321. final BlockingFuture future = new BlockingFuture();
  322. outboxFuture_ = future;
  323. // the reception of a new interrupt may awaken a sleeping ELFarReference
  324. // thread, so we interrupt them, forcing them to reevaluate their conditions
  325. processor_.interrupt();
  326. return future;
  327. }
  328. protected void finalize() throws Throwable{
  329. Logging.RemoteRef_LOG.info(this + ": ELFARREF STOPPED!!");
  330. }
  331. }