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

http://ambienttalk.googlecode.com/ · Java · 515 lines · 267 code · 47 blank · 201 comment · 9 complexity · a12349f3ae13e656c675020735867135 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * ELActor.java created on 27-dec-2006 at 16:17:23
  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.lang.reflect.Method;
  30. import java.util.EventListener;
  31. import edu.vub.at.actors.ATActorMirror;
  32. import edu.vub.at.actors.ATAsyncMessage;
  33. import edu.vub.at.actors.ATFarReference;
  34. import edu.vub.at.actors.eventloops.BlockingFuture;
  35. import edu.vub.at.actors.eventloops.Callable;
  36. import edu.vub.at.actors.eventloops.Event;
  37. import edu.vub.at.actors.eventloops.EventLoop;
  38. import edu.vub.at.actors.id.ATObjectID;
  39. import edu.vub.at.actors.id.ActorID;
  40. import edu.vub.at.actors.net.comm.Address;
  41. import edu.vub.at.eval.Evaluator;
  42. import edu.vub.at.exceptions.InterpreterException;
  43. import edu.vub.at.exceptions.XClassNotFound;
  44. import edu.vub.at.exceptions.XIOProblem;
  45. import edu.vub.at.exceptions.XIllegalOperation;
  46. import edu.vub.at.exceptions.XObjectOffline;
  47. import edu.vub.at.objects.ATAbstractGrammar;
  48. import edu.vub.at.objects.ATMethod;
  49. import edu.vub.at.objects.ATObject;
  50. import edu.vub.at.objects.ATTable;
  51. import edu.vub.at.objects.ATTypeTag;
  52. import edu.vub.at.objects.mirrors.Reflection;
  53. import edu.vub.at.objects.natives.NATContext;
  54. import edu.vub.at.objects.natives.NATObject;
  55. import edu.vub.at.objects.natives.NATTable;
  56. import edu.vub.at.objects.natives.OBJLexicalRoot;
  57. import edu.vub.at.objects.symbiosis.Symbiosis;
  58. import edu.vub.at.util.logging.Logging;
  59. /**
  60. * An instance of the class ELActor represents a programmer-defined
  61. * AmbientTalk/2 actor. The event queue of the actor event loop serves as the
  62. * actor's 'meta-level' queue.
  63. *
  64. * The events in the 'meta-level' queue are handled by the actor's mirror object.
  65. * This mirror is normally an instance of NATActorMirror, but it can be any
  66. * programmer-defined object that adheres to the ATActorMirror interface.
  67. *
  68. * @author tvcutsem
  69. */
  70. public class ELActor extends EventLoop {
  71. /**
  72. * A thread-local variable that contains the 'default actor' to use
  73. * when there is currently no ELActor event loop thread running.
  74. * This is primarily useful for performing unit tests where an actor
  75. * is automatically created when actor semantics is required.
  76. *
  77. * A warning is printed to the log because using the default actor should
  78. * only be used for testing purposes.
  79. */
  80. private static final ThreadLocal _DEFAULT_ACTOR_ = new ThreadLocal() {
  81. protected synchronized Object initialValue() {
  82. Logging.Actor_LOG.warn("Creating a default actor for thread " + Thread.currentThread());
  83. try {
  84. ELVirtualMachine host = new ELVirtualMachine(
  85. Evaluator.getNil(),
  86. new SharedActorField[] { },
  87. ELVirtualMachine._DEFAULT_GROUP_NAME_);
  88. return host.createEmptyActor().getFarHost();
  89. } catch (InterpreterException e) {
  90. throw new RuntimeException("Failed to initialize default actor: " + e.getMessage());
  91. }
  92. }
  93. };
  94. /**
  95. * Retrieves the currently running actor. If there is no running actor thread,
  96. * this returns the value stored in the thread-local default actor field.
  97. */
  98. public static final ELActor currentActor() {
  99. try {
  100. return ((ELActor) EventLoop.currentEventLoop());
  101. } catch (ClassCastException e) {
  102. // current event loop is not an actor event loop
  103. } catch (IllegalStateException e) {
  104. // current thread is not an event loop
  105. }
  106. Logging.Actor_LOG.warn("Asked for an actor in non-actor thread " + Thread.currentThread());
  107. return (ELActor) _DEFAULT_ACTOR_.get();
  108. }
  109. private ATActorMirror mirror_;
  110. private final ActorID id_;
  111. protected final ELVirtualMachine host_;
  112. protected final ReceptionistsSet receptionists_;
  113. /*
  114. * This object is created when the actor is initialized: i.e. it is the passed
  115. * version of the isolate that was passed to the actor: primitive by the creating actor.
  116. */
  117. private ATObject behaviour_;
  118. public ELActor(ATActorMirror mirror, ELVirtualMachine host) {
  119. super("actor " + mirror.toString());
  120. id_ = new ActorID();
  121. mirror_ = mirror;
  122. host_ = host;
  123. receptionists_ = new ReceptionistsSet(this);
  124. }
  125. /** constructor dedicated to initialization of discovery actor */
  126. protected ELActor(ELVirtualMachine host) {
  127. super("discovery actor");
  128. id_ = new ActorID();
  129. mirror_ = new NATActorMirror(host);
  130. host_ = host;
  131. receptionists_ = new ReceptionistsSet(this);
  132. }
  133. /**
  134. * Actor event loops handle events by allowing the meta-level events to
  135. * process themselves.
  136. */
  137. public void handle(Event event) {
  138. event.process(mirror_);
  139. Evaluator.getDataflowEngine().propagatePendingUpdates();
  140. }
  141. public ATActorMirror getImplicitActorMirror() { return mirror_; }
  142. public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; }
  143. public ELVirtualMachine getHost() {
  144. return host_;
  145. }
  146. public ATObject getBehaviour() {
  147. return behaviour_;
  148. }
  149. public ActorID getActorID() {
  150. return id_;
  151. }
  152. public Thread getExecutor() {
  153. return processor_;
  154. }
  155. /**
  156. * Takes offline a given remote object such that it is no longer remotely accessible.
  157. * @param object a **far?** reference to the object to export
  158. * @throws XIllegalOperation if the passed object is not part of the export table - i.e. non-remotely accessible.
  159. */
  160. public void takeOffline(ATObject object) throws InterpreterException {
  161. // receptionist set will check whether ATObject is really remote to me
  162. receptionists_.takeOfflineObject(object);
  163. }
  164. /**
  165. * Resolve the given object id into a local reference. There are three cases to
  166. * consider:
  167. * A) The given id designates an object local to this actor: the returned object
  168. * will be a **near** reference to the object (i.e. the object itself)
  169. * B) The given id designates a far (non-local) object that lives in the same
  170. * address space as this actor: the returned object wil be a **far** reference
  171. * to the object.
  172. * C) The given id designates a far object that lives on a remote machine: the
  173. * returned object will be a **far** and **remote** reference to the object.
  174. *
  175. * @param id the identifier of the object to resolve
  176. * @return a near or far reference to the object, depending on where the designated object lives
  177. */
  178. public ATObject resolve(ATObjectID id, ATTypeTag[] types) throws XObjectOffline {
  179. return receptionists_.resolveObject(id, types);
  180. }
  181. /* -----------------------------
  182. * -- Initialisation Protocol --
  183. * ----------------------------- */
  184. /**
  185. * Initialises the root using the contents of the init file stored by
  186. * the hosting virtual machine.
  187. * @throws InterpreterException
  188. */
  189. protected void initRootObject() throws InterpreterException {
  190. ATAbstractGrammar initialisationCode = host_.getInitialisationCode();
  191. // evaluate the initialization code in the context of the global scope
  192. NATObject globalScope = Evaluator.getGlobalLexicalScope();
  193. NATContext initCtx = new NATContext(globalScope, globalScope);
  194. initialisationCode.meta_eval(initCtx);
  195. }
  196. /**
  197. * Initialises various fields in the lexical root of the actor, which are defined in the
  198. * context of every actor. Candidates are a "system" field which allows the program to
  199. * perform IO operations or a "~" field denoting the current working directory.
  200. *
  201. * @throws InterpreterException when initialisation of a field fails
  202. */
  203. protected void initSharedFields() throws InterpreterException {
  204. SharedActorField[] fields = host_.getFieldsToInitialize();
  205. NATObject globalScope = Evaluator.getGlobalLexicalScope();
  206. for (int i = 0; i < fields.length; i++) {
  207. SharedActorField field = fields[i];
  208. ATObject value = field.initialize();
  209. if (value != null) {
  210. globalScope.meta_defineField(field.getName(), value);
  211. }
  212. }
  213. }
  214. // Events to be processed by the actor event loop
  215. /**
  216. * The initial event sent by the actor mirror to its event loop to intialize itself.
  217. * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour.
  218. * @param parametersPkt the serialized parameters for the initialization code
  219. * @param initcodePkt the serialized initialization code (e.g. the code in 'actor: { code }')
  220. */
  221. protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) {
  222. receive(new Event("init("+this+")") {
  223. public void process(Object byMyself) {
  224. try {
  225. behaviour_ = new NATObject();
  226. // pass far ref to behaviour to creator actor who is waiting for this
  227. future.resolve(receptionists_.exportObject(behaviour_));
  228. // !! WARNING: the following code is also duplicated in
  229. // ELDiscoveryActor's event_init. If this code is modified, don't
  230. // forget to modify that of the discovery actor as well !!
  231. // initialize lexically visible fields
  232. initSharedFields();
  233. // go on to initialize the root and all lexically visible fields
  234. initRootObject();
  235. ATTable params = parametersPkt.unpack().asTable();
  236. ATMethod initCode = initcodePkt.unpack().asMethod();
  237. // initialize the behaviour using the parameters and the code
  238. initCode.base_applyInScope(params, new NATContext(behaviour_, behaviour_));
  239. } catch (InterpreterException e) {
  240. Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e);
  241. }
  242. }
  243. });
  244. }
  245. /**
  246. * The main entry point for any asynchronous self-sends.
  247. * Asynchronous self-sends (i.e. intra-actor sends) do not undergo any form of parameter passing,
  248. * there is no need to serialize and deserialize the message parameter in a Packet.
  249. *
  250. * When an actor receives an asynchronous message for a given receiver, it delegates control
  251. * to the message itself by means of the message's <tt>process</tt> method.
  252. * @throws InterpreterException
  253. */
  254. public void acceptSelfSend(final ATObject receiver, final ATAsyncMessage msg) throws InterpreterException {
  255. // This is the only place where messages are scheduled
  256. // The receiver is always a local object, receive has
  257. // already been invoked.
  258. mirror_.base_schedule(receiver, msg);
  259. // signal a serve event for every message that is accepted
  260. this.event_serve();
  261. }
  262. /**
  263. * The main entry point for any asynchronous messages sent to this actor
  264. * by external sources.
  265. * @param sender address of the sending actor, used to notify when the receiver has gone offline.
  266. * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
  267. */
  268. public void event_remoteAccept(final Address sender, final Packet serializedMessage) {
  269. receive(new Event("remoteAccept("+serializedMessage+")") {
  270. public void process(Object myActorMirror) {
  271. try {
  272. // receive a pair [receiver, message]
  273. ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
  274. ATObject receiver = pair[0];
  275. ATAsyncMessage msg = pair[1].asAsyncMessage();
  276. performAccept(receiver, msg);
  277. } catch (XObjectOffline e) {
  278. host_.event_objectTakenOffline(e.getObjectId(), sender);
  279. Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
  280. } catch (InterpreterException e) {
  281. Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
  282. }
  283. }
  284. });
  285. }
  286. /**
  287. * The main entry point for any asynchronous messages sent to this actor
  288. * by local actors.
  289. * @param ref the local reference of the sending actor, used to notify when the receiver has gone offline.
  290. * @param serializedMessage the asynchronous AmbientTalk base-level message to enqueue
  291. */
  292. public void event_localAccept(final NATLocalFarRef ref, final Packet serializedMessage) {
  293. receive(new Event("localAccept("+serializedMessage+")") {
  294. public void process(Object myActorMirror) {
  295. try {
  296. // receive a pair [receiver, message]
  297. ATObject[] pair = serializedMessage.unpack().asNativeTable().elements_;
  298. ATObject receiver = pair[0];
  299. ATAsyncMessage msg = pair[1].asAsyncMessage();
  300. performAccept(receiver, msg);
  301. } catch (XObjectOffline e) {
  302. ref.notifyTakenOffline();
  303. Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
  304. } catch (InterpreterException e) {
  305. Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e);
  306. }
  307. }
  308. });
  309. }
  310. public void event_serve() {
  311. receive(new Event("serve()") {
  312. public void process(Object myActorMirror) {
  313. try {
  314. ATObject result = mirror_.base_serve();
  315. // TODO what to do with return value?
  316. Logging.Actor_LOG.debug(mirror_ + ": serve() returned " + result);
  317. } catch (InterpreterException e) {
  318. // TODO what to do with exception?
  319. e.printAmbientTalkStackTrace(System.err);
  320. Logging.Actor_LOG.error(mirror_ + ": serve() failed ", e);
  321. }
  322. }
  323. });
  324. }
  325. private void performAccept(ATObject receiver, ATAsyncMessage msg) {
  326. try {
  327. ATObject result = mirror_.base_receive(receiver, msg);
  328. // TODO what to do with return value?
  329. Logging.Actor_LOG.debug(mirror_ + ": scheduling "+ msg + " returned " + result);
  330. // signal a serve event for every message that is accepted
  331. event_serve();
  332. } catch (InterpreterException e) {
  333. // TODO what to do with exception?
  334. e.printAmbientTalkStackTrace(System.err);
  335. Logging.Actor_LOG.error(mirror_ + ": scheduling "+ msg + " failed ", e);
  336. }
  337. }
  338. /**
  339. * This method is invoked by a coercer in order to schedule a purely asynchronous symbiotic invocation
  340. * from the Java world.
  341. *
  342. * This method schedules the call for asynchronous execution. Its return value and or raised exceptions
  343. * are ignored. This method should only be used for {@link Method} objects whose return type is <tt>void</tt>
  344. * and whose declaring class is a subtype of {@link EventListener}. It represents asynchronous method
  345. * invocations from the Java world to the AmbientTalk world.
  346. *
  347. * @param principal the AmbientTalk object owned by this actor on which to invoke the method
  348. * @param method the Java method that was symbiotically invoked on the principal
  349. * @param args the arguments to the Java method call, already converted into AmbientTalk values
  350. */
  351. public void event_symbioticInvocation(final ATObject principal, final Method method, final ATObject[] args) {
  352. receive(new Event("asyncSymbioticInv of "+method.getName()) {
  353. public void process(Object actorMirror) {
  354. try {
  355. Reflection.downInvocation(principal, method, args);
  356. } catch (InterpreterException e) {
  357. e.printAmbientTalkStackTrace(System.err);
  358. Logging.Actor_LOG.error("asynchronous symbiotic invocation of "+method.getName()+" failed", e);
  359. }
  360. }
  361. });
  362. }
  363. /**
  364. * This method is invoked by a coercer in order to schedule a symbiotic invocation
  365. * from the Java world, which should be synchronous to the Java thread, but which
  366. * must be scheduled asynchronously to comply with the AT/2 actor model.
  367. *
  368. * The future returned by this method makes the calling (Java) thread <b>block</b> upon
  369. * accessing its value, waiting until the actor has processed the symbiotic invocation.
  370. *
  371. * @param principal the AmbientTalk object owned by this actor on which to invoke the method
  372. * @param meth the Java method that was symbiotically invoked on the principal
  373. * @param args the arguments to the Java method call, already converted into AmbientTalk values
  374. * @return a Java future that is resolved with the result of the symbiotic invocation
  375. * @throws Exception if the symbiotic invocation fails
  376. */
  377. public BlockingFuture sync_event_symbioticInvocation(final ATObject principal, final Method meth, final ATObject[] args) throws Exception {
  378. return receiveAndReturnFuture("syncSymbioticInv of " + meth.getName(), new Callable() {
  379. public Object call(Object actorMirror) throws Exception {
  380. Class targetType = meth.getReturnType();
  381. ATObject[] actualArgs = args;
  382. // if the return type is BlockingFuture, the first argument should specify the type
  383. // of the value with which BlockingFuture will be resolved
  384. if (targetType.equals(BlockingFuture.class)) {
  385. if ((meth.getParameterTypes().length > 0) && (meth.getParameterTypes()[0].equals(Class.class))) {
  386. targetType = args[0].asJavaClassUnderSymbiosis().getWrappedClass();
  387. // drop first argument, it only exists to specify the targetType
  388. ATObject[] newArgs = new ATObject[args.length-1];
  389. System.arraycopy(args, 1, newArgs, 0, newArgs.length);
  390. actualArgs = newArgs;
  391. }
  392. }
  393. ATObject result = Reflection.downInvocation(principal, meth, actualArgs);
  394. // SUPPORT FOR FUTURES
  395. if (Symbiosis.isAmbientTalkFuture(result)) {
  396. Logging.Actor_LOG.debug("Symbiotic futures: symbiotic call to " + meth.getName() + " returned an AT future");
  397. return Symbiosis.ambientTalkFutureToJavaFuture(result, targetType);
  398. } else {
  399. // return the proper value immediately
  400. return Symbiosis.ambientTalkToJava(result, targetType);
  401. }
  402. }
  403. });
  404. }
  405. /**
  406. * This method should only be used for purposes such as the IAT shell or unit testing.
  407. * It allows an external thread to make this actor evaluate an arbitrary expression.
  408. *
  409. * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the
  410. * scope of its behaviour).
  411. * @return the result of the evaluation
  412. * @throws InterpreterException if the evaluation fails
  413. */
  414. public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException {
  415. try {
  416. return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() {
  417. public Object call(Object inActor) throws Exception {
  418. return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_);
  419. }
  420. });
  421. } catch (Exception e) {
  422. throw (InterpreterException) e;
  423. }
  424. }
  425. /**
  426. * This method should only be used for purposes of unit testing. It allows
  427. * arbitary code to be scheduled by external threads such as unit testing frameworks.
  428. */
  429. public Object sync_event_performTest(final Callable c) throws Exception {
  430. return (ATObject) receiveAndWait("performTest("+c+")", c);
  431. }
  432. /**
  433. * When the discovery manager receives a publication from another local actor or
  434. * another remote VM, the actor is asked to compare the incoming publication against
  435. * a subscription that it had announced previously.
  436. *
  437. * @param requiredTypePkt serialized form of the type attached to the actor's subscription
  438. * @param myHandler the closure specified as a handler for the actor's subscription
  439. * @param discoveredTypePkt serialized form of the type attached to the new publication
  440. * @param remoteServicePkt serialized form of the reference to the remote discovered service
  441. */
  442. public void event_serviceJoined(final Packet requiredTypePkt, final ATFarReference myHandler,
  443. final Packet discoveredTypePkt, final Packet remoteServicePkt) {
  444. receive(new Event("serviceJoined") {
  445. public void process(Object myActorMirror) {
  446. try {
  447. ATTypeTag requiredType = requiredTypePkt.unpack().asTypeTag();
  448. ATTypeTag discoveredType = discoveredTypePkt.unpack().asTypeTag();
  449. // is there a match?
  450. if (discoveredType.base_isSubtypeOf(requiredType).asNativeBoolean().javaValue) {
  451. ATObject remoteService = remoteServicePkt.unpack();
  452. // myhandler<-apply([remoteService])@[]
  453. myHandler.meta_receive(
  454. new NATAsyncMessage(Evaluator._APPLY_,
  455. NATTable.of(NATTable.of(remoteService)),
  456. NATTable.EMPTY));
  457. }
  458. } catch (XIOProblem e) {
  459. Logging.Actor_LOG.error("Error deserializing joined types or services: ", e.getCause());
  460. } catch (XClassNotFound e) {
  461. Logging.Actor_LOG.fatal("Could not find class while deserializing joined types or services: ", e.getCause());
  462. } catch (InterpreterException e) {
  463. Logging.Actor_LOG.error("Error while joining services: ", e);
  464. }
  465. }
  466. });
  467. }
  468. }