[plt-scheme] using continuations for an async protocol

From: Daniel Silva (dansilva at lynx.dac.neu.edu)
Date: Tue Nov 11 04:16:50 EST 2003

Hello,

I am writing a jabber client library and have a few questions.

The jabber protocol is asynchronous, so there is no guarantee that an
incoming message is a response to the last sent message.  You may tag an
outgoing message with an ID that is copied in the future response
message.

One small example is a 'login' procedure: (note: jabber is xml-based)

  ;; login: JabberClient string string -> void
  (define (login client username password)
    (let ([res (iq-set client
                       `(query ([xmlns ,ns:iq-auth])
                          (username ,username)
                          (password ,password)
                          (resource "DrScheme")))])
      (if (string=? "error" (a-lookup 'type res))
          (printf "Login error.")
          (printf "Login successful."))))

Where ns:iq-auth is an xml namespace string, a-lookup finds an XML
attribute value in an element, res is an x-expression element, and
iq-set is:

  ;; iq-set: JabberClient x-expression -> sxp
  (define (iq-set client query-xexpr)
       ;; generate an id, but not a gensym.. so wrap it in a string
    (let ([id (format "~a" (gensym 'iqset))])
      (let/cc k
        (hash-table-put! *conts* (string->symbol id) k)
           ;; send the tagged query message to the jabber server
        (jwrite client `(iq ([type "set"]
                             [id ,id])
                            ,query-xexpr))
           ;; suspend?
        (update client))))

Where jwrite sends an x-expression to a jabber client, and update is:

  (define (update client)
    (let* ([xexpr (jread client)]  ;; read incoming message
           [id (a-lookup 'id xexpr)])  ;; find its id, if it has one
      (if id
          (let* ([key (string->symbol id)]
                 [k (hash-table-get *conts* key)]) ; find the stored k
            (hash-table-remove! *conts* key) ; clean up
            (k xexpr))
          (begin (dispatch xexpr) ;; dispatch untagged messages
                 (update client)))))  ;; rinse, repeat

Where jread reads an x-expression from a client and dispatch handles
targetless (id-less) messages like notifications about a contact going
away or coming back online.


Am I going about this [async. protocols] the right way?  Using this
implementation, any server notifications that come in between the time I
send my login info and when the auth server responds will be handled
correctly (and won't be mistaken for the response).

I could have done something similar to what the Haskell guys did in
WASH/CGI and passed a lambda to iq-set in the login procedure instead of
using let/cc to get a returned value, but that doesn't feel natural to
me.  Is it the right solution though?

Also, would it make more sense to run "update" in a separate thread? 
Though I don't think it could apply "k" then since it wouldn't own the
continuation.

asking +inf.0 questions,
Daniel



Posted on the users mailing list.