Joseph Garvin
Joseph Garvin

Reputation: 21974

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).

Upvotes: 3

Views: 97

Answers (1)

PiotrNycz
PiotrNycz

Reputation: 24422

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.

Upvotes: 4

Related Questions