/interpreter/tags/at_build150307/src/edu/vub/at/actors/natives/ELActor.java
Java | 376 lines | 185 code | 35 blank | 156 comment | 4 complexity | 8697659efa39c2be33bfc9993849d2ce 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 */ 28package edu.vub.at.actors.natives; 29 30import edu.vub.at.actors.ATActorMirror; 31import edu.vub.at.actors.ATAsyncMessage; 32import edu.vub.at.actors.ATFarReference; 33import edu.vub.at.actors.eventloops.BlockingFuture; 34import edu.vub.at.actors.eventloops.Callable; 35import edu.vub.at.actors.eventloops.Event; 36import edu.vub.at.actors.eventloops.EventLoop; 37import edu.vub.at.actors.id.ATObjectID; 38import edu.vub.at.actors.net.Logging; 39import edu.vub.at.eval.Evaluator; 40import edu.vub.at.exceptions.InterpreterException; 41import edu.vub.at.exceptions.XClassNotFound; 42import edu.vub.at.exceptions.XIOProblem; 43import edu.vub.at.exceptions.XIllegalOperation; 44import edu.vub.at.exceptions.XObjectOffline; 45import edu.vub.at.objects.ATAbstractGrammar; 46import edu.vub.at.objects.ATMethod; 47import edu.vub.at.objects.ATObject; 48import edu.vub.at.objects.ATStripe; 49import edu.vub.at.objects.ATTable; 50import edu.vub.at.objects.natives.NATContext; 51import edu.vub.at.objects.natives.NATNil; 52import edu.vub.at.objects.natives.NATObject; 53import edu.vub.at.objects.natives.NATTable; 54import edu.vub.at.objects.natives.OBJLexicalRoot; 55 56/** 57 * An instance of the class ELActor represents a programmer-defined 58 * AmbientTalk/2 actor. The event queue of the actor event loop serves as the 59 * actor's 'meta-level' queue. 60 * 61 * The events in the 'meta-level' queue are handled by the actor's mirror object. 62 * This mirror is normally an instance of NATActorMirror, but it can be any 63 * programmer-defined object that adheres to the ATActorMirror interface. 64 * 65 * @author tvcutsem 66 */ 67public class ELActor extends EventLoop { 68 69 /** 70 * A thread-local variable that contains the 'default actor' to use 71 * when there is currently no ELActor event loop thread running. 72 * This is primarily useful for performing unit tests where an actor 73 * is automatically created when actor semantics is required. 74 * 75 * A warning is printed to the log because using the default actor should 76 * only be used for testing purposes. 77 */ 78 private static final ThreadLocal _DEFAULT_ACTOR_ = new ThreadLocal() { 79 protected synchronized Object initialValue() { 80 Logging.Actor_LOG.warn("Creating a default actor for thread " + Thread.currentThread()); 81 try { 82 ELVirtualMachine host = new ELVirtualMachine(NATNil._INSTANCE_, new SharedActorField[] { }); 83 return NATActorMirror.createEmptyActor(host, new NATActorMirror(host)).getFarHost(); 84 } catch (InterpreterException e) { 85 throw new RuntimeException("Failed to initialize default actor",e); 86 } 87 } 88 }; 89 90 /** 91 * Retrieves the currently running actor. If there is no running actor thread, 92 * this returns the value stored in the thread-local default actor field. 93 */ 94 public static final ELActor currentActor() { 95 try { 96 return ((ELActor) EventLoop.currentEventLoop()); 97 } catch (ClassCastException e) { 98 // current event loop is not an actor event loop 99 } catch (IllegalStateException e) { 100 // current thread is not an event loop 101 } 102 Logging.Actor_LOG.warn("Asked for an actor in non-actor thread " + Thread.currentThread()); 103 return (ELActor) _DEFAULT_ACTOR_.get(); 104 } 105 106 private ATActorMirror mirror_; 107 protected final ELVirtualMachine host_; 108 protected final ReceptionistsSet receptionists_; 109 110 /* 111 * This object is created when the actor is initialized: i.e. it is the passed 112 * version of the isolate that was passed to the actor: primitive by the creating actor. 113 */ 114 private ATObject behaviour_; 115 116 public ELActor(ATActorMirror mirror, ELVirtualMachine host) { 117 super("actor " + mirror.toString()); 118 mirror_ = mirror; 119 host_ = host; 120 receptionists_ = new ReceptionistsSet(this); 121 } 122 123 /** constructor dedicated to initialization of discovery actor */ 124 protected ELActor(ELVirtualMachine host) { 125 super("discovery actor"); 126 mirror_ = new NATActorMirror(host); 127 host_ = host; 128 receptionists_ = new ReceptionistsSet(this); 129 } 130 131 /** 132 * Actor event loops handle events by allowing the meta-level events to 133 * process themselves. 134 */ 135 public void handle(Event event) { 136 event.process(mirror_); 137 } 138 139 public ATActorMirror getActorMirror() { return mirror_; } 140 141 public void setActorMirror(ATActorMirror mirror) { mirror_ = mirror; } 142 143 public ELVirtualMachine getHost() { 144 return host_; 145 } 146 147 /** 148 * Export the given local object such that it is now remotely accessible via the 149 * returned object id. 150 * @param object a **near** reference to the object to export 151 * @return a unique identifier by which this object can be retrieved via the resolve method. 152 * @throws XIllegalOperation if the passed object is a far reference, i.e. non-local 153 */ 154 public NATLocalFarRef export(ATObject object) throws InterpreterException { 155 // receptionist set will check whether ATObject is really local to me 156 return receptionists_.exportObject(object); 157 } 158 159 /** 160 * Resolve the given object id into a local reference. There are three cases to 161 * consider: 162 * A) The given id designates an object local to this actor: the returned object 163 * will be a **near** reference to the object (i.e. the object itself) 164 * B) The given id designates a far (non-local) object that lives in the same 165 * address space as this actor: the returned object wil be a **far** reference 166 * to the object. 167 * C) The given id designates a far object that lives on a remote machine: the 168 * returned object will be a **far** and **remote** reference to the object. 169 * 170 * @param id the identifier of the object to resolve 171 * @return a near or far reference to the object, depending on where the designated object lives 172 */ 173 public ATObject resolve(ATObjectID id, ATStripe[] stripes) throws XObjectOffline { 174 return receptionists_.resolveObject(id, stripes); 175 } 176 177 /* ----------------------------- 178 * -- Initialisation Protocol -- 179 * ----------------------------- */ 180 181 /** 182 * Initialises the root using the contents of the init file stored by 183 * the hosting virtual machine. 184 * @throws InterpreterException 185 */ 186 protected void initRootObject() throws InterpreterException { 187 ATAbstractGrammar initialisationCode = host_.getInitialisationCode(); 188 189 // evaluate the initialization code in the context of the global scope 190 NATObject globalScope = Evaluator.getGlobalLexicalScope(); 191 NATContext initCtx = new NATContext(globalScope, globalScope); 192 193 initialisationCode.meta_eval(initCtx); 194 } 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 207 for (int i = 0; i < fields.length; i++) { 208 SharedActorField field = fields[i]; 209 ATObject value = field.initialize(); 210 if (value != null) { 211 globalScope.meta_defineField(field.getName(), value); 212 } 213 } 214 } 215 216 // Events to be processed by the actor event loop 217 218 /** 219 * The initial event sent by the actor mirror to its event loop to intialize itself. 220 * @param future the synchronization point with the creating actor, needs to be fulfilled with a far ref to the behaviour. 221 * @param parametersPkt the serialized parameters for the initialization code 222 * @param initcodePkt the serialized initialization code 223 */ 224 protected void event_init(final BlockingFuture future, final Packet parametersPkt, final Packet initcodePkt) { 225 receive(new Event("init("+this+")") { 226 public void process(Object byMyself) { 227 try { 228 behaviour_ = new NATObject(); 229 230 // pass far ref to behaviour to creator actor who is waiting for this 231 future.resolve(receptionists_.exportObject(behaviour_)); 232 233 // initialize lexically visible fields 234 initSharedFields(); 235 236 ATTable params = parametersPkt.unpack().asTable(); 237 ATMethod initCode = initcodePkt.unpack().asMethod(); 238 239 // initialize the behaviour using the parameters and the code 240 initCode.base_applyInScope(params, new NATContext(behaviour_, behaviour_)); 241 242 // go on to initialize the root and all lexically visible fields 243 initRootObject(); 244 } catch (InterpreterException e) { 245 Logging.Actor_LOG.error(behaviour_ + ": could not initialize actor behaviour", e); 246 } 247 } 248 }); 249 } 250 251 /** 252 * The main entry point for any asynchronous self-sends. 253 * Asynchronous self-sends do not undergo any form of parameter passing, there is no need 254 * to serialize and deserialize the message parameter in a Packet. 255 */ 256 public void event_acceptSelfSend(final ATAsyncMessage msg) { 257 receive(new Event("accept("+msg+")") { 258 public void process(Object myActorMirror) { 259 performAccept(msg); 260 } 261 }); 262 } 263 264 /** 265 * The main entry point for any asynchronous messages sent to this actor 266 * by external sources (e.g. the VM or other local actors). 267 * @param msg the asynchronous AmbientTalk base-level message to enqueue 268 */ 269 public void event_accept(final Packet serializedMessage) { 270 receive(new Event("accept("+serializedMessage+")") { 271 public void process(Object myActorMirror) { 272 try { 273 ATAsyncMessage msg = serializedMessage.unpack().asAsyncMessage(); 274 performAccept(msg); 275 } catch (InterpreterException e) { 276 Logging.Actor_LOG.error(mirror_ + ": error unpacking "+ serializedMessage, e); 277 } catch (RuntimeException e) { 278 throw e; 279 } 280 } 281 }); 282 } 283 284 private void performAccept(ATAsyncMessage msg) { 285 try { 286 ATObject result = msg.base_getReceiver().meta_receive(msg); 287 // TODO what to do with return value? 288 Logging.Actor_LOG.info(mirror_ + ": "+ msg + " returned " + result); 289 } catch (InterpreterException e) { 290 // TODO what to do with exception? 291 Logging.Actor_LOG.error(mirror_ + ": "+ msg + " failed ", e); 292 } 293 } 294 295 /** 296 * This method is invoked by a coercer in order to schedule a symbiotic invocation 297 * from the Java world, which should be synchronous to the Java thread, but which 298 * must be scheduled asynchronously to comply with the AT/2 actor model. 299 * @param invocation a functor object that will perform the symbiotic invocation 300 * @return the result of the symbiotic invocation 301 * @throws Exception if the symbiotic invocation fails 302 */ 303 public Object sync_event_symbioticInvocation(Callable invocation) throws Exception { 304 return receiveAndWait("symbioticInvocation", invocation); 305 } 306 307 /** 308 * This method should only be used for purposes such as the IAT shell or unit testing. 309 * It allows an external thread to make this actor evaluate an arbitrary expression. 310 * 311 * @param ast an abstract syntax tree to be evaluated by the receiving actor (in the 312 * scope of its behaviour). 313 * @return the result of the evaluation 314 * @throws InterpreterException if the evaluation fails 315 */ 316 public ATObject sync_event_eval(final ATAbstractGrammar ast) throws InterpreterException { 317 try { 318 return (ATObject) receiveAndWait("nativeEval("+ast+")", new Callable() { 319 public Object call(Object inActor) throws Exception { 320 return OBJLexicalRoot._INSTANCE_.base_eval_in_(ast, behaviour_); 321 } 322 }); 323 } catch (Exception e) { 324 throw (InterpreterException) e; 325 } 326 } 327 328 329 /** 330 * This method should only be used for purposes of unit testing. It allows 331 * arbitary code to be scheduled by external threads such as unit testing frameworks. 332 */ 333 public Object sync_event_performTest(final Callable c) throws Exception { 334 return (ATObject) receiveAndWait("performTest("+c+")", c); 335 } 336 337 /** 338 * When the discovery manager receives a publication from another local actor or 339 * another remote VM, the actor is asked to compare the incoming publication against 340 * a subscription that it had announced previously. 341 * 342 * @param requiredStripe serialized form of the stripe attached to the actor's subscription 343 * @param myHandler the closure specified as a handler for the actor's subscription 344 * @param discoveredStripe serialized form of the stripe attached to the new publication 345 * @param remoteService serialized form of the reference to the remote discovered service 346 */ 347 public void event_serviceJoined(final Packet requiredStripePkt, final ATFarReference myHandler, 348 final Packet discoveredStripePkt, final Packet remoteServicePkt) { 349 receive(new Event("serviceJoined") { 350 public void process(Object myActorMirror) { 351 try { 352 ATStripe requiredStripe = requiredStripePkt.unpack().asStripe(); 353 ATStripe discoveredStripe = discoveredStripePkt.unpack().asStripe(); 354 // is there a match? 355 if (discoveredStripe.base_isSubstripeOf(requiredStripe).asNativeBoolean().javaValue) { 356 ATObject remoteService = remoteServicePkt.unpack(); 357 // myhandler<-apply([remoteService]) 358 myHandler.meta_receive( 359 NATAsyncMessage.createAsyncMessage(myHandler, myHandler, Evaluator._APPLY_, 360 NATTable.atValue(new ATObject[] { 361 NATTable.atValue(new ATObject[] { remoteService }) 362 }) 363 ) 364 ); 365 } 366 } catch (XIOProblem e) { 367 Logging.Actor_LOG.error("Error deserializing joined stripes or services: ", e.getCause()); 368 } catch (XClassNotFound e) { 369 Logging.Actor_LOG.fatal("Could not find class while deserializing joined stripes or services: ", e.getCause()); 370 } catch (InterpreterException e) { 371 Logging.Actor_LOG.error("Error while joining services: ", e); 372 } 373 } 374 }); 375 } 376}