The Racket community, and even more so the design of Racket
concurrency APIs, is very strongly influenced by the academic side of
Racket. As far as I can tell, structured concurrency is fairly close
to what is traditionally called the fork/join model. Concurrency in
Racket is usually structured in a somewhat different way, around
first-class events and channels. First-class events were originally
created in Concurrent ML, and the basic idea is that you can package
up things that you might think of as concurrency operations (such as
`select` in Go) and turn them into _values_ which you can then further
synchronize on.

Here's how I would write the Happy Eyeballs program in Racket:

#lang racket/base

(define DELAY 300)
(require racket/tcp racket/match racket/set)

(define threads (mutable-set))

(define (connect hosts)
  (let loop ([hosts hosts] [chans null])
  (cond [(null? hosts)
         #f]
        [else
         (define ch (make-channel))
         (define t (thread (lambda ()
                             (with-handlers ([exn:fail? (λ _
(channel-put ch #f))])
                               (define-values (in out) (tcp-connect
(car hosts) 80))
                               (channel-put ch (cons in out))))))
         (set-add! threads t)
         (match (apply sync (alarm-evt (+ (current-milliseconds)
DELAY)) ch chans)
           [(cons in out) ;; success
            (for ([t threads]) (kill-thread t))
            (cons in out)]
           [#f ;; error
            ;; thread is dead, don't need to kill it
            ;; don't need to remember this channel
            (loop (cdr hosts) chans)]
           [_ ;; timeout
            ;; ask the next iteration to sync on this channel too
            (loop (cdr hosts) (cons ch chans))])])))

I think that this does the right thing, although it's not actually
using IP addresses and the IP-level operations, but hostnames and TCP
sockets.

One other thing I would note. This is simple, but even simpler would
be to package up `tcp-connect` as an event, the way `tcp-accept-evt`
works. Then you could write something like:

(apply sync (for/list ([(i h) hosts]) (replace-evt (alarm-evt (+
(current-milliseconds) (* DELAY i)))

(lambda _ (tcp-connect-evt h 80)))))

Sam

On Wed, Oct 9, 2019 at 1:43 PM jab <[email protected]> wrote:
>
> So far from this thread, it seems the idea of Structured Concurrency hasn’t 
> yet made it into the Racket world. I’ll be interested to see if it gets 
> adopted in Racket in the future (or at least better understood) as its 
> adoption grows elsewhere.
>
> In the meantime, in case it helps illustrate the idea to anyone else still 
> interested, check out the talk I linked to previously showing an 
> implementation of Happy Eyeballs using structured concurrency. Or just read 
> it directly from the Trio source once you understand what a nursery and a 
> cancel scope are:
> https://github.com/python-trio/trio/blob/master/trio/_highlevel_open_tcp_stream.py
>
> (RFC 6555 Happy Eyeballs is like a Hello World of the problem space that 
> structured concurrency is addressing, so this is an illustrative example.)
>
> If there is some blessed Racket implementation of Happy Eyeballs, it could be 
> useful to compare – for correctness, completeness, obviousness, and elegance.
>
> --
> You received this message because you are subscribed to the Google Groups 
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/racket-users/ed325f57-d7e3-47dc-a2e7-76fc0af1ff50%40googlegroups.com.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/CAK%3DHD%2Baw3vS5k%2BM0qdNdrnazVxbTNkfqWE7-__E1Lx35TsUqjQ%40mail.gmail.com.

Reply via email to