Home | Contact | Pricing | News | Events | Partners | Mailing List | Site Map
Gnat Pro. Powerful tools. Frontline Support. Ada expertise.

Gem #85:The Distributed Systems Annex 2 — Distributed Objects

Author: Thomas Quinot, AdaCore

Abstract: Ada Gem #85: This is the second in a series of Gems introducing the facilities defined by the optional annex for Distributed Systems (Annex E) of the Ada Reference Manual. In the first installment, we showed how a simple client/server architecture can be implemented easily with the Distributed Systems Annex (DSA). We now introduce distributed objects, which allow dynamic relationships between components of a distributed application.

« Previous Gem | Next Gem » | Gems Menu

Let’s get started…

In the previous DSA Gem, we showed how subprograms in a package can be made remotely callable using a pragma Remote_Call_Interface (RCI for short). Each RCI unit is present in only one partition of a distributed application, and any call to a subprogram in such a unit made from another partition is transparently handled by the distribution run-time library.

This is sufficient to implement simple client/server communication, where a single partition is identified as the provider of a service (defined by an RCI package) and accepts requests from other partitions. Different services can be provided by different partitions, and services can be clients of one another. However this scheme is inflexible in that a given service can only ever be provided by a single server. Furthermore, the association between services and partitions is static.

In some contexts, however, more flexible interactions between application components are desired: multiple partitions may want to provide the same service, for performance or fault-tolerance reasons; servers may need to call back their clients; finally, direct (peer-to-peer) interactions between partitions may need to be established in a dynamic fashion, without determining in advance (prior to execution) who will interact with whom.

Such a flexible organization can be implemented using distributed objects. In nondistributed object-oriented programming, an object is an entity with an identity (you can reference, or designate it), internal state, and a set of methods that are common to all objects that belong to the same class, and which represent the ways any object of the class can interact with others. In a distributed world, this paradigm is naturally extended by allowing object references to designate objects that are located on another partition.

In the DSA, distributed objects are created using a specific pragma: Remote_Types. When this pragma is applied to a package, certain type declarations have additional semantics specific to distribution. If you declare a tagged limited private type in such a package, and a corresponding access-to-class-wide type, then that access type is a Remote Access to Class-Wide type (or RACW), and is allowed to designate objects that are located on partitions other than the current one.

These remote object references can be passed around as parameters in remote subprogram calls. For example, they can be sent to an RCI package, or retrieved from it, by passing them as parameters in remote subprogram calls.

Methods of remote objects can be called by just writing a regular dispatching call on any primitive operation. All underlying communication is handled transparently by the distribution run-time library.

So let’s now assume that we want to allow users of our bulletin board application to exchange direct messages with one another. Each user will instantiate an object of a concrete type derived from the User type:

package Chat_Users is
   pragma Remote_Types;
   --  This package declares a remote object type

   type User is abstract tagged limited private;
   --  Remote objects must be tagged, limited, and private

   type User_Ref is access all User'Class;
   --  This is a remote access-to-class-wide type

   function Name (Who : User) return String;
   procedure Say
     (From : User_Ref;
      To   : User;
      What : String);
   --  The controlling formal 'To' determines the object that calls are sent to.

   --  The recipient object may be remote. Formal parameter 'From' is a reference
   --  to the originating user, and can be used to call the user back at a later
   --  time.

private
   ...
end Chat_Users;

Each message posted to the bulletin board can now include a reference to the message author:

with Chat_Users;
package Bulletin_Board is
  ...
  type News_Item (Message_Length : Natural) is
     Author  : Chat_Users.User_Ref;
     Message : String (1 .. Message_Length);
  end News_Item;
  ...
end Bulletin_Board;

Now each client can create an instance of a concrete type derived from Chat_Users.User, and pass a ‘Access to that object to the bulletin board as it posts messages.

with Chat_Users;
package Client is
   --  This is a regular package, no pragma needed

   type Myself_Type is new Chat_Users.User with null record;
   function Name (Self : Myself_Type) return String;
   procedure Say
     (From : Chat_Users.User_Ref;
      To   : Myself_Type;
      What : String);

end Client;
with Ada.Text_IO; use Ada.Text_IO;
package body Client is
   function Name (Self : Myself_Type) return String is

   begin
      return "Jean-Pierre";
   end Name;

   procedure Say
     (From : Chat_Users.User_Ref;
      To   : Myself_Type;
      What : String)
   is
      pragma Unreferenced (To);
      --  Parameter 'To' is unused within the body. Its purpose is just to cause
      --  dispatching to the appropriate object instance.

   begin
      Put_Line ("Got a message from " & From.Name);
      --  Dispatching call to Name to retrieve user name of the sender 'From'

      Put_Line (What);
      --  Display received message.
   end Say;

   Myself : aliased Myself_Type;
   ...
end Client;

Other clients can use the Author component retrieved from the bulletin board to directly contact other clients using the Say method:

   Say
     (From    => Myself'Access,
      To      => Some_Item.Author,
      Message => "I like it!");

Arbitrary partition-to-partition interactions can thus be established using distributed objects. More precisely, these are actually normal objects with the additional property that they can be designated from other partitions using special access types (RACWs). RCI units serve as switchboards to initially propagate references to remote objects across partition boundaries. Once these references are disseminated, partitions can interact directly without the mediation of RCIs.

In a future Gem, we will discuss the implementation of mailbox-based message passing using the Distributed Systems Annex.

 

Posted by Posted in Development Log, Devt log - Gem of the Week, PolyORB

Have your own idea for a Gem?

If you have an idea for a Gem you would like to contribute please feel free to contact us at: gems@adacore.com

Discussion

3 responses to “Gem #85:The Distributed Systems Annex 2 — Distributed Objects”


  1. Frank said:

    Dear Thomas,

    how about running a DSA-distributed application under “job scheduler conditions” on clusters?
    I am a bit puzzled: normally, one gets a bunch of compute nodes there. MPI’s mpiexec would replicate an “mpi”-ed binary onto these nodes and start them.
    How to do something similar with the DSA approach? (Or is a very different approach needed here?)

    Related to the is another question: many MPI programs follow the SPMD approach. I guess that this would have to be done with an array of distributed objects in the DSA world. How could I create these during run time on selected nodes/partitions?

    Thanks a lot in advance.


  2. Pascal said:

    Dear Frank,

    I would use the following approach. On cluster where you have a “home” directory available on every node I would
    use a script as starter for the application. This script
    would then start the proper partition on each node.

    For the other question a solution is to use remote objects (RACW). It is possible to have multiple instances of RACW
    in a distributed application. The remote object will
    register to a controller. Those objects will be requested
    by other partitions when needed. Data can then be sent to each of them doing the same computation with different
    data.

    Of course Ada distributed annex is very flexible, so it
    is possible to come with different solutions I would say.


  3. Thomas Quinot said:

    Frank,

    The usual pattern to have a number of similar nodes cooperating to share some work load would be for each node (possibly running the same executable image) to create an object and to register a RACW designating that object with a partition acting as a connection point. That partition can provide that registration service using a RCI unit, which could be implemented using an array of RACWs (i.e. an array of references to distributed objects. For an example of this pattern, see package Hub in the 3rd DSA gem (Gem #87, Mailboxes).

Leave a Reply