E.4 Remote Subprogram Calls
This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue
A remote subprogram call is a subprogram call that invokes the execution of a subprogram in another (active) partition. The partition that originates the remote subprogram call is the calling partition, and the partition that executes the corresponding subprogram body is the called partition. Some remote procedure calls are allowed to return prior to the completion of subprogram execution. These are called asynchronous remote procedure calls.
There are three different ways of performing a remote subprogram call:
- As a direct call on a (remote) subprogram explicitly declared in a remote call interface;
- As an indirect call through a value of a remote access-to-subprogram type;
- As a dispatching call with a controlling operand designated by a value of a remote access-to-class-wide type.
The first way of calling corresponds to a static binding between the calling and the called partition. The latter two ways correspond to a dynamic binding between the calling and the called partition.
Remote types library units (see E.2.2) and remote call interface library units (see E.2.3) define the remote subprograms or remote access types used for remote subprogram calls.
Language Design Principles
Legality Rules
8In a dispatching call with two or more controlling operands, if one controlling operand is designated by a value of a remote access-to-class-wide type, then all shall be.
A nonblocking program unit shall not contain, other than within nested units with Nonblocking specified as statically False, a dispatching call with a controlling operand designated by a value of a remote access-to-class-wide type.
Dynamic Semantics
9For the execution of a remote subprogram call, subprogram parameters (and later the results, if any) are passed using a stream-oriented representation (see 13.13.1) [which is suitable for transmission between partitions]. This action is called marshalling. Unmarshalling is the reverse action of reconstructing the parameters or results from the stream-oriented representation. [Marshalling is performed initially as part of the remote subprogram call in the calling partition; unmarshalling is done in the called partition. After the remote subprogram completes, marshalling is performed in the called partition, and finally unmarshalling is done in the calling partition.]
A calling stub is the sequence of code that replaces the subprogram body of a remotely called subprogram in the calling partition. A receiving stub is the sequence of code (the “wrapper”) that receives a remote subprogram call on the called partition and invokes the appropriate subprogram body.
Remote subprogram calls are executed at most once, that is, if the subprogram call returns normally, then the called subprogram's body was executed exactly once.
The task executing a remote subprogram call blocks until the subprogram in the called partition returns, unless the call is asynchronous. For an asynchronous remote procedure call, the calling task can become ready before the procedure in the called partition returns.
If a construct containing a remote call is aborted, the remote subprogram call is cancelled. Whether the execution of the remote subprogram is immediately aborted as a result of the cancellation is implementation defined.
If a remote subprogram call is received by a called partition before the partition has completed its elaboration, the call is kept pending until the called partition completes its elaboration (unless the call is cancelled by the calling partition prior to that).
If an exception is propagated by a remotely called subprogram, and the call is not an asynchronous call, the corresponding exception is reraised at the point of the remote subprogram call. For an asynchronous call, if the remote procedure call returns prior to the completion of the remotely called subprogram, any exception is lost.
The exception Communication_Error (see E.5) is raised if a remote call cannot be completed due to difficulties in communicating with the called partition.
All forms of remote subprogram calls are potentially blocking operations (see 9.5 ).
{8652/0085} In a remote subprogram call with a formal parameter of a class-wide type, a check is made that the tag of the actual parameter identifies a tagged type declared in a declared-pure or shared passive library unit, or in the visible part of a remote types or remote call interface library unit. Program_Error is raised if this check fails. In a remote function call which returns a class-wide type, the same check is made on the function result.
In a dispatching call with two or more controlling operands that are designated by values of a remote access-to-class-wide type, a check is made [(in addition to the normal Tag_Check — see 11.5)] that all the remote access-to-class-wide values originated from Access attribute_reference
s that were evaluated by tasks of the same active partition. Constraint_Error is raised if this check fails.
attribute_reference
, the identity of the active partition that evaluated the attribute_reference
should be recorded in the representation of the remote access value. Implementation Requirements
20The implementation of remote subprogram calls shall conform to the PCS interface as defined by the specification of the language-defined package System.RPC (see E.5). The calling stub shall use the Do_RPC procedure unless the remote procedure call is asynchronous in which case Do_APC shall be used. On the receiving side, the corresponding receiving stub shall be invoked by the RPC-receiver.
- It allocates (or reuses) a stream of Params_Stream_Type of Initial_Size, and initializes it by repeatedly calling Write operations, first to identify which remote subprogram in the receiving partition is being called, and then to pass the incoming value of each of the in and in out parameters of the call.
- It allocates (or reuses) a stream for the Result, unless an aspect Asynchronous is specified as True for the procedure.
- It calls Do_RPC unless an aspect Asynchronous is specified as True for the procedure in which case it calls Do_APC. An access value designating the message stream allocated and initialized above is passed as the Params parameter. An access value designating the Result stream is passed as the Result parameter.
- If the aspect Asynchronous is not specified for the procedure, Do_RPC blocks until a reply message arrives, and then returns to the calling stub. The stub returns after extracting from the Result stream, using Read operations, the in out and out parameters or the function result. If the reply message indicates that the execution of the remote subprogram propagated an exception, the exception is propagated from Do_RPC to the calling stub, and thence to the point of the original remote subprogram call. If Do_RPC detects that communication with the remote partition has failed, it propagates Communication_Error.
- It is called from the PCS when a remote-subprogram-call message is received. The call originates in some remote call receiver task executed and managed in the context of the PCS.
- It extracts information from the stream to identify the appropriate receiving stub.
- The receiving stub extracts the in and in out parameters using Read from the stream designated by the Params parameter.
- The receiving stub calls the actual subprogram body and, upon completion of the subprogram, uses Write to insert the results into the stream pointed to by the Result parameter. The receiving stub returns to the RPC-receiver procedure which in turn returns to the PCS. If the actual subprogram body propagates an exception, it is propagated by the RPC-receiver to the PCS, which handles the exception, and indicates in the reply message that the execution of the subprogram body propagated an exception. The exception occurrence can be represented in the reply message using the Write attribute of Ada.Exceptions.Exception_Occurrence.
subprogram_body
is determined as for a local dispatching call once the receiving stub has been reached.{8652/0086} With respect to shared variables in shared passive library units, the execution of the corresponding subprogram body of a synchronous remote procedure call is considered to be part of the execution of the calling task. The execution of the corresponding subprogram body of an asynchronous remote procedure call proceeds in parallel with the calling task and does not signal the next action of the calling task (see 9.10).
Wording Changes from Ada 95
Wording Changes from Ada 2005
Wording Changes from Ada 2012
E.4.1 Asynchronous Remote Calls
1/3[This subclause introduces the aspect Asynchronous which can be specified to allow a remote subprogram call to return prior to completion of the execution of the corresponding remote subprogram body.]
Paragraphs 2 through 7 were deleted.
Static Semantics
8/3For a remote procedure, the following language-defined representation aspect may be specified:
Asynchronous
- The type of aspect Asynchronous is Boolean. If directly specified, the
aspect_definition
shall be a static expression. If not specified, the aspect is False.
For a remote access type, the following language-defined representation aspect may be specified:
Asynchronous
- The type of aspect Asynchronous is Boolean. If directly specified, the
aspect_definition
shall be a static expression. If not specified (including by inheritance), the aspect is False.
Legality Rules
8.4/3If aspect Asynchronous is specified for a remote procedure, the formal parameters of the procedure shall all be of mode in.
If aspect Asynchronous is specified for a remote access type, the type shall be a remote access-to-class-wide type, or the type shall be a remote access-to-procedure type with the formal parameters of the designated profile of the type all of mode in.
Dynamic Semantics
9/3A remote call is asynchronous if it is a call to a procedure, or a call through a value of an access-to-procedure type, for which aspect Asynchronous is True. In addition, if aspect Asynchronous is True for a remote access-to-class-wide type, then a dispatching call on a procedure with a controlling operand designated by a value of the type is asynchronous if the formal parameters of the procedure are all of mode in.
Implementation Requirements
10Asynchronous remote procedure calls shall be implemented such that the corresponding body executes at most once as a result of the call.
Extensions to Ada 2005
E.4.2 Example of Use of a Remote Access-to-Class-Wide Type
Examples
1Example of using a remote access-to-class-wide type to achieve dynamic binding across active partitions:
package Tapes
with Pure is
type Tape is abstract tagged limited private;
-- Primitive dispatching operations where
-- Tape is controlling operand
procedure Copy (From, To : access Tape;
Num_Recs : in Natural) is abstract;
procedure Rewind (T : access Tape) is abstract;
-- More operations
private
type Tape is ...
end Tapes;
3/5with Tapes;
package Name_Server
with Remote_Call_Interface is
-- Dynamic binding to remote operations is achieved
-- using the access-to-limited-class-wide type Tape_Ptr
type Tape_Ptr is access all Tapes.Tape'Class;
-- The following statically bound remote operations
-- allow for a name-server capability in this example
function Find (Name : String) return Tape_Ptr;
procedure Register (Name : in String; T : in Tape_Ptr);
procedure Remove (T : in Tape_Ptr);
-- More operations
end Name_Server;
4package Tape_Driver is
-- Declarations are not shown, they are irrelevant here
end Tape_Driver;
5/5with Tapes, Name_Server;
package body Tape_Driver is
type New_Tape is new Tapes.Tape with ...
overriding
procedure Rewind (T : access New_Tape);
overriding
procedure Copy
(From, To : access New_Tape; Num_Recs: in Natural) is
begin
. . .
end Copy;
procedure Rewind (T : access New_Tape) is
begin
. . .
end Rewind;
-- Objects remotely accessible through use
-- of Name_Server operations
Tape1, Tape2 : aliased New_Tape;
begin
Name_Server.Register ("NINE-TRACK", Tape1'Access);
Name_Server.Register ("SEVEN-TRACK", Tape2'Access);
end Tape_Driver;
6with Tapes, Name_Server;
-- Tape_Driver is not needed and thus not mentioned in the with_clause
procedure Tape_Client is
T1, T2 : Name_Server.Tape_Ptr;
begin
T1 := Name_Server.Find ("NINE-TRACK");
T2 := Name_Server.Find ("SEVEN-TRACK");
Tapes.Rewind (T1);
Tapes.Rewind (T2);
Tapes.Copy (T1, T2, 3);
end Tape_Client;
7/5Discussion of the example:
This paragraph was deleted.
- The package Tapes provides the necessary declarations of the type and its primitive operations.
- Name_Server is a remote call interface package and is elaborated in a separate active partition to provide the necessary naming services (such as Register and Find) to the entire distributed program through remote subprogram calls.
- Tape_Driver is a normal package that is elaborated in a partition configured on the processing node that is connected to the tape device(s). The abstract operations are overridden to support the locally declared tape devices (Tape1, Tape2). The package is not visible to its clients, but it exports the tape devices (as remote objects) through the services of the Name_Server. This allows for tape devices to be dynamically added, removed or replaced without requiring the modification of the clients' code.
- The Tape_Client procedure references only declarations in the Tapes and Name_Server packages. Before using a tape for the first time, it will query the Name_Server for a system-wide identity for that tape. From then on, it can use that identity to access the tape device.
- Values of remote access type Tape_Ptr include the necessary information to complete the remote dispatching operations that result from dereferencing the controlling operands T1 and T2.