Description
It is increasingly apparent that unstructured goroutines tend to behave, conceptually, like the "go to" statement which Edgar Dijkstra famously Considered Harmful. A good introduction to the problem is
https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/
Other languages also start to "get it", e.g. Kotlin:
https://medium.com/@elizarov/structured-concurrency-722d765aa952
In my experience, contrasting Python+asyncio ("go") vs. Python+Trio ("structured"), thinking about coroutines in a "structured" way (and, importantly, having runtime support for enforcing said structure) helps avoid a whole class of bugs and is very helpful WRT structuring code in a way that can simplify problem spaces immensely.
As one possible and not-too-disruptive step towards structured concurrency I would like to propose that in Go 2, "go func()" shall return some opaque value which must be assigned to some variable (possibly of built-in type goroutine
). The idea is that at the point where that variable goes out of scope, the Go runtime shall wait until the associated goroutine has terminated. Thus, if required for compatibility, an easy way to get the current behavior would be to assign to a global, or append to a global list.
Other, more disruptive implementations are of course possible; in particular, other languages use the concept of a CoroutineScope (Kotlin) / TaskGroup (Python 3.8) / Nursery (Trio) which all coroutines must be attached to, primarily so that they may be cancelled when another coroutine runs into an error that requires the whole group to end prematurely (or regularly – one interesting example is the Happy Eyeballs algorithm for opening a TCP connection, where the terminating condition is "a coroutine successfully opens a connection"). The obvious problem is that Go so far doesn't have any way to generically signal a goroutine to please terminate itself, but maybe somebody else has an idea how to do that.