The case for wait_any()... and other nursery utils

The proprietary codebase I work on is about 50k lines now, heavily using Trio. How many times do you want to code a nursery block in your lifetime? The nursery API is actually fairly low level, and it turns out in most cases we can use simpler abstractions.

Our most popular utility in this area is wait_any(func1, func2, ...), which just runs the given functions concurrently in a new nursery, and cancels the nursery when any function completes. wait_any() is easy to understand, not prone to incorrect use, and makes code more readable. A common use is to run one or more processing tasks alongside a monitor which has the option to abort them.

Another is wait_all(func1, func2, ...). It’s not as popular, and users may get confused if one of the functions is Event.wait() and the event is recycled via clear().

Here are use counts of these vs. open_nursery():

191 wait_any()
137 open_nursery()
 58 wait_all()

We also have some async generator utilities. periodic(duration) is wildly popular among those who can’t be bothered with event-driven programming. Another is the sync() method pattern, where one task can have its iteration triggered by the end of another task’s iteration (which itself typically uses periodic()).

385 periodic()
 86 obj.sync()

and the winner is…

I think at least wait_any() should be considered for trio proper. It would spare countless manual nursery blocks.

2 Likes

There’s now an open issue for discussing adding wait_any to trio: