/interpreter/tags/at2dist110511/src/edu/vub/at/actors/natives/NATActorMirror.java
Java | 448 lines | 246 code | 38 blank | 164 comment | 13 complexity | abbf3d043324cc8bb47690335fbb2321 MD5 | raw file
1/** 2 * AmbientTalk/2 Project 3 * NATActorMirror.java created on Oct 16, 2006 at 1:55:05 PM 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.ATLetter; 33import edu.vub.at.actors.natives.DiscoveryManager.Publication; 34import edu.vub.at.actors.natives.DiscoveryManager.Subscription; 35import edu.vub.at.eval.Evaluator; 36import edu.vub.at.exceptions.InterpreterException; 37import edu.vub.at.exceptions.XArityMismatch; 38import edu.vub.at.exceptions.XIllegalOperation; 39import edu.vub.at.exceptions.XTypeMismatch; 40import edu.vub.at.objects.ATBoolean; 41import edu.vub.at.objects.ATClosure; 42import edu.vub.at.objects.ATContext; 43import edu.vub.at.objects.ATNil; 44import edu.vub.at.objects.ATObject; 45import edu.vub.at.objects.ATTable; 46import edu.vub.at.objects.ATTypeTag; 47import edu.vub.at.objects.coercion.NativeTypeTags; 48import edu.vub.at.objects.grammar.ATSymbol; 49import edu.vub.at.objects.mirrors.NATIntrospectiveMirror; 50import edu.vub.at.objects.mirrors.NativeClosure; 51import edu.vub.at.objects.mirrors.PrimitiveMethod; 52import edu.vub.at.objects.natives.NATByRef; 53import edu.vub.at.objects.natives.NATMethodInvocation; 54import edu.vub.at.objects.natives.NATNumber; 55import edu.vub.at.objects.natives.NATObject; 56import edu.vub.at.objects.natives.NATTable; 57import edu.vub.at.objects.natives.NATText; 58import edu.vub.at.objects.natives.OBJLexicalRoot; 59import edu.vub.at.objects.natives.grammar.AGSymbol; 60 61import java.util.LinkedList; 62 63/** 64 * The NATActorMirror class implements the concurrency model of ambienttalk. It continually 65 * consumes meta-events which are written to a synchronized queue. This way the actor 66 * is notified of incoming messages, discovered and lost services, etc. When no meta- 67 * messages are available, the actor consumes base-level messages as they are stored 68 * in its inbox. These messages have a receiver object internal to the actor and they 69 * will be invoked on this receiver by the actor's thread. 70 * 71 * @author smostinc 72 */ 73public class NATActorMirror extends NATByRef implements ATActorMirror { 74 75 // INSTANCE VARIABLES 76 77 // the actor mirror refers directly to its ELActor because 78 // using ELActor.currentActor is slower 79 private ELActor myActor_; 80 private final ELDiscoveryActor discoveryActor_; 81 82 /** 83 * When initializing a new actor, do not forget that this constructor is still executed by the 84 * creating actor, not by the created actor. To make the created actor perform something, it is 85 * necessary to use meta_receive to send itself messages for later execution. 86 */ 87 public NATActorMirror(ELVirtualMachine host) { 88 discoveryActor_ = host.discoveryActor_; 89 } 90 91 /** set the mirror's base actor instance */ 92 protected void setActor(ELActor myActor) { 93 myActor_ = myActor; 94 } 95 96 /* ------------------------------------------ 97 * -- Language Construct to Actor Protocol -- 98 * ------------------------------------------ */ 99 100 public ATAsyncMessage base_createMessage(ATSymbol selector, ATTable arguments, ATTable types) throws InterpreterException { 101 ATAsyncMessage msg = new NATAsyncMessage(selector, arguments, types); 102 // types.inject: msg into: { |constructingMsg, type| type.annotate(constructingMsg) } 103 ATAsyncMessage returnMsg = types.base_inject_into_(msg, new NativeClosure(this) { 104 public ATObject base_apply(ATTable args) throws InterpreterException { 105 checkArity(args, 2); 106 ATObject newMessage = get(args, 1); 107 ATObject type = get(args, 2); 108 return type.asTypeTag().base_annotateMessage(newMessage); 109 } 110 }).asAsyncMessage(); 111 // if the message was indeed annotated, set the correct location informal on the original message. 112 if (! returnMsg.equals(msg)){ 113 msg.impl_setLocation(types.impl_getLocation()); 114 } 115 return returnMsg; 116 } 117 118 public ATObject base_createMirror(ATObject reflectee) throws InterpreterException { 119 return NATIntrospectiveMirror.atValue(reflectee); 120 } 121 122 /** 123 * A publication object is defined as: 124 * object: { 125 * def topic := //topic under which service is published; 126 * def service := //the exported service object; 127 * def cancel() { //unexport the service object } 128 * } 129 */ 130 public static class NATPublication extends NATObject { 131 private static final AGSymbol _TOPIC_ = AGSymbol.jAlloc("topic"); 132 private static final AGSymbol _SERVICE_ = AGSymbol.jAlloc("service"); 133 private static final AGSymbol _CANCEL_ = AGSymbol.jAlloc("cancel"); 134 public NATPublication(final ELDiscoveryActor discoveryActor, 135 ATTypeTag topic, ATObject service, 136 final Publication pub) throws InterpreterException { 137 meta_defineField(_TOPIC_, topic); 138 meta_defineField(_SERVICE_, service); 139 meta_addMethod(new PrimitiveMethod(_CANCEL_, NATTable.EMPTY) { 140 public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException { 141 int arity = arguments.base_length().asNativeNumber().javaValue; 142 if (arity != 0) { 143 throw new XArityMismatch("cancel", 0, arity); 144 } 145 discoveryActor.event_cancelPublication(pub); 146 return Evaluator.getNil(); 147 } 148 }); 149 } 150 public NATText meta_print() throws InterpreterException { 151 return NATText.atValue("<publication:"+impl_invokeAccessor(this, _TOPIC_, NATTable.EMPTY)+">"); 152 } 153 } 154 155 /** 156 * A subscription object is defined as: 157 * object: { 158 * def topic := //topic subscribed to; 159 * def handler := //the closure to be triggered; 160 * def cancel() { //unsubscribe the handler } 161 * } 162 */ 163 public static class NATSubscription extends NATObject { 164 private static final AGSymbol _TOPIC_ = AGSymbol.jAlloc("topic"); 165 private static final AGSymbol _HANDLER_ = AGSymbol.jAlloc("handler"); 166 private static final AGSymbol _CANCEL_ = AGSymbol.jAlloc("cancel"); 167 public NATSubscription(final ELDiscoveryActor discoveryActor, 168 ATTypeTag topic, ATClosure handler, 169 final Subscription sub) throws InterpreterException { 170 meta_defineField(_TOPIC_, topic); 171 meta_defineField(_HANDLER_, handler); 172 meta_addMethod(new PrimitiveMethod(_CANCEL_, NATTable.EMPTY) { 173 public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException { 174 int arity = arguments.base_length().asNativeNumber().javaValue; 175 if (arity != 0) { 176 throw new XArityMismatch("cancel", 0, arity); 177 } 178 discoveryActor.event_cancelSubscription(sub); 179 return Evaluator.getNil(); 180 } 181 }); 182 } 183 public NATText meta_print() throws InterpreterException { 184 return NATText.atValue("<subscription:"+impl_invokeAccessor(this, _TOPIC_, NATTable.EMPTY)+">"); 185 } 186 } 187 188 public ATObject base_provide(final ATTypeTag topic, final ATObject service) throws InterpreterException { 189 Publication pub = new Publication(myActor_, 190 new Packet(topic), 191 new Packet(service), 192 service); 193 discoveryActor_.event_servicePublished(pub); 194 return new NATPublication(discoveryActor_, topic, service, pub); 195 } 196 197 public ATObject base_require(final ATTypeTag topic, final ATClosure handler, ATBoolean isPermanent) throws InterpreterException { 198 Subscription sub = new Subscription(myActor_, 199 new Packet(topic), 200 new Packet(handler), 201 isPermanent.asNativeBoolean().javaValue); 202 discoveryActor_.event_clientSubscribed(sub); 203 return new NATSubscription(discoveryActor_, topic, handler, sub); 204 } 205 206 public ATTable base_listPublications() throws InterpreterException { 207 Publication[] pubs = discoveryActor_.sync_event_listPublications(myActor_); 208 NATPublication[] natpubs = new NATPublication[pubs.length]; 209 for(int i = 0; i < pubs.length; i++) { 210 Publication pub = pubs[i]; 211 natpubs[i] = new NATPublication( 212 discoveryActor_, 213 pub.providedTypeTag_.unpack().asTypeTag(), 214 pub.exportedService_.unpack(), 215 pub); 216 } 217 return NATTable.atValue(natpubs); 218 } 219 220 public ATTable base_listSubscriptions() throws InterpreterException { 221 Subscription[] subs = discoveryActor_.sync_event_listSubscriptions(myActor_); 222 NATSubscription[] natsubs = new NATSubscription[subs.length]; 223 for(int i = 0; i < subs.length; i++) { 224 Subscription sub = subs[i]; 225 natsubs[i] = new NATSubscription( 226 discoveryActor_, 227 sub.requiredTypeTag_.unpack().asTypeTag(), 228 sub.registeredHandler_.unpack().asClosure(), 229 sub); 230 } 231 return NATTable.atValue(natsubs); 232 } 233 234 /* -------------------------- 235 * -- VM to Actor Protocol -- 236 * -------------------------- */ 237 238 public ATObject meta_clone() throws InterpreterException { 239 throw new XIllegalOperation("Cannot clone actor " + toString()); 240 } 241 242 /** 243 * actor.new(closure) 244 * => same effect as evaluating 'actor: closure' 245 */ 246 public ATObject meta_newInstance(ATTable initargs) throws InterpreterException { 247 int length = initargs.base_length().asNativeNumber().javaValue; 248 if(length != 1) 249 throw new XArityMismatch("newInstance", 1, length); 250 251 ATClosure closure = initargs.base_at(NATNumber.ONE).asClosure(); 252 return OBJLexicalRoot._INSTANCE_.base_actor_(closure); 253 } 254 255 public NATText meta_print() throws InterpreterException { 256 return NATText.atValue("<actormirror:" + this.hashCode() + ">"); 257 } 258 259 public ATTable meta_typeTags() throws InterpreterException { 260 return NATTable.of(NativeTypeTags._ACTORMIRROR_); 261 } 262 263 /* ----------------------------- 264 * -- Object Passing Protocol -- 265 * ----------------------------- */ 266 267 /** 268 * When default base-level objects send an asynchronous message, they delegate 269 * this responsibility to their actor by means of this base-level method. 270 * 271 * The actor's default implementation is to invoke the receiver mirror's <tt>receive</tt> method 272 * which defines the default asynchronous message reception semantics. 273 * 274 * Note: in pre-2.9 versions of AmbientTalk, this method did not immediately pass 275 * control to the receiver via the <tt>receive</tt> method. By delegating to the receiver 276 * in the same execution turn as the message send, we allow possible proxies (custom eventual 277 * references) to intervene in the message sending process. 278 */ 279 public ATObject base_send(ATObject receiver, ATAsyncMessage message) throws InterpreterException { 280 return receiver.meta_receive(message); 281 } 282 283 /** 284 * def becomeMirroredBy: protocol 285 * => returns the old installed protocol 286 * 287 * @see ATActorMirror#base_becomeMirroredBy_(ATClosure) 288 */ 289 public ATObject base_becomeMirroredBy_(ATActorMirror newActorMirror) throws InterpreterException { 290 ATActorMirror oldMirror = myActor_.getImplicitActorMirror(); 291 myActor_.setActorMirror(newActorMirror); 292 return oldMirror; 293 } 294 295 /** 296 * def getExplicitActorMirror() 297 * => return an explicit actor mirror for the current actor. 298 * 299 * The default implementation uses the actor's implicit mirror as the 300 * default explicit mirror. 301 * 302 * @see ATActorMirror#base_getExplicitActorMirror() 303 */ 304 public ATActorMirror base_getExplicitActorMirror() throws InterpreterException { 305 return myActor_.getImplicitActorMirror(); 306 } 307 308 /** 309 * This operation is 310 * introduced as a mechanism to alter the semantics of message reception for all objects 311 * owned by an actor. It can be used e.g. to keep track of all successfully processed messages. 312 * 313 * Note that this operation is *only* invoked for messages received from *other* 314 * actors (i.e. local or remote actors), it is *not* invoked for recursive asynchronous self-sends 315 * (intra-actor message sends)! This is an important change w.r.t pre-2.9 versions of AmbientTalk. 316 */ 317 public ATObject base_receive(ATObject receiver, ATAsyncMessage message) throws InterpreterException { 318 // this additional dispatch to receive will schedule an async self-send when performed 319 // on near references, which then invokes message.base_process in a later execution turn 320 // We do not short-circuit this behaviour for consistency purposes: receive is invoked 321 // on all receiver objects consistently, whether they are near refs, far refs, custom eventual 322 // refs or any other kind of mirage. 323 return receiver.meta_receive(message); 324 } 325 326 /** 327 * Export the given local object such that it is now remotely accessible via the 328 * returned object id. 329 * @param object a **near** reference to the object to export 330 * @return a local far reference to the object being exported 331 * @throws XIllegalOperation if the passed object is a far reference, i.e. non-local 332 */ 333 public ATObject base_createReference(ATObject object) throws InterpreterException { 334 // receptionist set will check whether ATObject is really local to me 335 return myActor_.receptionists_.exportObject(object); 336 } 337 338 /** 339 * Provides access to this actor's "behaviour" object. This is the first 340 * object created within an actor. 341 * 342 * Note: if the behaviour is accessed when evaluating the "init.at" initialization 343 * file of an actor, the behaviour will <em>not have been initialized</em> yet. 344 * It will appear as an empty object. 345 */ 346 public ATObject base_behaviour() throws InterpreterException { 347 return myActor_.behaviour_; 348 } 349 350 public ATActorMirror asActorMirror() throws XTypeMismatch { 351 return this; 352 } 353 354 public NATActorMirror asNativeActorMirror() throws XTypeMismatch { 355 return this; 356 } 357 358 /* ------------------------- 359 * -- Scheduling Protocol -- 360 * ------------------------- */ 361 362 /** 363 * The inbox of this actor. It contains objects that implement the {@link ATLetter} interface 364 */ 365 private LinkedList inbox_ = new LinkedList(); 366 367 /** 368 * A letter object is defined as: 369 * object: { 370 * def receiver := //receiver of the letter; 371 * def message := //the message that is being sent; 372 * def cancel() { //cancel the delivery of the letter } 373 * } 374 */ 375 public static class NATLetter extends NATObject implements ATLetter { 376 private static final AGSymbol _RCVR_ = AGSymbol.jAlloc("receiver"); 377 private static final AGSymbol _MSG_ = AGSymbol.jAlloc("message"); 378 private static final AGSymbol _CANCEL_ = AGSymbol.jAlloc("cancel"); 379 public NATLetter(final LinkedList mailbox, ATObject receiver, ATObject message) throws InterpreterException { 380 super(new ATTypeTag[] { NativeTypeTags._LETTER_ }); 381 meta_defineField(_RCVR_, receiver); 382 meta_defineField(_MSG_, message); 383 final NATLetter thisLetter = this; 384 meta_addMethod(new PrimitiveMethod(_CANCEL_, NATTable.EMPTY) { 385 public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException { 386 int arity = arguments.base_length().asNativeNumber().javaValue; 387 if (arity != 0) { 388 throw new XArityMismatch("cancel", 0, arity); 389 } 390 // Note: if the receiver and message fields are changed by an AmbientTalk program 391 // canceling the letter will still remove the original letter, not a letter that corresponds 392 // to the new receiver and message. 393 mailbox.remove(thisLetter); 394 return Evaluator.getNil(); 395 } 396 }); 397 } 398 public NATText meta_print() throws InterpreterException { 399 return NATText.atValue("<letter:"+impl_invokeAccessor(this, _MSG_, NATTable.EMPTY)+">"); 400 } 401 402 public ATLetter asLetter() { return this; } 403 public ATObject base_cancel() throws InterpreterException { 404 return this.meta_invoke(this, new NATMethodInvocation(_CANCEL_, NATTable.EMPTY, NATTable.EMPTY)); 405 } 406 public ATAsyncMessage base_message() throws InterpreterException { 407 return this.meta_invokeField(this, _MSG_).asAsyncMessage(); 408 } 409 public ATObject base_receiver() throws InterpreterException { 410 return this.meta_invokeField(this, _RCVR_); 411 } 412 } 413 414 /** 415 * Returns a table with all letters currently in the inbox 416 */ 417 public ATTable base_listIncomingLetters() throws InterpreterException { 418 ATObject[] incoming = (ATObject[]) inbox_.toArray(new ATObject[inbox_.size()]); 419 return NATTable.atValue(incoming); 420 } 421 422 /** 423 * Creates a letter object and adds it to the actor's inbox 424 */ 425 public ATObject base_schedule(ATObject receiver, ATAsyncMessage message) throws InterpreterException { 426 NATLetter letter = new NATLetter(inbox_, receiver, message); 427 inbox_.addFirst(letter); 428 // signal a serve event for every message that is scheduled 429 myActor_.event_serve(); 430 return letter; 431 } 432 433 /** 434 * Fetches the next letter from the actor's inbox, if any, and processes it. 435 */ 436 public ATObject base_serve() throws InterpreterException { 437 if (inbox_.size() > 0) { 438 ATObject next = (ATObject) inbox_.removeLast(); 439 ATLetter letter = next.asLetter(); 440 // receive has already been invoked prior to scheduling 441 // the receive is known to be a local object, therefore we 442 // can immediately process the message 443 return letter.base_message().base_process(letter.base_receiver()); 444 } else { 445 return Evaluator.getNil(); 446 } 447 } 448}