C++20 coroutines symmetric transfer

122 Views Asked by At

I am studying about coroutines and symmetric transfer. Based on https://itnext.io/c-20-practical-coroutines-79202872ebba, I am trying to create a simple example where a coroutine bar waiting coroutine foo to return before returns its result. However the body of function bar is never called. Below is my try

#include <iostream>
#include <coroutine>



#include <coroutine>
#include <exception>
#include <utility>



// Chainable task for awaitable coroutines.
struct Task {
  struct Promise {
    using handle_t = std::coroutine_handle<Promise>;
    Task get_return_object() {
      return Task{handle_t::from_promise(*this)};
    }
      std::suspend_always initial_suspend() noexcept {
          std::cout << "initial suspend_always" << std::endl;
          return {};
      }
      std::suspend_always final_suspend() noexcept {
          std::cout << "final suspend_always" << std::endl;
          return {};
      }
      void unhandled_exception() {
          std::cout << "exception" << std::endl;
          std::terminate();
      }
    void return_value(int result) { result_ = result; }
    //void return_void(void){}

  private:
    std::coroutine_handle<> continuation = std::noop_coroutine();
    int result_;
    friend struct Task;
  };

  using promise_type = Promise;
  explicit Task(promise_type::handle_t handle) : handle_(handle) {}
  Task(const Task &) = delete;
  Task(Task &&task)
      : handle_(std::exchange(task.handle_, nullptr)) {}
  Task &operator=(const Task &) = delete;
  Task &operator=(Task &&task) {
    handle_ = std::exchange(task.handle_, nullptr);
    return *this;
  }
  ~Task() {
    if (handle_)
      handle_.destroy();
  }

  bool await_ready() noexcept { return false; }
  std::coroutine_handle<> await_suspend(std::coroutine_handle<> caller) {
      std::cout << "await_suspend" << std::endl;
      handle_.promise().continuation = caller;
      std::cout << "suspend" << std::endl;
      return std::noop_coroutine();
  }
  auto  await_resume() { 
      std::cout << "resume" << std::endl;
      return handle_.promise().result_; }

private:
  promise_type::handle_t handle_;
};



Task foo(){
    std::cout << "foo" << std::endl;
    co_return 2;
}
Task bar(){
    std::cout << "bar" << std::endl;
    auto result = co_await foo();
    std::cout << "return from foo" << std::endl;
    co_return result;
}

int main(int argc, const char * argv[]) {
    Task c = bar();
    std::cout << "return from bar" << std::endl;
    int i = 0;
     c.await_resume();
    //c.await_resume();
    while(1){}
    std::cout << "Hello, World!\n" <<std::endl;
    return 0;
}

Code under https://godbolt.org/z/evoGG4d43 Any help to this?

1

There are 1 best solutions below

0
Brian Bi On

When you call bar, it hits its initial suspend point, as you can see from the output. Then, you seem to think that calling c.await_resume() resumes the coroutine, but in fact it does no such thing; c.await_resume() is simply an ordinary function call, where the fact that the function is named await_resume does not magically cause it to resume the coroutine. (Rather, await_resume is called automatically by a co_await expression after the calling coroutine is resumed.)

To resume the suspended invocation of bar from another coroutine, you would normally use co_await c. But since main isn't a coroutine, you instead need to find some way of getting the coroutine handle that refers to that coroutine frame (which is not possible in your current implementation because handle_ is private) or call some public member of Task that tells it to resume itself (which also doesn't exist in your current implementation). Or implement a different coroutine that co_awaits on c for you, and make that coroutine run to completion.

For example, in your Godbolt snippet, if we make handle_ public and then call c.handle_.resume() twice instead of c.await_resume(), I think you get the behaviour that you're looking for.