Re: ANN: Etiquette: a protocol construction tool

Luke Gorrie <[email protected]> 20 Sep 2003 19:20:49 +0200
Newsgroups gmane.lisp.clump
Message-ID <[email protected]>
"Thomas F. Burdick" <[email protected]> writes:

> Continuations are too general, though -- the state machine doesn't
> consist of all possible state, just the stat it consists.

I agree for many cases. Having an explicit set of states is nice in
that it focuses your attention on minimizing the states, which means
minimizing the interactions, which leads to nice protocols.

On the other hand, for some real protocols the state you want to
capture _is_ a continuation -- i.e. a stack and a program
counter. Continuations are awfully handy in those cases :-)

Consider reading an S-expression from a stream without blocking. Your
state has to include a stack so you know if you're reading a top-level
object, or if you're reading an element of a list within a vector
within a list, and so on. You also need to keep track of where you're
up to with reading the current object - maybe you're reading a symbol
and you have ten characters of it so far, etc.

If you have capturable continuations, either from your host language
or in your Little Language, then this is easy. If you don't, then you
have to CPS-convert the program by hand, which is much more
complicated (at least I don't know any tricks to make it really simple).

There's also a tempting way to "cheat": buffer up all the data, and
each time you get some more try a whole READ from scratch -- if it
fails, wait until some more data arrives and then try again. The only
trouble is that it takes O(n^2) time on the number of retries.


We've just gone through this whole rigmarole while writing an ILISP
clone based on a Hemlock-style TCP communication channel. We used WIRE
to talk between Emacs and CMUCL, and the WIRE protocol has the same
characteristics of S-expressions described above.

The initial Emacs Lisp implementation used blocking I/O and was quite
simple. But then we didn't like blocking Emacs while waiting for I/O,
so next we did a non-blocking version using heavy macrology to capture
continuations. This works fine, but it takes a lot of pretty heavy
code and, as it turned out, a fair bit of CPU.

Finally (?), we exchanged WIRE for a simple protocol that writes a
fixed-size length header followed by an S-expression as a string. This
is much simpler. You only need one state, represented as an array of
available bytes (an Emacs buffer). Each time some new data arrives you
add it to the array and check if you have a whole message.  If so you
extract the message and READ it, then continue. It's efficient since
the length header makes the "do I have a whole message?" test fast.

So for a protocol like WIRE you really want continuations, but if you
can tweak the protocol a little bit by adding framing information then
you're just fine without them. Quite an "if" though :-)

> After learning SmallTalk, I wrote a Lisp server using ST-style
> message passing for network I/O.  Personally, I found
> message-passing to be a wonderful interface to nonblocking I/O.

I'd enjoy reading any code you'd care to post!

Cheers,
Luke