User Tools

Site Tools


at:tutorial:actors

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
at:tutorial:actors [2007/04/01 11:54] – finished tvcutsemat:tutorial:actors [2007/04/01 12:50] – added tvcutsem
Line 35: Line 35:
  
 === Asynchronous Message Sending === === Asynchronous Message Sending ===
 +
 +AmbientTalk, like E, lexically distinguishes between synchronous method invocation and asynchronous message sending. The former is expressed as ''o.m()'' while the latter is expressed as ''o<-m()''. Regular object references can carry both kinds of invocations. Synchronous method invocation behaves as in any typical object-oriented language. When an asynchronous message is sent to a local object ("local" meaning "hosted by the same actor"), the message is enqueued in the actor's own message queue and the method invocation will be executed at a later point in time.
 +
 +Far references, like the reference stored in the variable ''a'' above, only carry asynchronous message sends, and as such totally decouple objects hosted by different actors in time: objects can //never// be blocked waiting for an outstanding remote procedure call, they can only communicate by means of purely //asynchronous// message passing. This is a key property of AmbientTalk's concurrency model, and it is a crucial property in the context of [[distribution|distributed programming]].
 +
 +Hence, given the example above, the method ''sayHello'' can only be invoked as follows given a far reference ''a'':
 +
 +<code>
 +>a<-sayHello();
 +>>nil
 +</code>
 +
 +The above code is simple enough to understand: the ''sayHello'' message is asynchronously sent to the object pointed to by ''a'' by enqueueing it in ''a'''s message queue. The message send itself immediately returns ''nil'': asynchronous sends do not return a value by default.
 +
 +But what happens when the method to invoke asynchronously has parameters that need to be passed. How does parameter passing work in the context of inter-actor message sending? The rules are simple enough:
 +  - Objects and closures are always passed **by reference**
 +  - Native data types like numbers, text, tables, ... are always passed **by copy**
 +
 +Generally speaking, any object that encapsulates a lexical scope is passed by reference, because passing such an object by copy would entail passing the entire lexical scope by copy - a costly operation. Objects without a lexical scope, such as methods, can be copied without having to recursively copy any scope.
 +
 +When an object is passed by reference, we mean that the formal parameter of a method will be bound to a far reference to the original object. When it is passed by copy, the formal parameter will be bound to a local copy of the object. For example, consider the following ''calculator'' actor:
 +
 +<code>
 +>def calculator := actor: {
 +  def add(x,y,customer) {
 +    customer<-result(x+y)
 +  };
 +};
 +>><far ref to:<object:11600335>>
 +</code>
 +
 +The ''add'' method takes three parameters: two numbers to add, and a so-called //customer// object which is responsible for consuming the "return value" of the method. Here is how to invoke this method:
 +
 +<code>
 +>calculator<-add(1,2,object: {
 +  def result(sum) {
 +    system.println("sum = " + sum);
 +  };
 +};
 +>>nil
 +</code>
 +
 +Because of the parameter passing rules described above, the ''add'' method will receive copies of the numbers ''1'' and ''2'', will add them synchronously, and will send the result asynchronously to the customer object, which was passed by reference, i.e. ''customer'' is bound to a far reference. Eventually, the actor that sent the ''add'' message will itself receive a ''result'' message, and when this message is processed by the anonymous consumer object, the result is printed:
 +
 +<code>
 +sum = 3
 +</code>
 +
 +<note>
 +The parameter passing semantics just described lead to a model where the only references that cross actor boundaries are far references. In combination with the message sending semantics described previously, this guarantees that asynchronous messages are the only type of messages that can cross actor boundaries, ensuring that concurrent (and as will be shown later, also distributed) communication is strictly asynchronous. In such a model, deadlocks cannot occur (an actor is never blocked) and race conditions within one single actor can never occur. These properties significantly reduce the complexity of concurrent programs.
 +</note>
  
 === Isolates === === Isolates ===
 +
 +The parameter passing semantics defined above rule out any possibility for an object to be passed by copy. The reason for this semantics is that objects encapsulate a lexical scope, and parameter passing an object by-copy would require the entire lexical scope to be parameter-[assed as well.
 +
 +To enable objects to be passed by copy between actors, a special type of objects is introduced. These objects are called **isolates** because they are //isolated// from their lexical scope. Continuing our previous example, imagine we want our calculator to work with complex numbers, which are typically objects that one would want to pass by copy. We can define complex numbers as isolate objects as follows:
 +
 +<code>
 +>def complexNumber := isolate: {
 +  def re; // assume cartesian coordinates
 +  def im;
 +  def init(re,im) {
 +    self.re := re;
 +    self.im := im;
 +  };
 +  def +(other) {
 +    self.new(re+other.re, im+other.im);
 +  };
 +};
 +>><object:15603573[<stripe:Isolate>]>
 +</code>
 +
 +The ''isolate:'' primitive is actually syntactic sugar for the creation of an object that is automatically striped with the ''/.at.stripes.Isolate'' stripe. Any object that is striped with this stripe is treated as an isolate. If you are a Java programmer, you can best compare this behaviour to having to implement the ''java.io.Serializable'' interface to make a class's instances serializable.
 +
 +An isolate differs from a regular object as follows:
 +  - it has **no** access to its surrounding lexical scope; this means that an isolate only has access to its local fields and methods. An isolate does have access to the global lexical scope of its actor.
 +  - it is parameter-passed by-copy rather than by-reference in inter-actor message sends. The copy of the isolate received by the remote actor can only access that actor's global lexical scope, no longer the global scope of its original host.
 +  - external method definitions on isolates are disallowed. The reason for this is that external method definitions implicitly carry a lexical scope (the scope of their definition). Hence, if an isolate with external methods has to be copied, those scopes would have to be copied as well. Following the rule that objects  encapsulating a lexical scope are pass-by-reference, we chose to disallow external methods on isolates.
 +
 +=== Futures ===
 +
 +futures language construct
  
 === Actor Mirrors === === Actor Mirrors ===
 +
 +explain: mirror factory, message creation, message sending, install
 +
 +=== Nesting Actors ===
 +
 +lexical scoping rules for nested actors
at/tutorial/actors.txt · Last modified: 2020/02/05 21:26 by elisag