Getting the Value through an Event

In the previous section I have the client blocking while waiting for a reply from an interaction with the counter object. This does not mesh with the style of CML which is to separate the construction of an interaction from waiting for it to complete. What I need is to have a Get request return an event which the client can wait on. Then the client has the option of, for example, timing-out the reply or waiting for replies from multiple sources.

Events will typically be used with the CML select or choose function to decide among several interactions. When an event is chosen the program becomes commited to that choice. The event is said to represent the commit point in the interaction.

For the simple request-reply interaction of the counter object there are two places I can put the commit point. The simplest and most direct is to put it in the request phase of the interaction. This means that the event will be enabled when the counter is ready to accept a request. Once the event is chosen and synchronised then the program is committed to waiting for the reply. If the client were to use a time-out with this kind of event it would be timing-out waiting for the counter to become ready to accept the request, which is probably not what's intended.

Alternatively the commit point can be put in the reply phase. The event becomes enabled when the reply is received. This would allow the client to wait for a reply or for a time-out.

For the counter object, committing in the request phase will be fine since the object will reply quickly. The following example shows the counter object with an event that commits in the request. I have added a getEvt function to the counter's interface. This returns an event that will deliver the counter's value when synchronised.

signature COUNTER =
sig
    type Counter

    val new:    int -> Counter 
    val incr:   Counter -> int -> unit
    val get:    Counter -> int
    val getEvt: Counter -> int CML.event

end

Here is the implementation of the Get interaction.

fun getEvt (Counter {req_chan, rpl_chan}) =
let
    fun recv() =
    (
        case CML.recv rpl_chan of
          ReplyIsCount n => n
    )
in
    CML.wrap(CML.sendEvt(req_chan, ReqIsGet), recv)
end

fun get counter = CML.sync(getEvt counter)

In the getEvt function I use sendEvt to get an event that is enabled when the request has been passed to the counter. This event is wrapped with the recv function which will wait for the reply and return it. The original get function becomes a simple synchronisation on the Get event.

Because of the synchronous nature of channel communication the ReqIsGet message won't pass to the counter until the client has synchronised on the send event and the counter is waiting to receive. When the message is passed the client immediately goes on to wait for the reply and the counter immediately sends the reply so there is no further delay.