c++async-awaitcoroutinec++23c++-coroutine

Is it possible to create a C++ coroutine task/promise type that makes it possible to get_running_coroutine()?


AFAICT there is no builtin way in C++20 or C++23 to globally query the promise or handle for the coroutine running on the current thread. However any of the functions that let you return a coroutine to trigger running it, e.g. initial_suspend, final_suspend, await_suspend etc would know what coroutine is about to run and could set a thread_local to the coroutine_handle they are returning before returning. Is that sufficient or are there more cases to worry about? This is assuming that in typical usage people are doing co_await on their task types and not manually running their own loop calling handle.resume() over and over (and if there was an executor/execution-agent switching between stored top-level coroutines that it would cooperate with the scheme).


Solution

  • I had the same problem some time ago - I tried various approaches - also the "co_await"-way mentioned in comments - but the best with performance in mind is to use "co_yield". Just create some get_handle empty class to tag-dispatch to your yield_value method. And from this yield_value(get_handle) method return awaitable type derived from std::suspend_never - so no "suspend" - and overload await_resume to return handle to the caller from the actual coroutine body.

    #include <coroutine>
    
    struct get_handle{}; // just tag type
    struct coro
    {
        struct promise_type;
        using handle_type = std::coroutine_handle<promise_type>;
        struct promise_type
        {
            int some_value = 42; // for presentation purposes
    
            auto initial_suspend() noexcept { return std::suspend_never{}; }
            auto final_suspend() noexcept { return std::suspend_never{}; }
            coro get_return_object() { return {}; }
            void unhandled_exception() {}
            void return_void() {}
    
            auto yield_value(get_handle)
            {
                struct return_handle : std::suspend_never
                {
                    handle_type handle;
    
                    handle_type await_resume() const
                    {
                        return handle;
                    }
                };
    
                return return_handle{.handle = handle_type::from_promise(*this)};
            }
    
        };
    };
    

    and proof it works:

    coro foo()
    {
        auto handle = co_yield get_handle{};
        std::cout << handle.promise().some_value;
    }
    
    
    
    int main() {
    
        foo();
    }
    
    

    But maybe most important - you can use "co_yield" (and co_await with await_transform too) to communicate with your promise object - so, actually, in most cases you shall not need to get full handle - so if you can redesign your implementation in the way to not need this handle inside coroutine body - that would be the best probably.