In this part we will present the process of creating an active object from a class or from another already existing object. We will also look at the internal structure of an active object and at its thread. In the last chapters we will examine group communication, mobile agents and exception handling for active objects.
Active objects are the basic units of activity and distribution used for building concurrent applications using ProActive. As opposed to passive(regular) objects, the active object has its own thread and execution queue. ProActive manages the active objects threads relieving the programmer from explicitly manipulating Thread objects thus making the use of the threads transparent.
Active objects can be created on any of the hosts involved in the computation. Once an active object is created, its activity (the fact that it runs with its own thread) and its location (local or remote) are transparent. As a matter of fact, any active object can be manipulated just like if it were a passive instance of the same class.
An application based on active objects is structured in subsystems. A subsystem is composed of only one active object (with its own thread) and several passive objects (possibly zero). The active object thread executes only the methods invoked on the active object by other active objects or by passive objects of the subsystem of the active object.
This has consequences on the semantics of message-passing between subsystems.
When an object in a subsystem calls a method on an active object, the parameters of the call may be references on passive objects of the subsystem, which would lead to shared passive objects. This is why passive objects passed as parameters of calls on active objects are always passed by deep-copy. Active objects, on the other hand, are always passed by reference. Symmetrically, this also applies to objects returned from methods called on active objects.
When a method is called on an active object, it returns immediately (as the thread cannot execute methods in the other subsystem). A future object, which is a placeholder for the result of the methods invocation, is returned. From the point of view of the caller subsystem, no difference can be made between the future object and the object that would have been returned if the same call had been issued onto a passive object. Then, the calling thread can continue executing its code just like if the call had been effectively performed. The role of the future object is to block this thread if it invokes a method on the future object and the result has not yet been set (i.e. the thread of the subsystem on which the call was received has not yet performed the call and placed the result into the future object). This type of inter-object synchronization policy is known as wait-by-necessity.
The active object is the composition of two objects:
a body (Body
)
a regular instance of the Object
The body is responsible for receiving calls on the active object, storing these calls in the queue of pending calls (also called requests). It will execute these calls in an order specified by a specific synchronization policy. If no specific synchronization policy is provided, calls are managed in a FIFO manner (first come, first served). The body is not visible from the outside of the active object therefore the active object looks exactly like a standard object from the user's perspective. It is important to note that no parallelism is provided inside an active object. This is an important decision in the design of ProActive which enables the use of pre and post conditions and class invariants.
On the side of the subsystem which sends a call to an active object, the active object is accessed through a stub and a proxy. The proxy's main responsibility is to generate future objects for representing future values, transform calls into request objects (in terms of meta-object programming, this is a reification) and perform deep-copy of passive objects passed as parameters. The passive objects are not shared between subsystems. Any call on an remote active object using passive objects as arguments leads to a deep-copy of the passive objects on the subsystem of the remote active object. The role of the stub , is to reify the all the method calls that can be performed through a reference to the active object. Reifying a call simply means constructing an object (in our case, all reified calls are instance of class MethodCall) that represents the call, so that it can be manipulated as any other object. This reified call is then processed by the other components of the active object in order to achieve the behavior we expect from an active object.
However, the use of the stub, proxy, body, and queue is transparent. ProActive manages all of them, with the user accessing the active objects in the same way as passive objects.
© 1997-2008 INRIA Sophia Antipolis All Rights Reserved