https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100334
--- Comment #2 from m.cencora at gmail dot com --- I have adapted the test to gcc trunk, but I am not entirely sure it is correct, because I don't have gcc trunk locally, I was just testing this on wandbox.org The problem is even bigger here, because it seems that besides wrong thread is woken up, also wrong std::atomic<T> instance finishes the wait() call! #include <atomic> #include <future> #include <iostream> #include <source_location> #include <thread> #include <vector> void verify(bool cond, std::source_location loc = std::source_location::current()) { if (!cond) { std::cout << "Failed at line " << loc.line() << '\n'; //std::abort(); } } void emptyDeleter(void *) {} template <typename T> struct atomics_sharing_same_waiter { std::atomic<T> tmp[49 * 4] = {}; std::shared_ptr<std::atomic<T>> a[4] = { { &tmp[0], emptyDeleter }, { &tmp[16 * 4], emptyDeleter }, { &tmp[32 * 4], emptyDeleter }, { &tmp[48 * 4], emptyDeleter } }; }; constexpr unsigned key(void * a) { constexpr uintptr_t __ct = 16; return (uintptr_t(a) >> 2) % __ct; } int main() { // all atomic share the same waiter atomics_sharing_same_waiter<char> atomics; for (auto& atom : atomics.a) { atom->store(0); } std::cout << "atom0 " << atomics.a[0].get() << " key " << key(atomics.a[0].get()) << '\n'; std::cout << "atom1 " << atomics.a[1].get() << " key " << key(atomics.a[1].get()) << '\n'; std::cout << "atom2 " << atomics.a[2].get() << " key " << key(atomics.a[2].get()) << '\n'; std::cout << "atom3 " << atomics.a[3].get() << " key " << key(atomics.a[3].get()) << '\n'; verify(&std::__detail::__waiter_pool_base::_S_for(reinterpret_cast<char *>(atomics.a[0].get())) == &std::__detail::__waiter_pool_base::_S_for(reinterpret_cast<char *>(atomics.a[1].get()))); auto fut0 = std::async(std::launch::async, [&] { atomics.a[0]->wait(0); }); auto fut1 = std::async(std::launch::async, [&] { atomics.a[1]->wait(0); }); auto fut2 = std::async(std::launch::async, [&] { atomics.a[2]->wait(0); }); auto fut3 = std::async(std::launch::async, [&] { atomics.a[3]->wait(0); }); // make sure the all threads already await std::this_thread::sleep_for(std::chrono::milliseconds{100}); atomics.a[2]->store(1); atomics.a[2]->notify_one(); // changing to notify_all() doesn't help on gcc trunk verify(std::future_status::timeout == fut0.wait_for(std::chrono::milliseconds{100})); verify(atomics.a[0]->load() == 0); verify(std::future_status::timeout == fut1.wait_for(std::chrono::milliseconds{100})); verify(atomics.a[1]->load() == 0); verify(std::future_status::ready == fut2.wait_for(std::chrono::milliseconds{100})); verify(atomics.a[2]->load() == 1); verify(std::future_status::timeout == fut3.wait_for(std::chrono::milliseconds{100})); verify(atomics.a[3]->load() == 0); atomics.a[0]->store(1); atomics.a[0]->notify_one(); atomics.a[1]->store(1); atomics.a[1]->notify_one(); atomics.a[3]->store(1); atomics.a[3]->notify_one(); }