Search code examples
c++c++-coroutine

co_await -- unexpected result in MSVC


I have a rather straightforward Event design, which defines a trigger() function and operator co_await() and keeps a set of awaitables, which it resumes when triggered. A demo is here.

struct Event{
    void trigger();
    auto operator co_await();
private:
    class Awaitable;
    std::set<Awaitable*> awaitables;
    bool triggered;
};

struct Event::Awaitable {
    explicit Awaitable(Event* e) : evt(e) {}
    bool await_ready() const noexcept { return evt->triggered; }
    void await_suspend(std::coroutine_handle<> h) {
        std::cout << "await_suspend\n";
        awaiter = h;
        evt->awaitables.insert(this);
    }
    void await_resume() {std::cout << "await_resume\n"; }
    bool await_must_resume() const noexcept { return false; }
private:
    friend Event;
    Event* evt;
    std::coroutine_handle<> awaiter;
    public:
};

auto Event::operator co_await() { return Awaitable{this}; }

void Event::trigger() {
    triggered = true;
    std::cout << "resuming " << awaitables.size() << " awaitable(s)\n";
    for(auto&& a : std::move(awaitables))
        a->awaiter.resume();
}

Compiling it in gcc and running shows that await_suspend is called, which is what I would expect. But MSVC calls await_resume instead, which I can't explain. Is that a bug in compiler or UB in design?


Solution

  • using an initialized variable is UB.

    struct Event{
        void trigger();
        auto operator co_await();
    private:
        class Awaitable;
        std::set<Awaitable*> awaitables;
        bool triggered; // uninititalized
    };
    

    fixing it to bool triggered = false; or bool triggered{}; fixed your issue.

    godbolt demo