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();
}