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

Reply via email to