E.4 Remote Subprogram Calls
1
{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.
2
There are three different
ways of performing a remote subprogram call:
3
- As a direct call on a (remote) subprogram
explicitly declared in a remote call interface;
4
- As an indirect call through a value
of a remote access-to-subprogram type;
5
- As a dispatching call with a controlling
operand designated by a value of a remote access-to-class-wide type.
6
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.
7
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
7.a
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
8
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
9
{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.]
10
{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.
10.a
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.
11
{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.
12
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.
13
{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.
13.a
Implementation defined: Whether the execution
of the remote subprogram is immediately aborted as a result of cancellation.
14
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).
15
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.
16
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.
17
{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).
17.a
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.
18/1
{
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.
18.a/1
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.
18.b
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.
18.c
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.
19
{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.
19.a
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
20
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.
20.a
Implementation Note: One possible implementation
model is as follows:
20.b
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.
20.c
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.
20.d
For the statically bound remote calls, the identity
of the remote partition is statically determined (it is resolved at configuration/link
time).
20.e
The calling stub
operates as follows:
20.f
- 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.
20.g
- It allocates (or reuses) a stream
for the Result, unless a pragma Asynchronous is applied to the procedure.
20.h
- 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.
20.i
- 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.
20.j
On the receiving
side, the RPC-receiver procedure operates as follows:
20.k
- 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.
20.l
- It extracts information from
the stream to identify the appropriate receiving stub.
20.m
- The receiving stub extracts the
in and in out parameters using Read from the stream designated
by the Params parameter.
20.n
- 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.
20.o
For remote access-to-subprogram types:
20.p
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.
20.q
For remote access-to-class-wide types:
20.r
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.
20.s
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.
20.t
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.
20.u
{
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).
20.v
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.
20.1/1
{
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).
21
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.
22
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.
22.a
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
22.b/2
22.c/2
{
8652/0085}
{
AI95-00215-01}
Corrigendum: Clarified that the check on
class-wide types also applies to values returned from remote subprogram
call functions.