:wave: Hi all, Josh pointed me to this thread. I'm the author of that blog post 
he linked to.

Sam Tobin-Hochstadt wrote:
> 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:

I believe this has a bug – if the 'connect' call is killed, then the threads it 
spawned will "leak":

(define connect-thread (thread (lambda () (connect ...))))
(kill-thread connect-thread)
;; Threads may still be running in the background here

Of course you could also accidentally leak threads via a regular bug, where you 
just forget to wait for the child threads on some exit path. I don't see any 
bugs like that in this particular code, but in general, hey, stuff happens, 
no-one's perfect, so we need to be able to cope with thread leaks.

And Racket has much better tools to handle this kind of situation than most 
systems, via "custodians". Unfortunately I'm not very familiar with Racket 
myself; I just know about these from Flatt & Findler's excellent paper on 
kill-safe abstractions :-). So I might have some details wrong here, but I 
think the caller could prevent this issue by doing something like:

;; Encapsulate the 'connect' call and all threads it spawns inside a custodian
(define connect-custodian (make-custodian))
(define connect-thread
  (parametrize ([current-custodian connect-custodian])
               (thread (lambda () (connect ...)))))
;; Kill that custodian, instead of just the thread
(custodian-shutdown-all connect-custodian)
;; Now all the child threads have been killed too

But, to do this, the caller has to know that 'connect' spawns threads. The 
threads were supposed to be an internal implementation detail, but now they've 
leaked out and become part of our function's publicly visible semantics. We 
could fix this by making a custodian inside 'connect', but: why does our 
language allow us to express this bug in the first place?

The core idea of the "structured programming" movement in the 60s/70s was that 
functions should be opaque abstractions that encapsulate control flow. 
Therefore, operators that can break the function abstraction boundary like 
cross-function 'goto' should be eliminated from our vocabulary, and replaced by 
operators like 'if' and 'loop' that respect functional abstraction. The core 
idea of "structured concurrency" is that, well, concurrent control flow is a 
type of control flow. Therefore, we should also get rid of operators like 
'thread' that can cause control flow to accidentally leak outside the boundary 
of a function, and replace them with new concurrency operators that respect 
functional abstraction. That way when we call 'connect' we can tell that it 
doesn't leak threads by just looking at the signature, without having to peek 
inside and read the body. And this bug becomes inexpressible.

Concurrent ML's 'select' is a "structured" operator in this sense, and 
Concurrent ML is super cool as far as it goes. But unfortunately, you can't use 
Concurrent ML events as your sole form of concurrent abstraction, because any 
given event can only have at most one side-effect (roughly speaking). So you 
can't, like, have your entire program be one big event; you need something like 
'thread' too. And the blog post proposes a specific structured replacement for 
the 'thread' operator, that's sort of fork/join-ish. But the overall program 
isn't about fork/join; it's about respecting functional abstraction.

-n

-- 
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 racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/1e85727f-00be-482e-a6e2-58f2f1a9c328%40googlegroups.com.

Reply via email to