Search code examples
c++gccg++coroutine

c++ coroutine, done() function returns different value in a same loop


godbolt: https://godbolt.org/z/6avWcGqKv

The following code, compiled and run with g++ 12, 13, 14, all give the same (wrong?) output.

clang 18, 19 are fine (all "done: 1").

As far as I understand, since the initial_suspend() is suspend_never, after creation, the promise runs immediately to final_suspend, and should return "true" when querying "done()", but one of them does not. Is it my programming error, or compiler error, or some UB, or something?

Program returned: 0
done: 1
done: 1
done: 1
done: 1
done: 1
done: 0  <-- ???
done: 1
done: 1
done: 1
done: 1

code:

#include <coroutine>
#include <iostream>
#include <vector>

struct Task {
  struct promise_type {
    Task get_return_object() {
      return {.h = std::coroutine_handle<>::from_address(this)};
    }
    std::suspend_never initial_suspend() noexcept { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    void unhandled_exception() {}
    void return_void() {}
  };
  std::coroutine_handle<> h;
};

Task coro() { co_return; }

int main() {
  std::vector<Task> v;
  for (int i = 0; i < 10; ++i)
    v.push_back(coro());
  for (auto &t : v)
    std::cout << "done: "<< t.h.done() << "\n";

  return 0;
}

Solution

  • Per cppreference, std::coroutine_handle<Promise>::from_address can only be passed the result of another's (compatible) handle std::coroutine_handle<Promise>::address but you are passing the address of promise_type. To reconstruct the handle from that, you need std::coroutine_handle<Promise>::from_promise

    Task get_return_object() {
          return { std::coroutine_handle<promise_type>::from_promise(*this)};
        }
    

    returns all ones with gcc https://godbolt.org/z/9E8aMdK1v

    The original code compiled with clang and -fsanitize=memory complained about:

    ==1==WARNING: MemorySanitizer: use-of-uninitialized-value
        #0 0x55555561f462 in std::ostream::operator<<(bool) /opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:183:16
        #1 0x55555561f462 in main /app/example.cpp:25:26
        #2 0x7ffff7829d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
        #3 0x7ffff7829e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
        #4 0x555555586324 in _start (/app/output.s+0x32324)
    
    SUMMARY: MemorySanitizer: use-of-uninitialized-value /app/example.cpp:25:26 in main
    Exiting