Waiting for all coroutines with when_all

105 views Asked by At
    #include <seastar/core/app-template.hh>
    #include <seastar/core/coroutine.hh>
    #include <seastar/core/sleep.hh>
    #include <seastar/core/when_all.hh>

/*Here I want all the futures to execute*/
    seastar::future<> all_futures()
    {
        seastar::sleep(2s).then([]{std::cout << "2s\n";});
        seastar::sleep(1s).then([]{std::cout << "1s\n";});
        return seastar::sleep(3s).then([]{std::cout << "3s\n";});//CHANGE HERE TO VALUE < 2s
    }
    
    seastar::future<int> get_int()
    {
        std::cout << "Entering winter sleep\n";
        return seastar::sleep(5s).then([]{return 2;}); //CHANGE HERE TO VALUE < 3s
    }
    
    seastar::future<> f_sleeping() {
        std::cout << "Sleeping... " << std::flush;
        using namespace std::chrono_literals;
//Here I want to wait for all the futures and only exit after all futures finished
        return when_all(all_futures(),get_int().then([](const int val){std::cout << "got: " << val << '\n'; })).discard_result();
    }
    
    int main(int argc, char** argv) {
        app_template app;
    
        return app.run(argc, argv, [&app] {
            
            return f_sleeping();
        });
    }

When running the code as it is everything seems to work as intended. But try to change the waiting time in lines marked //CHANGE HERE TO VALUE and suddenly not all futures are being waited for!!! So the question is, how to wait for x number of futures? Another question is, is this a bug in that library or is it simply C++ which at this stage becomes really old tech that cannot catch such problems during the compilation? Yet, another question is, why does it compile at all. I've looked into the declaration of when_all and apparently it accepts pair of iterators. I do not in the code above, give any iter to that function and yet it compiles and runs. As is often the case with C++, not correctly, without giving as little as one single warning...

EDIT| The error:

error: could not convert ‘seastar::when_all(FutOrFuncs&& ...) [with FutOrFuncs = {seastar::future, seastar::future, seastar::future}]((* & std::moveseastar::future<&>(one)), (* & std::moveseastar::future<&>(three)))’ from ‘futurestd::tuple<seastar::future<void, seastar::future, seastar::future >>’ to ‘future’ 172 | return when_all(std::move(two), std::move(one), std::move(three)); | ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | futurestd::tuple<seastar::future<void, seastar::future, seastar::future >>

1

There are 1 answers

5
Caleth On BEST ANSWER

You are only returning one future out of all_futures, the first two are discarded. Having done that, there's no way of waiting for them, so if nothing else waits as long as they do, they might outlive main.

If you do want to wait for them, you need to have something wait for them.

seastar::future<> all_futures()
{
    auto two = seastar::sleep(2s).then([]{std::cout << "2s\n";});
    auto one = seastar::sleep(1s).then([]{std::cout << "1s\n";});
    auto three = seastar::sleep(3s).then([]{std::cout << "3s\n";});
    return when_all(std::move(two), std::move(one), std::move(three)).discard_result();
}