/interpreter/tags/at2-build270707/src/edu/vub/at/actors/natives/ELFarReference.java

http://ambienttalk.googlecode.com/ · Java · 393 lines · 160 code · 43 blank · 190 comment · 20 complexity · 89690bb492ce923865f06b803189b1d3 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 edu.vub.at.actors.ATAsyncMessage;
  30. import edu.vub.at.actors.eventloops.BlockingFuture;
  31. import edu.vub.at.actors.eventloops.Event;
  32. import edu.vub.at.actors.eventloops.EventLoop;
  33. import edu.vub.at.actors.eventloops.EventQueue;
  34. import edu.vub.at.actors.id.ATObjectID;
  35. import edu.vub.at.actors.net.ConnectionListener;
  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. import java.lang.ref.WeakReference;
  47. import java.util.Vector;
  48. /**
  49. * An instance of the class ELFarReference represents the event loop processor for
  50. * a remote far reference. That is, the event queue of this event loop serves as
  51. * an 'outbox' which is dedicated for storing all messages sent to a particular
  52. * receiver object hosted by a remote virtual machine.
  53. *
  54. * This event loop handles events from its event queue by trying to transmit them
  55. * to a remote virtual machine.
  56. *
  57. * Concurrent processing behaviour using a CSP-like nondeterministic select:
  58. * execute = select {
  59. * ?receive(event) => handle(event)
  60. * [] ?interrupted() => retractOutbox()
  61. * }
  62. * handle(e) = select {
  63. * connected => transmit(e)
  64. * [] ?interrupted() => putBack(e); retractOutbox()
  65. * }
  66. *
  67. * @author tvcutsem
  68. */
  69. public final class ELFarReference extends EventLoop implements ConnectionListener {
  70. /**
  71. * When the {@link ELActor} owning this far reference wants to reify the event queue
  72. * as an outbox, it will 'interrupt' this event loop by setting this field to a
  73. * non-null value.
  74. * <p>
  75. * The {@link #handle(Event)} method tests for the presence of such an interrupt and
  76. * will call the {@link #handleRetractRequest()} method if this field is set.
  77. * <p>
  78. * Marked volatile because this variable is read/written by multiple threads without
  79. * synchronizing.
  80. */
  81. private volatile BlockingFuture outboxFuture_ = null;
  82. /**
  83. * the actor to which this far ref belongs, i.e. the only event loop that will schedule
  84. * transmission events into this event loop's event queue
  85. */
  86. private final ELActor owner_;
  87. /** the first-class AT language value wrapping this event loop */
  88. private final NATRemoteFarRef farRef_;
  89. //TODO MAKE HIS farRef weak. But then, we have a garbage cycle TO THINK ABOUT!!
  90. //private final WeakReference farRef_;
  91. /** the <i>wire representation</i> of the remote receiver of my messages */
  92. private final ATObjectID destination_;
  93. /** the network layer used to actually transmit an AmbientTalk message */
  94. private final CommunicationBus dispatcher_;
  95. /**
  96. * A state variable denoting whether the far reference denoted by this
  97. * event loop is 'connected' or 'disconnected' from its remote object.
  98. *
  99. * If this variable is set to <tt>false</tt>, the event loop will block
  100. * and stop processing new events until it is set to <tt>true</tt> again.
  101. */
  102. private boolean connected_;
  103. public ELFarReference(ATObjectID destination, ELActor owner, NATRemoteFarRef ref) {
  104. super("far reference " + destination);
  105. farRef_ = ref;
  106. // farRef_ = new WeakReference(ref);
  107. destination_ = destination;
  108. owner_ = owner;
  109. connected_ = true;
  110. // register the remote reference with the MembershipNotifier to keep track
  111. // of the state of the connection with the remote VM
  112. owner_.getHost().connectionManager_.addConnectionListener(destination_.getVirtualMachineId(), this);
  113. dispatcher_ = owner_.getHost().communicationBus_;
  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(ATAsyncMessage)}
  181. * @author smostinc
  182. */
  183. private class TransmissionEvent extends Event {
  184. public final Packet serializedMessage_;
  185. // Called by ELActor
  186. public TransmissionEvent(ATAsyncMessage msg) throws XIOProblem {
  187. super("transmit("+msg+")");
  188. serializedMessage_ = new Packet(msg.toString(), msg);
  189. }
  190. /**
  191. * This code is executed by the {@link ELFarReference} event loop.
  192. */
  193. public void process(Object owner) {
  194. Address destAddress = getDestinationVMAddress();
  195. if (destAddress != null) {
  196. try {
  197. new CMDTransmitATMessage(destination_.getActorId(), serializedMessage_).send(
  198. dispatcher_, destAddress);
  199. // getting here means the message was succesfully transmitted
  200. } catch (NetworkException e) {
  201. // TODO: the message MAY have been transmitted! (i.e. an orphan might have
  202. // been created: should make this more explicit to the AT programmer)
  203. Logging.RemoteRef_LOG.warn(this
  204. + ": timeout while trying to transmit message, retrying");
  205. receivePrioritized(this);
  206. }
  207. } else {
  208. Logging.RemoteRef_LOG.info(this + ": suspected a disconnection from " +
  209. destination_ + " because destination VM ID was not found in address book");
  210. connected_ = false;
  211. receivePrioritized(this);
  212. }
  213. }
  214. }
  215. /**
  216. * Inserts an AmbientTalk message into this far reference's outbox.
  217. */
  218. public void event_transmit(final ATAsyncMessage msg) throws XIOProblem {
  219. // the message will still be serialized in the actor's thread
  220. receive(new TransmissionEvent(msg));
  221. // the reception of a new event may awaken a sleeping ELFarReference thread,
  222. // so we interrupt the processor, forcing it to reevaluate its conditions
  223. // we don't use wait/notify because in order to notify the event loop, we
  224. // would require a lock on it, which might cause this actor to block on
  225. // remote communication. Therefore, we use the interrupt mechanism instead.
  226. processor_.interrupt();
  227. }
  228. /**
  229. * Interrupts this event loop by issuing a request for flushing
  230. * its event queue.
  231. *
  232. * This code is executed by the event loop thread of the {@link #owner_}!
  233. *
  234. * @return a table of copies for the messages currently in the outbox
  235. */
  236. public ATTable retractUnsentMessages() throws InterpreterException {
  237. BlockingFuture eventVectorF = setRetractingFuture();
  238. Vector events = null;
  239. try {
  240. // actor has to wait a bit until the event loop has stopped processing
  241. events = (Vector) eventVectorF.get();
  242. } catch(Exception e) {
  243. // should never occur!
  244. e.printStackTrace();
  245. return NATTable.EMPTY;
  246. }
  247. ATObject[] messages = new ATObject[events.size()];
  248. for(int i = 0; i < events.size(); i++) {
  249. TransmissionEvent current = (TransmissionEvent)events.get(i);
  250. messages[i] = current.serializedMessage_.unpack();
  251. }
  252. return NATTable.atValue(messages);
  253. }
  254. public ATObjectID getDestination() {
  255. return destination_;
  256. }
  257. /* ========================================================
  258. * == Implementation of the ConnectionListener interface ==
  259. * ========================================================
  260. */
  261. public synchronized void connected() {
  262. // sanity check: don't connect twice
  263. if (!connected_) {
  264. Logging.RemoteRef_LOG.info(this + ": reconnected to " + destination_);
  265. connected_ = true;
  266. this.notify();
  267. farRef_.notifyConnected();
  268. }
  269. }
  270. public synchronized void disconnected() {
  271. // sanity check: don't disconnect twice
  272. if (connected_) {
  273. // Will only take effect when next trying to send a message
  274. // If currently sending, the message will time out first.
  275. Logging.RemoteRef_LOG.info(this + ": disconnected from " + destination_);
  276. connected_ = false;
  277. farRef_.notifyDisconnected();
  278. }
  279. }
  280. public synchronized void takenOffline(){
  281. Logging.RemoteRef_LOG.info( this + ": remote object taken offline");
  282. connected_ = false;
  283. farRef_.notifyTakenOffline();
  284. }
  285. /**
  286. * Overrides the default event handling strategy of this event loop.
  287. * It is no longer possible to simply block and wait for a new event
  288. * by performing {@link EventQueue#dequeue()} because this event loop
  289. * can be triggered by two kinds of wake-up calls, either:
  290. * <ul>
  291. * <li>a new event arrives in the event queue, or
  292. * <li>the actor owning this far reference interrupts it to flush
  293. * its event queue into a reified outbox representation
  294. * </ul>
  295. *
  296. * Therefore, this event loop synchronises on two different boolean
  297. * conditions. When exiting the <tt>wait</tt>-loop, the event loop has
  298. * to check which condition woke it up:
  299. * <ul>
  300. * <li>if it was an incoming event, handle it
  301. * <li>if it was interrupted by the owning actor, honor the interrupt
  302. * </ul>
  303. *
  304. * Note that while executing, the event loop takes a lock on itself!
  305. * This synchronizes event processing with state transition notifications
  306. * via the {@link #connected()} and {@link #disconnected()} methods.
  307. */
  308. protected void execute() {
  309. synchronized (this) {
  310. while (eventQueue_.isEmpty() && outboxFuture_ == null) {
  311. try {
  312. this.wait();
  313. } catch (InterruptedException e) {
  314. if (askedToStop_) {
  315. return; // fall through and make the thread die
  316. }
  317. }
  318. }
  319. if (outboxFuture_ != null) {
  320. handleRetractRequest();
  321. } else { // if(!eventQueue_.isEmpty()) {
  322. try {
  323. handle(eventQueue_.dequeue());
  324. } catch (InterruptedException e) {
  325. // fall through, perhaps the thread was notified
  326. // that it had to stop
  327. }
  328. }
  329. }
  330. }
  331. private Address getDestinationVMAddress() {
  332. return owner_.getHost().vmAddressBook_.getAddressOf(destination_.getVirtualMachineId());
  333. }
  334. /**
  335. * Signals the far reference that its owning actor has requested to retract unsent
  336. * messages. The interrupt will be handled as soon as the processing of the current
  337. * event has finished.
  338. * @return a blocking future the ELActor thread can wait on.
  339. */
  340. private BlockingFuture setRetractingFuture() {
  341. outboxFuture_ = new BlockingFuture();
  342. // the reception of a new interrupt may awaken a sleeping ELFarReference
  343. // thread, so we interrupt them, forcing them to reevaluate their conditions
  344. processor_.interrupt();
  345. return outboxFuture_;
  346. }
  347. protected void finalize() throws Throwable{
  348. Logging.RemoteRef_LOG.info(this + ": ELFARREF STOPPED!!");
  349. }
  350. }