/interpreter/tags/at2dist220411/src/edu/vub/at/actors/natives/FarReferencesThreadPool.java

http://ambienttalk.googlecode.com/ · Java · 292 lines · 149 code · 19 blank · 124 comment · 10 complexity · 6843211225bbba89d5e369f09c0853f8 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * (c) Software Languages Lab, 2006 - 2010
  4. *
  5. * Permission is hereby granted, free of charge, to any person
  6. * obtaining a copy of this software and associated documentation
  7. * files (the "Software"), to deal in the Software without
  8. * restriction, including without limitation the rights to use,
  9. * copy, modify, merge, publish, distribute, sublicense, and/or
  10. * sell copies of the Software, and to permit persons to whom the
  11. * Software is furnished to do so, subject to the following
  12. * conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  24. * OTHER DEALINGS IN THE SOFTWARE.
  25. *
  26. **/
  27. package edu.vub.at.actors.natives;
  28. import java.util.HashMap;
  29. import java.util.concurrent.Callable;
  30. import java.util.concurrent.ExecutorService;
  31. import java.util.concurrent.Executors;
  32. import edu.vub.at.actors.ATFarReference;
  33. import edu.vub.at.actors.ATLetter;
  34. import edu.vub.at.actors.eventloops.BlockingFuture;
  35. import edu.vub.at.actors.eventloops.Event;
  36. import edu.vub.at.actors.id.ATObjectID;
  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.XTypeMismatch;
  43. import edu.vub.at.objects.ATObject;
  44. import edu.vub.at.objects.ATTable;
  45. import edu.vub.at.objects.natives.NATNil;
  46. import edu.vub.at.util.logging.Logging;
  47. /**
  48. * An instance of the class FarReferencesThreadPool represents a pool of threads for
  49. * all remote far references belonging to an ELVirtualMachine.
  50. *
  51. * ELDiscoveryActor is initialized without a FarReferencesThreadPool as it only
  52. * holds local far references to closures corresponding to when(er):discovered listeners.
  53. *
  54. * FarReferencesThreadPool works together with NATRemoteFarRef to ensure that messages are
  55. * sent in the order they are received by a NATFarReference. More concretely:
  56. * -ELActor owner of the far reference enqueues a message to be transmitted in its outbox when
  57. * calls NATFarReference.meta_receive().
  58. * -This will trigger an event_serve in FarReferencesThreadPool and a thread on it will dequeue
  59. * a letter from the outbox and transmits its message.
  60. * -After transmitting a message, event_serve() is called to check if there are other letters to be served.
  61. * -After a reconnect, event_serve() is also called to serve letters buffered by ELActor during a disconnection.
  62. *
  63. * This behaviour is similar to an EventLoop, just that it may not be the same thread
  64. * removing from the outbox, but one of the thread pool.
  65. * Removals from the NATFarReference outbox will be always executed by a thread of the pool
  66. * while additions can be executed either by ELActor owner (due to meta_receive)
  67. * or a thread of the pool if the message transmission failed and needs to be put back to the outbox.
  68. */
  69. public final class FarReferencesThreadPool {
  70. /**
  71. * The virtual machine to which this far ref pool belongs.
  72. * One can get the dispatcher from ELVirtualMachine, but keep for optimization.
  73. */
  74. private final ELVirtualMachine host_;
  75. /** the network layer used to actually transmit an AmbientTalk message */
  76. private final CommunicationBus dispatcher_;
  77. /** the pool of threads**/
  78. private final ExecutorService pool;
  79. /**
  80. * map of reference (NATRemoteFarREf) -> future for retract requests (BlockingFuture)
  81. * to be able to synchronize a thread performing a retract operation, and
  82. * a thread transmitting a message when the request is processed.
  83. * See {@link sync_event_retractUnsentMessages}
  84. */
  85. private final HashMap<ATFarReference, BlockingFuture> retractFutures_;
  86. public FarReferencesThreadPool(ELVirtualMachine host) {
  87. host_ = host;
  88. dispatcher_ = host_.communicationBus_;
  89. pool = Executors.newCachedThreadPool();
  90. retractFutures_ = new HashMap();
  91. }
  92. protected final void receive(Event event) {
  93. Handle h = new Handle(event);
  94. pool.execute( h);
  95. }
  96. // helper class to wrap the AT event to be
  97. // processed.
  98. class Handle implements Runnable{
  99. private Event event_;
  100. public Handle( Event event){
  101. event_ = event;
  102. }
  103. public void run() {
  104. event_.process(host_);
  105. }
  106. }
  107. /**
  108. * This is a named subclass of event, which allows access to the letter
  109. * that is being transmitted. The constructor is executed by:
  110. * ELActor sending the message when reference is connected, or
  111. * a thread in the pool that schedules this transmission event after a reconnection.
  112. */
  113. class TransmissionEvent extends Event{
  114. /** the letter containing the serialized and original message */
  115. public final ATLetter letter_;
  116. /** the <i>wire representation</i> of the remote receiver of this message */
  117. public final ATObjectID destination_;
  118. /* the first-class AT reference sending this message */
  119. public final ATFarReference reference_;
  120. public TransmissionEvent(ATFarReference reference, ATLetter letter) throws InterpreterException {
  121. super ("transmit( ["+ letter.base_receiver().asFarReference() + ","+ letter.base_message().asAsyncMessage() +"])");
  122. letter_ = letter;
  123. reference_ = reference;
  124. destination_ = reference_.asNativeFarReference().impl_getObjectId();
  125. }
  126. public void process(Object owner){
  127. Address destAddress = getDestinationVMAddress();
  128. if (destAddress != null) {
  129. try {
  130. new CMDTransmitATMessage(destination_.getActorId(), letter_.asNativeOutboxLetter().impl_getSerializedMessage()).send(
  131. dispatcher_, destAddress);
  132. // getting here means the message was succesfully transmitted
  133. reference_.asNativeRemoteFarReference().setTransmitting(false);
  134. // check if 1) there is a retract request for this reference
  135. // and afterwards 2) if another message to be transmitted.
  136. // Needed now because pool is not an event loop.
  137. handleRetractRequest(reference_);
  138. reference_.asNativeRemoteFarReference().impl_transmit();
  139. } catch (NetworkException e) {
  140. // TODO: the message MAY have been transmitted! (i.e. an orphan might have
  141. // been created: should make this more explicit to the AT programmer)
  142. // To solve this add message ids, and check you don't process twice the same message.
  143. Logging.RemoteRef_LOG.warn(reference_
  144. + ": timeout while trying to transmit message, retrying");
  145. // try to send it again, if the remote VM went offline,
  146. // next time this message is processed destAddress == null.
  147. this.process(owner);
  148. } catch (XTypeMismatch e) {
  149. Logging.RemoteRef_LOG.warn(reference_
  150. + ": unexpected type mismatch: " + e.getMessage());
  151. e.printStackTrace();
  152. } catch (InterpreterException e) {
  153. Logging.RemoteRef_LOG.warn(reference_
  154. + ": unexpected error while handling retract request after a successful transmission: " + e.getMessage());
  155. e.printStackTrace();
  156. }
  157. } else {
  158. Logging.RemoteRef_LOG.info(reference_ + ": suspected a disconnection from " +
  159. destination_ + " because destination VM ID was not found in address book");
  160. // destAddress is null is because it was removed from a event_memberLeft();
  161. try {
  162. reference_.asNativeRemoteFarReference().impl_transmitFailed(letter_);
  163. handleRetractRequest(reference_);
  164. } catch (XTypeMismatch e) {
  165. Logging.RemoteRef_LOG.warn(reference_
  166. + ": unexpected type mismatch: " + e.getMessage());
  167. e.printStackTrace();
  168. } catch (InterpreterException e) {
  169. Logging.RemoteRef_LOG.warn(reference_
  170. + ": unexpected error while handling retract request after transmission failed: " + e.getMessage());
  171. e.printStackTrace();
  172. }
  173. }
  174. }
  175. private Address getDestinationVMAddress() {
  176. return host_.vmAddressBook_.getAddressOf(destination_.getVirtualMachineId());
  177. }
  178. }
  179. /** transmitting is embedded first in another Event so that only
  180. * a thread from this thread pool will dequeue messages from the mailbox.
  181. *
  182. */
  183. public void event_serve(final ATFarReference reference) {
  184. receive(new Event("serve()") {
  185. public void process(Object owner) {
  186. try {
  187. ATObject result = reference.asNativeRemoteFarReference().impl_serve();
  188. // do not span a new thread to transmit, try to transmit it itself.
  189. // Not comparing to Evaluator.getNil() because it will return a different instance.
  190. if (!( result instanceof NATNil)) {
  191. TransmissionEvent transmit = new TransmissionEvent(reference, result.asNativeOutboxLetter());
  192. transmit.process(owner);
  193. }
  194. } catch (InterpreterException e) {
  195. Logging.RemoteRef_LOG.warn(this + ": serve() failed ", e);
  196. }
  197. }
  198. } );
  199. }
  200. /**
  201. * Signals that the owning actor of a far reference has requested to retract unsent
  202. * messages. The request will be handled as soon as the transmission of the current
  203. * letter has finished.
  204. *
  205. * Note that it cannot happen that two consecutives retracts requests arrive for the
  206. * same reference since the ELActor is blocked on a future waiting for the request
  207. * to be processed.
  208. *
  209. * @return a blocking future the ELActor thread can wait on.
  210. */
  211. private BlockingFuture setRetractingFuture(ATFarReference reference) {
  212. // first assign future to a local variable to avoid race conditions on writing to the outboxFuture_ variable!
  213. final BlockingFuture future = new BlockingFuture();
  214. synchronized(this) {
  215. // synchronized access because different thread in the pool could
  216. // be processing retractUnsentMessages requests for different references.
  217. retractFutures_.put(reference, future);
  218. }
  219. return future;
  220. }
  221. /**
  222. * Checks in the {@link #retractFutures_} for pending retractUnsentMessages request
  223. * for a given reference.
  224. *
  225. * After handling the retract request, the entry in the {@link #retractFutures_} for
  226. * the given reference is removed. This is extremely important because it signifies
  227. * that there is no more pending retract request for this reference.
  228. */
  229. private synchronized void handleRetractRequest(ATFarReference reference) throws XTypeMismatch, InterpreterException{
  230. BlockingFuture retractFuture = retractFutures_.remove(reference);
  231. if (retractFuture != null) {
  232. retractFuture.resolve(reference.asNativeFarReference().impl_retractUnsentMessages());
  233. }
  234. }
  235. public ATTable sync_event_retractUnsentMessages(final ATFarReference reference) throws InterpreterException {
  236. try {
  237. return (ATTable) pool.submit(new Callable() {
  238. public Object call() throws Exception {
  239. /** we need to synchronize the whole block to make sure that getTransmitting and
  240. * adding the retract future to the map is done atomically to avoid that the
  241. * ELActor is block till the next transmission event because the transmission
  242. * thread didn't see the request because:
  243. * Transmission -thread 1 Retract Request - thread 2
  244. * T1 getTransmit() -> true
  245. * T2 setTransmit(false)
  246. * T3 handleRetractRequest()
  247. * T4 setRetractingFuture(reference);
  248. *
  249. */
  250. synchronized (this) {
  251. if ( reference.asNativeRemoteFarReference().getTransmitting()){
  252. // if there is a thread transmitting a message for this reference:
  253. // resolve the future with a BlockingFuture to wait for the result of the transmission.
  254. BlockingFuture future = setRetractingFuture(reference);
  255. return future.get();
  256. } else {
  257. // if there is no thread transmitting a message for this reference:
  258. // resolve the future immediately with the content of its oubox.
  259. return reference.asNativeFarReference().impl_retractUnsentMessages();
  260. }
  261. }
  262. }
  263. }).get();
  264. } catch (Exception e) {
  265. if (e instanceof InterpreterException) {
  266. throw (InterpreterException) e;
  267. } else {
  268. Logging.RemoteRef_LOG.fatal("Unexpected Java exception: "+e.getMessage(), e);
  269. throw new RuntimeException("Unexpected exception: "+e);
  270. }
  271. }
  272. }
  273. }