E.4 Remote Subprogram Calls
{remote subprogram
call} {asynchronous
remote procedure call [distributed]} {calling
partition} {called
partition} {remote
subprogram binding} A
remote subprogram
call is a subprogram call that invokes the execution of a subprogram
in another 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.
A remote call interface library unit (see
E.2.3)
defines the remote subprograms or remote access types used for remote
subprogram calls.
Language Design Principles
Remote subprogram calls are standardized since
the RPC paradigm is widely-used, and establishing an interface to it
in the annex will increase the portability and reusability of distributed
programs.
Legality Rules
In 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.
Dynamic Semantics
{marshalling}
{unmarshalling}
{execution (remote
subprogram call) [partial]} For 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.]
{calling stub}
{receiving stub}
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.
Discussion: The use of the term
stub
in this annex should not be confused with
body_stub
as defined in
10.1.3. The term
stub
is used here because it is a commonly understood term when talking about
the RPC paradigm.
{at-most-once execution}
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.
{cancellation of
a remote subprogram call} 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.
Implementation defined: Whether the execution
of the remote subprogram is immediately aborted as a result of cancellation.
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.
{potentially blocking
operation (remote subprogram call) [partial]} {blocking,
potentially (remote subprogram call) [partial]} All
forms of remote subprogram calls are potentially blocking operations
(see
9.5.1).
Reason: Asynchronous remote procedure
calls are potentially blocking since the implementation may require waiting
for the availability of shared resources to initiate the remote call.
{
8652/0085}
{
AI95-00215-01}
{Accessibility_Check [partial]}
{check, language-defined
(Accessibility_Check)} 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
(raised by failure of run-time check)} 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.
Discussion: {
8652/0085}
{
AI95-00215-01}
This check makes certain that the specific type passed or returned in
an RPC satisfies the rules for a "communicable" type. Normally
this is guaranteed by the compile-time restrictions on remote call interfaces.
However, with class-wide types, it is possible to pass an object whose
tag identifies a type declared outside the "safe" packages.
This is considered an accessibility_check since
only the types declared in "safe" packages are considered truly
"global" (cross-partition). Other types are local to a single
partition. This is analogous to the "accessibility" of global
vs. local declarations in a single-partition program.
This rule replaces a rule from an early version
of Ada 9X which was given in the subclause on Remote Types Library Units
(now
E.2.2, “
Remote
Types Library Units”). That rule tried to prevent "bad"
types from being sent by arranging for their tags to mismatch between
partitions. However, that interfered with other uses of tags. The new
rule allows tags to agree in all partitions, even for those types which
are not "safe" to pass in an RPC.
{Partition_Check
[partial]} {check,
language-defined (Partition_Check)} 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_references
that were evaluated by tasks of the same active partition.
{Constraint_Error
(raised by failure of run-time check)} Constraint_Error
is raised if this check fails.
Implementation Note: When a remote access-to-class-wide
value is created by an Access
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
The 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.
Implementation Note: One possible implementation
model is as follows:
The code for calls to subprograms declared in
an RCI package is generated normally, that is, the call-site is the same
as for a local subprogram call. The code for the remotely callable subprogram
bodies is also generated normally. Subprogram's prologue and epilogue
are the same as for a local call.
When compiling the specification of an RCI package,
the compiler generates calling stubs for each visible subprogram. Similarly,
when compiling the body of an RCI package, the compiler generates receiving
stubs for each visible subprogram together with the appropriate tables
to allow the RPC-receiver to locate the correct receiving stub.
For the statically bound remote calls, the identity
of the remote partition is statically determined (it is resolved at configuration/link
time).
The calling stub
operates as follows:
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 a pragma Asynchronous is applied to the procedure.
It calls Do_RPC unless a pragma Asynchronous
is applied to 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 pragma 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.
On the receiving
side, the RPC-receiver procedure operates as follows:
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.
For remote access-to-subprogram types:
A value of a remote access-to-subprogram type
can be represented by the following components: a reference to the remote
partition, an index to the package containing the remote subprogram,
and an index to the subprogram within the package. The values of these
components are determined at run time when the remote access value is
created. These three components serve the same purpose when calling Do_APC/RPC,
as in the statically bound remote calls; the only difference is that
they are evaluated dynamically.
For remote access-to-class-wide types:
For each remote access-to-class-wide type, a
calling stub is generated for each dispatching operation of the designated
type. In addition, receiving stubs are generated to perform the remote
dispatching operations in the called partition. The appropriate
subprogram_body
is determined as for a local dispatching call once the receiving stub
has been reached.
A value of a remote access-to-class-wide type
can be represented with the following components: a reference to the
remote partition, an index to a table (created one per each such access
type) containing addresses of all the dispatching operations of the designated
type, and an access value designating the actual remote object.
Alternatively, a remote access-to-class-wide
value can be represented as a normal access value, pointing to a "stub"
object which in turn contains the information mentioned above. A call
on any dispatching operation of such a stub object does the remote call,
if necessary, using the information in the stub object to locate the
target partition, etc. This approach has the advantage that less special-casing
is required in the compiler. All access values can remain just a simple
address.
{
Constraint_Error (raised by failure of run-time
check)}
For a call to Do_RPC or Do_APC: The partition
ID of all controlling operands are checked for equality (a Constraint_Error
is raised if this check fails). The partition ID value is used for the
Partition parameter. An index into the
tagged-type-descriptor
is created. This index points to the receiving stub of the class-wide
operation. This index and the index to the table (described above) are
written to the stream. Then, the actual parameters are marshalled into
the message stream. For a controlling operand, only the access value
designating the remote object is required (the other two components are
already present in the other parameters).
On the called partition (after the RPC-receiver
has transferred control to the appropriate receiving stub) the parameters
are first unmarshalled. Then, the tags of the controlling operands (obtained
by dereferencing the pointer to the object) are checked for equality.
{
Constraint_Error (raised by failure of run-time check)}
If
the check fails Constraint_Error is raised and propagated back to the
calling partition, unless it is a result of an asynchronous call. Finally,
a dispatching call to the specific subprogram (based on the controlling
object's tag) is made. Note that since this subprogram is not in an RCI
package, no specific stub is generated for it, it is called normally
from the
dispatching stub.
{
8652/0086}
{
AI95-00159-01}
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).
6 A given active partition can both make
and receive remote subprogram calls. Thus, an active partition can act
as both a client and a server.
7 If a given exception is propagated by
a remote subprogram call, but the exception does not exist in the calling
partition, the exception can be handled by an others choice or
be propagated to and handled by a third partition.
Discussion: This situation can happen
in a case of dynamically nested remote subprogram calls, where an intermediate
call executes in a partition that does not include the library unit that
defines the exception.
Wording Changes from Ada 95
{
8652/0085}
{
AI95-00215-01}
Corrigendum: Clarified that the check on class-wide types also
applies to values returned from remote subprogram call functions.