| // See PR94359, we will need either a general solution to this, or at least |
| // some hook for targets to opt in, for now the test will work on targets that |
| // can do the tailcall (which would normally be available for O2+) |
| |
| // { dg-do run { target { i?86-*-linux-gnu x86_64-*-linux-gnu *-*-darwin* } } } |
| // { dg-additional-options "-O2" } |
| |
| #if __has_include(<coroutine>) |
| |
| #include <coroutine> |
| namespace coro = std; |
| |
| #elif __has_include(<experimental/coroutine>) |
| |
| #include <experimental/coroutine> |
| namespace coro = std::experimental; |
| |
| #endif |
| |
| #if __clang__ |
| # include <utility> |
| #endif |
| |
| #include <chrono> |
| #include <thread> |
| #include <cstdio> |
| |
| template <typename T> |
| struct Loopy { |
| struct promise_type; |
| using handle_type = coro::coroutine_handle<Loopy::promise_type>; |
| handle_type handle; |
| |
| struct promise_type { |
| // Cause the Loopy object to be created from the handle. |
| auto get_return_object () { |
| return handle_type::from_promise (*this); |
| } |
| |
| coro::suspend_never yield_value(T value) { |
| //fprintf (stderr, "%s yields %d\n", me, value); |
| current_value = value; |
| return coro::suspend_never{}; |
| } |
| |
| coro::suspend_always initial_suspend() { return {}; } |
| coro::suspend_always final_suspend() noexcept { return {}; } |
| |
| void unhandled_exception() { /*std::terminate();*/ } |
| void return_void() {} |
| |
| void set_peer (handle_type _p) { peer = _p; } |
| void set_me (const char *m) { me = m; } |
| |
| T get_value () {return current_value;} |
| T current_value; |
| handle_type peer; |
| const char *me; |
| }; |
| |
| Loopy () : handle(0) {} |
| Loopy (handle_type _handle) : handle(_handle) {} |
| Loopy (const Loopy &) = delete; |
| Loopy (Loopy &&s) : handle(s.handle) { s.handle = nullptr; } |
| ~Loopy() { if ( handle ) handle.destroy(); } |
| |
| struct loopy_awaiter { |
| handle_type p; |
| bool await_ready () { return false; } |
| /* continue the peer. */ |
| handle_type await_suspend (handle_type h) { |
| p = h.promise().peer; |
| return p; |
| } |
| T await_resume () { return p.promise().get_value (); } |
| }; |
| }; |
| |
| Loopy<int> |
| pingpong (const char *id) |
| { |
| int v = 0; |
| Loopy<int>::loopy_awaiter aw{}; |
| for (;;) |
| { |
| co_yield (v+1); |
| //std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
| if (v > 10'000'000) |
| break; |
| v = co_await aw; |
| //fprintf (stderr, "%s = %d\n", id, v); |
| } |
| //fprintf (stderr, "%s = %d\n", id, v); |
| } |
| |
| int main () |
| { |
| // identify for fun.. |
| const char *ping_id = "ping"; |
| const char *pong_id = "pong"; |
| auto ping = pingpong (ping_id); // created suspended. |
| auto pong = pingpong (pong_id); |
| |
| // point them at each other... |
| ping.handle.promise().set_peer (pong.handle); |
| pong.handle.promise().set_peer (ping.handle); |
| |
| ping.handle.promise().set_me (ping_id); |
| pong.handle.promise().set_me (pong_id); |
| |
| // and start them ... |
| ping.handle.resume (); |
| pong.handle.resume (); |
| |
| } |
| |