Next: Pragma All_Calls_Remote, Previous: Exceptions, Up: More About Categorization Pragmas

##### 8.1.7.4 Pragma Asynchronous

By default, a remote call is blocking: the caller waits until the remote call is complete and the output stream is received. Just like a normal (nonremote) call, the caller does not proceed until the call returns. By contrast, a remote subprogram labeled with pragma Asynchronous allows statically and dynamically bound remote calls to it to be executed asynchronously. A call to an asynchronous procedure doesn't wait for the completion of the remote call, and lets the caller continue its execution. The remote procedure must have only in parameters, and any exception raised during the execution of the remote procedure is lost.

When pragma Asynchronous applies to a regular subprogram with in parameters, any call to this subprogram will be executed asynchronously. The following declaration of AsynchronousRCI.Asynchronous gives an example.

         package AsynchronousRCI is
            pragma Remote_Call_Interface;

            procedure Asynchronous (X : Integer);
            pragma Asynchronous (Asynchronous);

            procedure Synchronous  (X : Integer);

            type AsynchronousRAS is access procedure (X : Integer);
            pragma Asynchronous (AsynchronousRAS);
         end AsynchronousRCI;

         package AsynchronousRT is
            pragma Remote_Types;

            type Object is tagged limited private;

            type AsynchronousRACW is access all Object'Class;
            pragma Asynchronous (AsynchronousRACW);

            procedure Asynchronous (X : Object);
            procedure Synchronous  (X : in out Object);
            function Create return AsynchronousRACW;

         private
            --  implementation removed
         end AsynchronousRT;

A pragma Asynchronous may apply to a remote access-to-subprogram (RAS) type. An asynchronous RAS can be both asynchronous and synchronous depending on the designated subprogram. For instance, in the code below, remote call (1) is asynchronous but remote call (2) is synchronous.

A pragma Asynchronous may apply to a RACW as well. In this case, the invocation of any method with in parameters is always performed asynchronously. Remote method invocation (3) is asynchronous but remote method invocation (4) is synchronous.

         with AsynchronousRCI, AsynchronousRT;
         use AsynchronousRCI, AsynchronousRT;
         procedure AsynchronousMain is
            RAS  : AsynchronousRAS;
            RACW : AsynchronousRACW := Create;
         begin
            --  Asynchronous Dynamically Bound Remote Call (1)
            RAS := AsynchronousRCI.Asynchronous'Access;
            RAS (0);  --  Abbrev for RAS.all (0)
            --  Synchronous Dynamically Bound Remote Call (2)
            RAS := AsynchronousRCI.Synchronous'Access;
            RAS (0);
            --  Asynchronous Dynamically Bound Remote Call (3)
            Asynchronous (RACW.all);
            --  Synchronous Dynamically Bound Remote Call (4)
            Synchronous (RACW.all);
         end AsynchronousMain;

This feature supports the conventional message passing paradigm. The user must be aware that this paradigm, and asynchronous remote calls in particular, has several drawbacks:

- It violates the normal semantics of calls; the caller proceeds without awaiting the return. The semantics are more similar to a “remote goto” than a remote call
- It prevents easy development and debugging in a non-distributed context
- It can introduce race conditions

To illustrate the latter, let us take the following example:

         package Node2 is
            pragma Remote_Call_Interface;

            procedure Send (X : Integer);
            pragma Asynchronous (Send);
         end Node2;

         package body Node2 is
            V : Integer := 0;
            procedure Send (X : Integer) is
            begin
               V := X;
            end Send;
         end Node2;

         package Node1 is
            pragma Remote_Call_Interface;

            procedure Send (X : Integer);
            pragma Asynchronous (Send);
         end Node1;

         with Node2;
         package body Node1 is
            procedure Send (X : Integer) is
            begin
               Node2.Send (X);
            end Send;
         end Node1;

         with Node1, Node2;
         procedure NonDeterministic is
         begin
            Node1.Send (1);
            Node2.Send (2);
         end NonDeterministic;

Let us say that Main is configured on Partition_0, Node1 on Partition_1 and Node2 on Partition_2. If Node1.Send and Node2.Send procedures were synchronous or if no latency was introduced during network communication, we would have the following RPC order: Main remotely calls Node1.Send which remotely calls Node2.Send which sets V to 1. Then, Main remotely calls Node2.Send and sets V to 2.

Now, let us assume that both Send procedures are asynchronous and that the connection between Partition_1 and Partition_2 is very slow. The following scenario can very well occur. Main remotely calls Node1.Send and is unblocked. Immediately after this call, Main remotely calls Node2.Send and sets V to 2. Once this is done, the remote call to Node1.Send completes on Partition_1 and it remotely calls Node2.Send which sets V to 1.