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:
The interaction-initiating messages from the remote side will be described in more detail in their own sections.
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.
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.
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
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:
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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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]).