I have a main task that will continuously spawn other tasks. I don't care for the results of the other tasks, but I do want to make sure all the other tasks have finished before the main task will finish. I don't want to keep around a handle for each task because that will leak too many resources (the main task will run for a long time) and I don't have a good place to clean up the handles.
In my synchronous/thread-based implementation, I've implemented this by spawning each task on a new thread, detaching each thread (by dropping the join handle immediately), and keeping track of the running thread count. I also use a condition variable that the main thread can wait on (once it's done). Just before a secondary thread finishes, it will decrease the count, and if the count is now zero, notify the main thread.
How would I go about implementing the same using tokio/futures? Obviously, I can't use the (blocking) CondVar primitive.
Is that io or cpu bound?
I don’t want to keep around a handle for each task because that will leak too many resources
Why would that leak anything? Once task is done you can dispose any resources it used.
Have a queue of tasks, push new ones there, small set of worker threads can pick up tasks and execute them. How is that implemented (threads or futures) really is not that important.
You could create your own "wrapping"future (a simple struct that impl's the Future trait) that increments a global atomic counter when it is created, and decrements it when the future it wraps completes. Use it like tokio::spawn(Wrapper::new(your_future_here)) .
You could do signalling with a mpsc channel. Clone the tx side into every wrapper. When one of the wrapping futures has decremented the global count to zero, write some item into the channel - nonblocking - then return. Your main task can then when it's done loop and read from the channel, on every item received checking if the global counter is zero. if it is, it's done.