-
Example of using a remote access-to-class-wide type to achieve dynamic
binding across active partitions:
-
package Tapes is
pragma Pure(Tapes);
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;
-
with Tapes;
package Name_Server is
pragma Remote_Call_Interface;
-- 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;
-
package Tape_Driver is
-- Declarations are not shown, they are irrelevant here
end Tape_Driver;
-
with Tapes, Name_Server;
package body Tape_Driver is
type New_Tape is new Tapes.Tape with ...
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;
-
with 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;
-
Notes on the example:
-
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 needs
to 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.
-
The Partition Communication Subsystem (PCS) provides facilities for
supporting communication between the active partitions of a distributed
program. The package System.RPC is a language-defined interface to the
PCS. An implementation conforming to this Annex shall use the RPC
interface to implement remote subprogram calls.
Static Semantics
-
The following language-defined library package exists:
-
with Ada.Streams; -- see section The Package Streams.
package System.RPC is
-
type Partition_ID is range 0 .. implementation-defined;
-
Communication_Error : exception;
-
type Params_Stream_Type(
Initial_Size : Ada.Streams.Stream_Element_Count) is new
Ada.Streams.Root_Stream_Type with private;
-
procedure Read(
Stream : in out Params_Stream_Type;
Item : out Ada.Streams.Stream_Element_Array;
Last : out Ada.Streams.Stream_Element_Offset);
-
procedure Write(
Stream : in out Params_Stream_Type;
Item : in Ada.Streams.Stream_Element_Array);
-
-- Synchronous call
procedure Do_RPC(
Partition : in Partition_ID;
Params : access Params_Stream_Type;
Result : access Params_Stream_Type);
-
-- Asynchronous call
procedure Do_APC(
Partition : in Partition_ID;
Params : access Params_Stream_Type);
-
-- The handler for incoming RPCs
type RPC_Receiver is access procedure(
Params : access Params_Stream_Type;
Result : access Params_Stream_Type);
-
procedure Establish_RPC_Receiver(
Partition : in Partition_ID;
Receiver : in RPC_Receiver);
-
private
... -- not specified by the language
end System.RPC;
-
A value of the type Partition_ID is used to identify a partition.
-
An object of the type Params_Stream_Type is used for identifying the
particular remote subprogram that is being called, as well as
marshalling and unmarshalling the parameters or result of a remote
subprogram call, as part of sending them between partitions.
-
The Read and Write procedures override the corresponding abstract
operations for the type Params_Stream_Type.
Dynamic Semantics
-
The Do_RPC and Do_APC procedures send a message to the active partition
identified by the Partition parameter.
-
After sending the message, Do_RPC blocks the calling task until a reply
message comes back from the called partition or some error is detected
by the underlying communication system in which case Communication_Error
is raised at the point of the call to Do_RPC.
-
Do_APC operates in the same way as Do_RPC except that it is allowed to
return immediately after sending the message.
-
Upon normal return, the stream designated by the Result parameter of
Do_RPC contains the reply message.
-
The procedure System.RPC.Establish_RPC_Receiver is called once,
immediately after elaborating the library units of an active partition
(that is, right after the elaboration of the partition) if the partition
includes an RCI library unit, but prior to invoking the main subprogram,
if any. The Partition parameter is the Partition_ID of the active
partition being elaborated. The Receiver parameter designates an
implementation-provided procedure called the RPC-receiver which will
handle all RPCs received by the partition from the PCS.
Establish_RPC_Receiver saves a reference to the RPC-receiver; when a
message is received at the called partition, the RPC-receiver is called
with the Params stream containing the message. When the RPC-receiver
returns, the contents of the stream designated by Result is placed in a
message and sent back to the calling partition.
-
If a call on Do_RPC is aborted, a cancellation message is sent to the
called partition, to request that the execution of the remotely called
subprogram be aborted.
-
The subprograms declared in System.RPC are potentially blocking
operations.
Implementation Requirements
-
The implementation of the RPC-receiver shall be reentrant, thereby
allowing concurrent calls on it from the PCS to service concurrent
remote subprogram calls into the partition.
Documentation Requirements
-
The implementation of the PCS shall document whether the RPC-receiver is
invoked from concurrent tasks. If there is an upper limit on the number
of such tasks, this limit shall be documented as well, together with the
mechanisms to configure it (if this is supported).
Implementation Permissions
-
The PCS is allowed to contain implementation-defined interfaces for
explicit message passing, broadcasting, etc. Similarly, it is allowed to
provide additional interfaces to query the state of some remote
partition (given its partition ID) or of the PCS itself, to set timeouts
and retry parameters, to get more detailed error status, etc. These
additional interfaces should be provided in child packages of
System.RPC.
-
A body for the package System.RPC need not be supplied by the
implementation.
Implementation Advice
-
Whenever possible, the PCS on the called partition should allow for
multiple tasks to call the RPC-receiver with different messages and
should allow them to block until the corresponding subprogram body
returns.
-
The Write operation on a stream of type Params_Stream_Type should raise
Storage_Error if it runs out of space trying to write the Item into the
stream.
NOTES
-
(8) The package System.RPC is not designed for direct calls by user
programs. It is instead designed for use in the implementation of remote
subprograms calls, being called by the calling stubs generated for a
remote call interface library unit to initiate a remote call, and in
turn calling back to an RPC-receiver that dispatches to the receiving
stubs generated for the body of a remote call interface, to handle a
remote call received from elsewhere.