Inspirel banner

Programming Distributed Systems with YAMI4

5.1.1 Ada

In Ada the outgoing message has to be created by the user code as the instance of library-provided protected type:

with YAMI.Outgoing_Messages; use YAMI.Outgoing_Messages;
--  ...

declare
   Message : aliased Outgoing_Message;

The Message object is aliased so that the agent can refer to it and update it accordingly to the state changes of the internally controlled message.

The outgoing message object is logically initialized when the agent is asked to send a message:

begin
   My_Agent.Send
     ("tcp://somewhere:12345", "my_object", "do_something",
      Content, Message'Unchecked_Access);
   --  ...
end;

Above, the message is sent to the given target, destination object name and with a given message name. The Content object is supposed to be a parameters object; if it is omitted, the empty object is used by default. The outgoing message protected object is passed to the agent as an access value - here, the unchecked access value is taken, because the access type is defined at the library level and the example Message object is declared locally. This usage of the unchecked access value is safe if it is ensured that the agent will not use the message object longer than its lifetime - this is the case when within the same scope the code waits for message completion, as presented in the next example. In more realistic systems, however, the outgoing message might be better placed at the library level to avoid the potential problem of dangling references.

The possible message states are defined in the Message_State type in the same package. As described in the main part of this section, message states can be tracked and used for synchronization to enable the user code to align its processing with the message progress that is performed by background tasks. Thus, a complete idiom for message sending in Ada can involve the following structure:

with YAMI.Outgoing_Messages; use YAMI.Outgoing_Messages;
--  ...

declare
   Message : aliased Outgoing_Message;
   State : Message_State;

   procedure Process_Reply
     (Reply_Content : in out Parameters_Collection) is
   begin
      --  ...
   end Process_Reply;

begin

   Client_Agent.Send
     ("tcp://somewhere:12345", "my_object", "do_something",
      Content, Message'Unchecked_Access);

   --   do something else while the message makes progress
   --   or synchronize with its completion:
   Message.Wait_For_Completion;

   State := Message.State;
   if State = Replied then

      Message.Process_Reply_Content (Process_Reply'Access);

   elsif State = Rejected then
      --  Message.Exception_Message is an error's description

   else
      --  the message has been abandoned,
      --  because the given channel has been closed
   end if;

exception
   when Runtime_Error =>
      --  message could not be sent because the connection
      --  was not established with remote agent
end;

In addition to the proper idiom for managing complete messaging cycle in the command-response interaction, the above example shows that in case of successful reception of message reply, the reply content can be accessed and processed with a procedure that accepts the parameters object as its only argument. In many cases such a procedure will be declared locally.

The Wait_For_Completion and similar Wait_For_Transmission calls are protected entry calls and can be used with more sophisticated concurrency-related constructs.

A single outgoing message object can be reused for the purpose of handling subsequent messages. This approach is recommended for high performance systems where short messaging cycles are expected.