/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/actors/natives/ELFarReference.java

http://ambienttalk.googlecode.com/ · Java · 398 lines · 160 code · 44 blank · 194 comment · 20 complexity · ca7236b6c9322b6294c609b0425f9746 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.ConnectionListener;
  37. import edu.vub.at.actors.net.cmd.CMDTransmitATMessage;
  38. import edu.vub.at.actors.net.comm.Address;
  39. import edu.vub.at.actors.net.comm.CommunicationBus;
  40. import edu.vub.at.actors.net.comm.NetworkException;
  41. import edu.vub.at.exceptions.InterpreterException;
  42. import edu.vub.at.exceptions.XIOProblem;
  43. import edu.vub.at.objects.ATObject;
  44. import edu.vub.at.objects.ATTable;
  45. import edu.vub.at.objects.natives.NATTable;
  46. import edu.vub.at.util.logging.Logging;
  47. /**
  48. * An instance of the class ELFarReference represents the event loop processor for
  49. * a remote far reference. That is, the event queue of this event loop serves as
  50. * an 'outbox' which is dedicated for storing all messages sent to a particular
  51. * receiver object hosted by a remote virtual machine.
  52. *
  53. * This event loop handles events from its event queue by trying to transmit them
  54. * to a remote virtual machine.
  55. *
  56. * Concurrent processing behaviour using a CSP-like nondeterministic select:
  57. * execute = select {
  58. * ?receive(event) => handle(event)
  59. * [] ?interrupted() => retractOutbox()
  60. * }
  61. * handle(e) = select {
  62. * connected => transmit(e)
  63. * [] ?interrupted() => putBack(e); retractOutbox()
  64. * }
  65. *
  66. * @author tvcutsem
  67. */
  68. public final class ELFarReference extends EventLoop implements ConnectionListener {
  69. /**
  70. * When the {@link ELActor} owning this far reference wants to reify the event queue
  71. * as an outbox, it will 'interrupt' this event loop by setting this field to a
  72. * non-null value.
  73. * <p>
  74. * The {@link #handle(Event)} method tests for the presence of such an interrupt and
  75. * will call the {@link #handleRetractRequest()} method if this field is set.
  76. * <p>
  77. * Marked volatile because this variable is read/written by multiple threads without
  78. * synchronizing.
  79. */
  80. private volatile BlockingFuture outboxFuture_ = null;
  81. /**
  82. * the actor to which this far ref belongs, i.e. the only event loop that will schedule
  83. * transmission events into this event loop's event queue
  84. */
  85. private final ELActor owner_;
  86. /** the first-class AT language value wrapping this event loop */
  87. private final NATRemoteFarRef farRef_;
  88. //TODO MAKE HIS farRef weak. But then, we have a garbage cycle TO THINK ABOUT!!
  89. //private final WeakReference farRef_;
  90. /** the <i>wire representation</i> of the remote receiver of my messages */
  91. private final ATObjectID destination_;
  92. /** the network layer used to actually transmit an AmbientTalk message */
  93. private final CommunicationBus dispatcher_;
  94. /**
  95. * A state variable denoting whether the far reference denoted by this
  96. * event loop is 'connected' or 'disconnected' from its remote object.
  97. *
  98. * If this variable is set to <tt>false</tt>, the event loop will block
  99. * and stop processing new events until it is set to <tt>true</tt> again.
  100. */
  101. private boolean connected_;
  102. public ELFarReference(ATObjectID destination, ELActor owner, NATRemoteFarRef ref) {
  103. super("far reference " + destination);
  104. farRef_ = ref;
  105. // farRef_ = new WeakReference(ref);
  106. destination_ = destination;
  107. owner_ = owner;
  108. connected_ = true;
  109. // register the remote reference with the MembershipNotifier to keep track
  110. // of the state of the connection with the remote VM
  111. owner_.getHost().connectionManager_.addConnectionListener(destination_.getVirtualMachineId(), this);
  112. dispatcher_ = owner_.getHost().communicationBus_;
  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. public ATObjectID getDestination() {
  257. return destination_;
  258. }
  259. /* ========================================================
  260. * == Implementation of the ConnectionListener interface ==
  261. * ========================================================
  262. */
  263. public synchronized void connected() {
  264. // sanity check: don't connect twice
  265. if (!connected_) {
  266. Logging.RemoteRef_LOG.info(this + ": reconnected to " + destination_);
  267. connected_ = true;
  268. this.notify();
  269. farRef_.notifyConnected();
  270. }
  271. }
  272. public synchronized void disconnected() {
  273. // sanity check: don't disconnect twice
  274. if (connected_) {
  275. // Will only take effect when next trying to send a message
  276. // If currently sending, the message will time out first.
  277. Logging.RemoteRef_LOG.info(this + ": disconnected from " + destination_);
  278. connected_ = false;
  279. farRef_.notifyDisconnected();
  280. }
  281. }
  282. public synchronized void takenOffline(){
  283. Logging.RemoteRef_LOG.info( this + ": remote object taken offline");
  284. connected_ = false;
  285. farRef_.notifyTakenOffline();
  286. }
  287. /**
  288. * Overrides the default event handling strategy of this event loop.
  289. * It is no longer possible to simply block and wait for a new event
  290. * by performing {@link EventQueue#dequeue()} because this event loop
  291. * can be triggered by two kinds of wake-up calls, either:
  292. * <ul>
  293. * <li>a new event arrives in the event queue, or
  294. * <li>the actor owning this far reference interrupts it to flush
  295. * its event queue into a reified outbox representation
  296. * </ul>
  297. *
  298. * Therefore, this event loop synchronises on two different boolean
  299. * conditions. When exiting the <tt>wait</tt>-loop, the event loop has
  300. * to check which condition woke it up:
  301. * <ul>
  302. * <li>if it was an incoming event, handle it
  303. * <li>if it was interrupted by the owning actor, honor the interrupt
  304. * </ul>
  305. *
  306. * Note that while executing, the event loop takes a lock on itself!
  307. * This synchronizes event processing with state transition notifications
  308. * via the {@link #connected()} and {@link #disconnected()} methods.
  309. */
  310. protected void execute() {
  311. synchronized (this) {
  312. while (eventQueue_.isEmpty() && outboxFuture_ == null) {
  313. try {
  314. this.wait();
  315. } catch (InterruptedException e) {
  316. if (askedToStop_) {
  317. return; // fall through and make the thread die
  318. }
  319. }
  320. }
  321. if (outboxFuture_ != null) {
  322. handleRetractRequest();
  323. } else { // if(!eventQueue_.isEmpty()) {
  324. try {
  325. handle(eventQueue_.dequeue());
  326. } catch (InterruptedException e) {
  327. // fall through, perhaps the thread was notified
  328. // that it had to stop
  329. }
  330. }
  331. }
  332. }
  333. private Address getDestinationVMAddress() {
  334. return owner_.getHost().vmAddressBook_.getAddressOf(destination_.getVirtualMachineId());
  335. }
  336. /**
  337. * Signals the far reference that its owning actor has requested to retract unsent
  338. * messages. The interrupt will be handled as soon as the processing of the current
  339. * event has finished.
  340. * @return a blocking future the ELActor thread can wait on.
  341. */
  342. private BlockingFuture setRetractingFuture() {
  343. // first assign future to a local variable to avoid race conditions on writing to the outboxFuture_ variable!
  344. final BlockingFuture future = new BlockingFuture();
  345. outboxFuture_ = future;
  346. // the reception of a new interrupt may awaken a sleeping ELFarReference
  347. // thread, so we interrupt them, forcing them to reevaluate their conditions
  348. processor_.interrupt();
  349. return future;
  350. }
  351. protected void finalize() throws Throwable{
  352. Logging.RemoteRef_LOG.info(this + ": ELFARREF STOPPED!!");
  353. }
  354. }