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?
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.