Control Messages

## 10.5 Control Messages

- Messages from ECLiPSe side to remote side
- Messages from remote side to ECLiPSe side
- The disconnection protocol

These are the messages that are exchanged between the ECLiPSe and remote sides when the remote side is attached. The messages are sent on the control connection, and are in EXDR format. All arguments for the messages are either atoms or integers. The messages are used to co-ordinate and synchronise the two sides. A message should only be sent from a particular side when that side has control, and control is handed over to the other side when the message is sent.

Most messages are used to initiate an interaction with the other side. That is, used to cause some action to take place on the other side: control is handed over, the action takes place on the other side, and eventually control is handed back when the action is completed. Control can also be explicitly handed over from one side to the other so that the other side can initiate interactions. Some additional messages can only be sent as part of an interaction, for exchanges of information between the two sides. Finally, there are messages for terminating the remote attachment. Note that interactions can be nested, that is, an interaction from one side can contain an interaction initiated from the other side.

The implementer for the remote interface should provide the methods for a programmer to initiate the interactions from the remote side. These routines would send the appropriate control messages to the ECLiPSe side, and the messages should not be directly visible to the programmer.

The usage of each message is summarised in the diagram(s) accompanying them. These diagrams show:

- time proceeds downward. The ECLiPSe side is shown on the left, the remote side on the right. Messages are shown as vertical arrows between the two sides. The direction of the arrow indicates the direction the message is sent.
- the context in which the message can be sent, i.e. the message from the other side that it is either expecting as a response, or that it is a response to. The message sequence is shown, with the message highlighted.
- any accompanying actions expected with the message. These actions are either sending or receiving data on some other connections between the two sides. These are shown as dashed arrows in the diagrams.
- whether nested interactions can take place between messages of an interaction. This is indicated by vertical ellipsis between the messages in the diagram. In such cases, the nested interaction can be initiated, and this interaction completed before the next message for the original interaction is expected.

### 10.5.1 Messages from ECLiPSe side to remote side

yield
  this yields control to the remote side. The message is used either to implicitly return control to the remote side at the end of an interaction initiated from the remote side, or it is used to explicitly hand over control to the remote side.

  The interaction-initiating messages from the remote side will be described in more detail in their own sections.

ec_flushio(Queue, Length)
  this message is sent when output on a remote synchronous queue is flushed on the ECLiPSe side:

  Queue is the ECLiPSe stream number for the peer queue, and Length is the number of bytes that is being sent on the queue. Control is yielded to the remote side. The data on the queue Queue will be sent through the queue after sending this message on the control connection, so on receiving this message on the remote side, the remote side should read Length bytes from Queue. After processing the data, the remote side should return control to the ECLiPSe side via a resume message.

ec_waitio(Queue)
  this message is sent when ECLiPSe requests input from a remote synchronous queue, and the data is not available in the queue’s buffer.

  This interaction is triggered when the ECLiPSe side attempts a read operation on the empty buffer of a peer queue. The operation is suspended, and control is yielded to remote side so that it can provide the required data. There should be a data-provider handler associated with the queue on the remote side. This handler should obtain the data, and send the data to the ECLiPSe side. The data will arrive from the remote side via a rem_flushio message, which initiates a remote flushio interaction, nested within the ECLiPSe waitio interaction. The data arrives on the socket associated with the remote peer queue Queue, and is automatically copied by ECLiPSe into the peer queue buffer. Control is then yielded back to the remote side, completing the flushio interaction. The remote side then hands control back to ECLiPSe side by the resume message. The suspended read operation is resumed on the now non-empty buffer.

  The remote flushio interaction is described in more detail in its own section. The main difference between a remote flushio initiated on the remote side and one initiated by an ECLiPSe waitio described here is that there must not be a data-consumer handler on the ECLiPSe side, as the data is to be consumed by the suspended read operation instead. This is ensured in the protocol by prohibiting handlers on both sides of a synchronous peer queue.

  Note that the ECLiPSe side will also listen to the control connection while waiting for the data to be sent from the remote side. If a resume is sent before the data arrives, this is likely caused by a programming error in the data provider handler, which finished without sending data to the ECLiPSe side. The ECLiPSe side will print a warning message on the warning output stream, and immediately yield back to the remote side. Other messages are handled as normal, recursively while waiting for the data to arrive – this is mainly intended to allow for unexpected aborts from the remote side, although it could also be used to perform ec_rpc calls before the remote side sends the data.

socket_client(Port, Name, Type, Dir)
  this requests the remote side to form a client socket connection for the remote peer queue Name. The queue is of type Type (sync or async), and direction Dir (fromec, toec for synchronous queues, bidirect for asynchronous queues). The client socket is to connect at port Port with the ECLiPSe side host name.

  The ECLiPSe side first creates a server socket for the peer queue Name. The port address is Port. This, along with the details of the queue is passed to the remote side via the socket_client message. The remote side should then connect a client socket with Port as the port, and the Host used for the initial attachment (which is either localhost or the hostname of the `eclipse` side) for the host. It should also perform any additional setups for the peer queue using the information sent with the message (typically this involves setting up book-keeping information for the queue on the remote side). When the remote side connection is established, it returns control to ECLiPSe via a socket_connect message:

  socket_connect(Name, Status)

  Name is the name of the queue, and should be the same as the Name sent by the socket_client message. This is used to verify that the messages refer to same interaction. The ECLiPSe side will raise an error and disconnect from the remote side if the name does not match. Status is either success or fail, depending on if the remote side successfully created the remote side of the queue or not.

  If Status is success, then the ECLiPSe side will complete the connection for the peer queue by accepting the socket connection. Since the remote end of the socket exists, the accept operation should succeed very quickly. If not, the operation will time-out, using the time-out interval specified when the attachment was made. The server socket is closed immediately after the accept operation. On successful connection, ECLiPSe first checks that this client’s host is indeed the same as the one previously recorded for the remote side. If so, the ECLiPSe will finish creating the ECLiPSe side of the queue. If not, the connection is closed, and the operation is considered to have failed.

  If Status from the socket_connect message is fail, then the ECLiPSe side will clean up the preparation for the peer queue.

  The ECLiPSe side then returns control to the remote side via a socket_accept message:

  socket_accept(Name,Queue)

  Name is again the name of the queue, and Queue the stream id. If the accept was unsuccessful (or if Status for socket_connect was fail), then Queue will be the atom fail, indicating that the peer queue connection was unsuccessful.

  The remote side should then record the id Queue for later use (it is needed for the control messages connected with this peer queue). If instead fail was received, then the remote side should clean up the attempted queue connection. When the remote side has finished the final stage of the connection, control is returned to the ECLiPSe side via a resume message, and the socket_client interaction completes.

  Note that the socket_connect and socket_accept messages are always exchanged during a socket_client interaction, even if the connection failed on the remote side before socket_connect is sent. They also can only occur in this context. If these messages occur in any other occasion, an error should be raised.

queue_close(Queue)
  this message is sent when the ECLiPSe side closes the peer queue with id Queue.

  The remote side should close the remote side of the peer queue Queue, and remove all bookkeeping information associated with it. Control should then be returned to ECLiPSe via a resume message. The ECLiPSe side should also close the queue and remove bookkeeping information on the ECLiPSe side.

disconnect
  this message is sent when ECLiPSe side initiates disconnect.

  Control is yielded to the remote side, which should acknowledge with the disconnect_resume message. Once the ECLiPSe side receives this message, the connection between the two sides is considered terminated. The ECLiPSe side will then close all the connections (the control and ec_rpc connections, and any asynchronous and synchronous queues) to the remote side, and clean up the information associated with the attachment. After sending the disconnect_resume message, the remote side should also shutdown its end of the connection by closing all the connections on its side.

  Note that the disconnection message can be used to terminate the attachment from within an interaction. In such cases, the interaction(s) would not be completed.

disconnect_yield
  this message is sent when ECLiPSe side receives a disconnect message from the remote side, i.e. the remote side initiated disconnection.

  This message is sent as an acknowledgement to the disconnect message. Once the disconnect_yield is sent, the connection is considered terminated, and the ECLiPSe side will close all the connections and clean up. After the clean up, abort is called. This is done because the application on the ECLiPSe side (such as the remote development tools) may be deep inside some interaction loop with the remote side, and abort is the most general way of escaping from such a loop. It can be caught (see remote_yield/1 in section 10.6) if the user wants a more graceful termination.

### 10.5.2 Messages from remote side to ECLiPSe side

resume
  this message hands over control from remote side to ECLiPSe side. This is used to either implicitly return control to the ECLiPSe side at the end of an interaction initiated from the ECLiPSe side, or to explicitly hand over control to the remote side.
rpc
  this message is sent before the remote side sends an ec_rpc goal on the rpc connection.

  After sending the rpc message, the remote side should then send the ec_rpc goal (in EXDR format) on the rpc connection. When the execution of the ec_rpc goal is finished, the ECLiPSe side will yield control back to the remote side with a yield message, followed by the result of the ec_rpc execution on the rpc connection (in EXDR format) – the goal with its bindings if the execution succeeded; ‘fail’ if the goal failed; ‘throw’ if an exception is generated.

rem_flushio(Queue)
  this message is sent if the remote side wishes to transfer data to the ECLiPSe side on peer queue with queue id Queue, and the remote side does not know how many bytes will be sent with the operation.

  After sending the message, control is transferred over to the ECLiPSe side, and the data is sent on the socket stream. On the ECLiPSe side, if the queue is a synchronous queue, then the data sent must be a single EXDR term, because otherwise the ECLiPSe side would not know when the data transfer is complete. The ECLiPSe side would read the data from the socket stream as a single EXDR term, which is then written onto the buffer. If an event handler has been associated with the peer queue, this will now be invoked to consume the data from the buffer. If not (for example, if the rem_flushio was initiated by an ec_waitio message), then the data is left on the buffer to be processed later.

  The rem_flushio message can also be used to sending data to ECLiPSe for asynchronous queues as well. In this case, an event handler is directly associated with the socket stream, and this event is invoked when the rem_flushio message is received. The event handler goal in this case is invoked with the ‘culprit’ argument being the term rem_flushio(Queue, Len), where Len is the atom ‘unknown’. It is up to the user-defined event handler goal to properly read the data: since the length is unknown, the data sent should have natural boundaries, e.g. EXDR terms, or use a mutually agreed ‘end of data’ marker.

rem_flushio(Queue, Length)
  this message is sent if the remote side wishes to transfer data to the ECLiPSe side on peer queue with queue id Queue, and the length of data to be sent is known, and is specified in Length (the number of bytes to be sent).

  After sending the message, control is transferred over to the ECLiPSe side, and the data is sent on the socket stream. On the ECLiPSe side, if the queue is a synchronous queue, then it would read Length bytes of data from the socket stream and transfer the data to the queue buffer. If an event handler has been associated with the peer queue, this will now be invoked to consume the data from the buffer. If not (for example, if the rem_flushio was initiated by an ec_waitio message), then the data is left on the buffer to be processed later.

  In the case that the peer queue is an asynchronous queue, an event handler is directly associated with the socket stream, and this event is invoked when the rem_flushio message is received. The event handler goal in this case is invoked with the ‘culprit’ argument being the term rem_flushio(Queue, Length), It is up to the user-defined event handler goal to properly read the data.

queue_create(Name, Type, Dir, Event)
  this message is sent when the remote side wishes to initiates the creation of a new peer queue. Name is the name of the peer queue, Type is its Type: sync for synchronous, async for asynchronous. Dir is the data direction: fromec or toec, and Event is the name of the event that will be raised for the event handler goal on the ECLiPSe side, if no event is to be associated with the queue, this should be the empty atom (`''`).

  Control is handed over to ECLiPSe side, which should then set up a new server socket for connecting the socket stream for the peer queue. Once this server socket is set up, the creation of the queue proceeds via a socket_client interaction from the ECLiPSe, i.e. the ECLiPSe side sends a socket_client message. For more detail, see the description for the socket_client message. At the end of the socket_client interaction, the peer queue would be established, and ECLiPSe side has control. The ECLiPSe side will yield control back to the remote side, completing the queue_create interaction.

  Note that the socket_client interaction is performed by the ECLiPSe built-in peer_queue_create/5, this is the goal that ECLiPSe calls on receiving the queue_create message.

  If the initial creation of the socket server fails, then the ECLiPSe side will not initiate a socket_client interaction. Instead, it will simple yield control back to the remote side with a yield message. In this case, no peer queue is created.

queue_close(Queue)
  this message is sent when the remote side closes the peer queue with id Queue.

  The ECLiPSe side should close the ECLiPSe side of the peer queue Queue, and remove all bookkeeping information associated with it. Control should then be returned to ECLiPSe via a yield message. The queue should also be closed on the remote side, with the bookkeeping information removed too.

disconnect
  this message is sent if the remote side wishes to initiate disconnection.

  Control is handed over to the ECLiPSe side, which will acknowledge with disconnect_yield message. Once the ECLiPSe side receives this message, the remote attachment between the two sides is considered terminated. The remote side should now close all connections to the ECLiPSe side. Concurrently, the ECLiPSe side will also close down its end of the connections.

  The disconnect message can be issued during an interaction. In such cases, the interaction will be terminated early along with the attachment.

disconnect_resume
  this message is sent in acknowledgement of a disconnection initiated from the ECLiPSe side.

  After sending this message, the remote attachment between the two sides is considered terminated. The remote side should now close all connections to the ECLiPSe side. Concurrently, the ECLiPSe side will also close down its end of the connections. In addition, this message should be sent if the remote side has to terminate the attachment while the ECLiPSe side has control. This can happen if the remote process is forced to quit. This is the only case where a message can be sent via the control connection on the remote side while it does not have control. Once the message is sent, the remote side can terminate its connection unilaterally.

### 10.5.3 The disconnection protocol

Under normal circumstances, the disconnection of the two sides is initiated by the side that has control, by sending a disconnect message to the other side. The other side acknowledges this by responding with a disconnect_yield (ECLiPSe side) or disconnect_resume (remote side). The acknowledgement should be sent when that side is ready to disconnect. Once the messages have been exchanged, both sides should be ready, and can physically disconnect. The exchange of messages should ensure that any asynchronous I/O between the two sides are properly terminated.

However, under some circumstances, a side may be forced to disconnect when it does not have control. For example, in the Tcl remote interface, the root window for the Tcl process may be destroyed by the user. In such cases, a unilateral disconnect will be performed by that side – only the second part of the normal disconnect protocol is performed by sending the disconnect acknowledge message (disconnect_resume or disconnect_yield) without being initiated by a disconnect message.

The ECLiPSe side checks the control connection for any unexpected incoming messages before it sends an outgoing control message. If there is a disconnect_resume message, the ECLiPSe side will perform the disconnection on its side.

When the user exits normally from an ECLiPSe session, ECLiPSe will disconnect from all remote attachments. This is done in sepia_end/0 event handler.

As part of the disconnection process on the ECLiPSe side, a user definable event will be raised in ECLiPSe, just before the remote queues are closed. This allows the user to define application specific handlers for dealing with the disconnection of the remote interface (on the ECLiPSe side). A similar handler should probably be provided on the remote side. The event raised has the same name as the control stream. The event handler for this event is initially defined to be true/0 (i.e. a no-op) when the remote connection is set up. The handler can then be redefined by the user, e.g. during the user-defined initialisation during attachment:

        ...
        remote_connect(localhost/MyPort, Control,
            set_event_handler(Control, my_disconnect_handler/1)),
        ...

        my_disconnect_handler(Remote) :-
        % just print out a message about the disconnection
            printf("Disconnected from remote attachment %w", [Remote]).