[plt-scheme] using continuations for an async protocol

From: Abdulaziz Ghuloum (aghuloum at cs.indiana.edu)
Date: Tue Nov 11 05:20:02 EST 2003

Daniel Silva wrote:

>  ;; 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

I am assuming dispatch sends the xexpr to a function that handles it.  
That function may call update either directly or indirectly (by calling 
iq-set).  I can see some trouble if that happens given that (dispatch 
xexpr) is not a tail call.  You may see it more clearly if we write 
update as

(define (update client)
  (let ([xexpr (jread client)]) ;; read incoming message
      (dispatch xexpr) ;; dispatch untagged messages
      (update client)))) ;; rinse, repeat

;;; inlining dispatch may make update look like this:
(define (update client)
  (let ([xexpr (jread client)]) ;; read incoming message
      (case-xexpr xexpr
        [(welcome) (begin do-some-stuff-that-result-in (update client))]
        [(join)    (begin do-some-other-stuff (update client))]
      (update client))))

;;; and possibly this:
(define (update client)
  (let ([xexpr (jread client)]) ;; read incoming message
      (case-xexpr xexpr
        [(welcome) (begin do-some-stuff)]
        [(join)    (begin do-some-other-stuff)]
      (if some-consition (update client))
      (update client))))

;;; or this:
(define (update client)
  (if some-condition (update client))
  (update client))

So, your memory requirements are unbounded.  I would recommend 
formulating the problem as coroutines so that control jumps back and 
forth between your dispatch calls and your update function instead of 
having update continuously running in a a loop.


Posted on the users mailing list.