Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

longjmp-like coroutine support #19

Open
etorth opened this issue Nov 1, 2020 · 1 comment
Open

longjmp-like coroutine support #19

etorth opened this issue Nov 1, 2020 · 1 comment

Comments

@etorth
Copy link

etorth commented Nov 1, 2020

Hi Andreasbuhr,

Is it possible to add following coroutine model to your repo?
it works like C-stype longjmp function, to suspend/resume through multi-nested-coroutines.
I am not sure if this is a good coroutine model.

This is very useful for game coding when at every round the AI get update a little then jumps back to the main message loop.
The following code is what I got from stackoverflow:

https://stackoverflow.com/questions/61696746/supsending-thrugh-multiple-nested-coroutines

to compile:

g++-10 main.cpp -std=c++20 -fcoroutines -g -fsanitize=address -Wall
#include <cstdio>
#include <coroutine>
#include <optional>

namespace
{

template <typename T>
struct task 
{
    struct task_promise;

    using promise_type = task_promise;
    using handle_type = std::coroutine_handle<promise_type>;

    mutable handle_type m_handle;

    task(handle_type handle)
        : m_handle(handle) 
    {

    }

    task(task&& other) noexcept
        : m_handle(other.m_handle)
    {
        other.m_handle = nullptr;
    }

    bool await_ready()
    {
        return false;
    }

    bool await_suspend(std::coroutine_handle<> handle)
    {
        return true;
    }

    bool await_suspend(std::coroutine_handle<promise_type> handle)
    {
        handle.promise().m_inner_handler = m_handle;
        m_handle.promise().m_outer_handler = handle;
        return true;
    }

    auto await_resume()
    {
        return *m_handle.promise().m_value;
    }

    //manualy wait for finish
    bool one_step()
    {
        auto curr = m_handle;
        while (curr)
        {
            if (!curr.promise().m_inner_handler)
            {
                while (!curr.done())
                {
                    curr.resume();
                    if (!curr.done())
                    {
                        return true;
                    }
                    if (curr.promise().m_outer_handler)
                    {
                        curr = curr.promise().m_outer_handler;
                        curr.promise().m_inner_handler = nullptr;
                    }
                    else
                    {
                        return false;
                    }
                }
                break;
            }
            curr = curr.promise().m_inner_handler;
        }
        return !curr.done();
    }

    ~task()
    {
        if (m_handle)
            m_handle.destroy();
    }

    struct task_promise 
    {
        std::optional<T>    m_value {};
        std::coroutine_handle<promise_type> m_inner_handler {};
        std::coroutine_handle<promise_type> m_outer_handler {};

        auto value()
        {
            return m_value;
        }

        auto initial_suspend()
        {
            return std::suspend_never{};
        }

        auto final_suspend()
        {
            return std::suspend_always{};
        }

        auto return_value(T t)
        {
            m_value = t;
            return std::suspend_always{};
        }

        task<T> get_return_object()
        {
            return {handle_type::from_promise(*this)};
        }

        void unhandled_exception()
        {
            std::terminate();
        }

        void rethrow_if_unhandled_exception()
        {

        }
    };

};

task<int> suspend_one()
{
    std::printf("suspend_one \\\n");
    co_await std::suspend_always();
    std::printf("suspend_one /\n");
    co_return 1;
}
task<int> suspend_two()
{
    auto a = co_await suspend_one();
    auto b = co_await suspend_one();
    co_return a + b;
}

task<int> suspend_five()
{
    auto a = co_await suspend_two();
    auto b = co_await suspend_two();
    co_return 1 + a + b;
}

task<int> run()
{
    std::printf("run\n");
    auto a = co_await suspend_five();
    auto b = co_await suspend_five();
    auto c = co_await suspend_five();
    co_return 5 + a + b + c;
}

}

int main()
{
    std::printf( "main in\n");
    auto r = run();
    std::printf( "main -> while\n");
    while (r.one_step()){  std::printf("              while loop\n"); }

    std::printf( "main return\n");
    return r.await_resume();
}
@andreasbuhr
Copy link
Owner

Hi @etorth , thanks a lot for your proposal. At the moment we are quite busy stabilizing cppcoro and top priority is adding Linux support at the moment, so please be patient before we can add new functionality.

Some thoughts:

  1. What does "rethrow_if_unhandled_exception" do?
  2. Is it correct that "return_value" returns a std::suspend_always{} ?
  3. Is it correct that "await_suspend(std::coroutine_handle<> handle)" does not exchange the coroutine handles?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants