C++ Coroutines promise type

I’ve recently learnt about coroutines, and got excited as I maintain several async libraries, for which to support coroutines for at least the base library -the task scheduler-.

The task scheduler serves bare functions: No parameters , no return value, and I want to keep it as it is while supporting coroutine to co_await for periods, primarily.

Therefore I went to have the promise_type::get_return_object() to return void, as no intention to enforce application/user to switch such tasks to different function signature, and to avoid double managing the tasks.

I’ve initially implemented that, with a compile error preventing me to proceed: error: unable to find the promise type for this coroutine.

Following is the main implementation of the coroutine, and here’s the execution link: https://godbolt.org/z/4hWce9n6P

Am I getting coroutines wrong? What is suggested to do?

Thanks.

class H4Delay {
    uint32_t duration;
    task* owner;
    task* resumer=nullptr;
public: 
    class promise_type {
        task* owner=nullptr;
        friend class H4Delay;
        public:
        void get_return_object() noexcept {}
        std::suspend_never initial_suspend() noexcept { return {}; }
        void return_void() noexcept {}
        void unhandled_exception() noexcept { std::terminate(); }
        struct final_awaiter {
            bool await_ready() noexcept { return false; }
            void await_suspend(std::coroutine_handle<promise_type> h) noexcept {
                auto owner = h.promise().owner;
                if (owner) owner->_destruct();
                task::suspendedTasks.erase(owner);
                // [ ] IF NOT IMMEDIATEREQUEUE: MANAGE REQUEUE AND CHAIN CALLS.
                 
            }
            void await_resume() noexcept {}
        };
        final_awaiter final_suspend() noexcept { return {}; }

    };
    std::coroutine_handle<promise_type> _coro;
    
    H4Delay(uint32_t duration, task* caller=H4::context) : duration(duration), owner(caller) {}
    ~H4Delay() { 
        if (_coro) _coro.destroy();
    }

    bool await_ready() noexcept { return false; }

    void await_suspend(std::coroutine_handle<promise_type> h) noexcept {
        // Schedule the resumer.
        resumer = h4.once(duration, [h]{ h.resume(); });
        _coro = h;
        _coro.promise().owner = owner;
        task::suspendedTasks[owner] = this;
    }

    void await_resume() noexcept { resumer = nullptr; }

    
    void cancel() { ... }

};

10

I’ve skimmed your code and as far as I can tell, you have a misunderstanding of coroutines. There are 2 concepts at play here: a coroutine and an awaitable.

A coroutine is a function that runs as a coroutine, meaning it can suspend and resume its execution at suspension points. For example:

Foo foo() {
    co_await std::suspend_never{};
}

This is necessarily a coroutine, because otherwise you can’t use co_await. Therefore, Foo must be capable of being used as a returnvalue from a coroutine, meaning it must define a suitable promise_type.

What you want is an awaitable. An awaitable is simply a type that can be co_awaited. For this it needs the await_ready, await_suspend and await_resume methods (or an operator co_await whose return type has these methods). No need to define a promise type for H4Delay, if all you want to do with it is co_await H4Delay{...};.

Now you’ll run into the issue, that you can’t just co_await in a regular function. You can only do that in a coroutine. That’s why, in your godbolt, the compiler complains:

In function 'void someF()':
<source>:737:5: error: unable to find the promise type for this coroutine
  737 |     co_await H4Delay(400);

Because you use co_await, someF becomes a coroutine. Meaning void needs to define a suitable promise_type, which it does not.

You can either define your own rudimentary coroutine type (Coro<Result> or whatever), or you can search for libraries that implement this for you. Then you define someF as Coro<void> someF(), and you’ll be able to co_await in it.

Word of warning: implementing a coroutine type yourself isn’t that hard to do initially, but the devil is very much in the details.

  • coroutine state: Language provided thingy that represents the state of a coroutine. Things like a copy of your stack frame get associated with it.

  • coroutine promise object: user or library written object that handles coroutine logic. The language will regularly interact with it in response to coroutine type operations.

  • coroutine handle: A relatively opaque handle from which you can manipulate/interact with the coroutine state, and get your coroutine promise object.

  • C++ coroutine: a function whose code is converted into something that can be suspended and resumed. You can think of it as having local variables in a struct and a bunch of GOTO labels at every possible suspension/resumption location. Created by using any coroutine operation in a function.

  • Awaitable. A type that supports being co_await’d “in the raw” within a coroutine. I’d include types with an operator co_await() (member or free) that return an Awaitable here. Awaitables need to support support:

    bool await_ready();
    ? await_suspend(handle); // (can return void, bool, or a handle)
    ? await_resume(); // (return type of co_await operation).

(Coroutines can modify what co_await means to convert expressions into awaitables)


As a rule, you cannot await something outside of a coroutine. The await API basically lets the computation “reach back” into the containing coroutine and suspend or resume it.

From the outside — from its signature — it isn’t possible to determine if a function is a coroutine or not. To the external caller, you invoke the function and it returns something.

The difference is that a coroutine can set up the suspend/resume machinery. It can even expose access to this machinery within the returned object or other parameters passed to the function. It can also choose not to expose access to this machinery.

But, as the language supports user-defined types of coroutines, some means to map from the function to the coroutine defined coroutine needs to exist.

Typically this is done through the return type. The default coroutine_traits just returns R::promise_type. So if your return type has a promise_type typedef, and you have coroutine operations, it uses that type.

In your case, you are using a coroutine operation (co_await in a function returning void. The compiler is complaining that it doesn’t know how to turn

void someF() {
  printf("on 500, awaiting 400 ms:n");
  co_await H4Delay(400);
  printf("400ms awaited!n");
  return;
}

into a coroutine.

co_await tries to hook into the coroutine machinery of someF and fails.

Specifically, in:

void await_suspend(std::coroutine_handle<promise_type> h)

that h is the coroutine handle of someF, which doesn’t exist.

To fix this we need to turn someF into a coroutine. When you co_await, someF will return immediately. Later on, it will be resumed by the H4Delay class and the tail end of the code will run.

Does the caller of someF deserve need to know when it finished? Then you can return a std::task<void>. If not, you could do this:

struct secret_coroutine {};
template<class R>
struct secret_coroutine_impl {};

template<>
struct secret_coroutine_impl<secret_coroutine> {
  secret_coroutine get_return_object() noexcept { return {}; }
  std::suspend_never initial_suspend() noexcept { return {}; }
  void return_void() noexcept {}
  void unhandled_exception() noexcept { std::terminate(); }
  struct final_awaiter {
    bool await_ready() noexcept { return false; }
    template<class T>
    void await_suspend(std::coroutine_handle<T> h) noexcept {}
    void await_resume() noexcept {}
  };
  final_awaiter final_suspend() noexcept { return {}; }
};

template<>
struct secret_coroutine_impl<void> {
  void get_return_object() noexcept {}
  std::suspend_never initial_suspend() noexcept { return {}; }
  void return_void() noexcept {}
  void unhandled_exception() noexcept { std::terminate(); }
  struct final_awaiter {
    bool await_ready() noexcept { return false; }
    template<class T>
    void await_suspend(std::coroutine_handle<T> h) noexcept {}
    void await_resume() noexcept {}
  };
  final_awaiter final_suspend() noexcept { return {}; }
};

note that this doesn’t do the proper resource management and lifetime; I just made it compile.

When used in your code:

secret_coroutine someF() {
    printf("on 500, awaiting 400 ms:n");
    co_await H4Delay(400);
    printf("400ms awaited!n");
    co_return;
}

it fails because your co_await code for H4Delay assumes that the coroutine it is suspended in is H4Delays specific coroutine, both by setting its type and by assuming it has a .owner field and similar.

So you’d have to augment the someF coroutine to have that data.

Once you are done, your H4Delay no longer has to have the coroutine support (just the awaitable support) I think, unless you want to chain co_awaits.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật