public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug libstdc++/100334] New: atomic<T>::notify_one() sometimes wakes wrong thread
@ 2021-04-29 13:22 m.cencora at gmail dot com
  2021-04-29 13:49 ` [Bug libstdc++/100334] " m.cencora at gmail dot com
                   ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: m.cencora at gmail dot com @ 2021-04-29 13:22 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100334

            Bug ID: 100334
           Summary: atomic<T>::notify_one() sometimes wakes wrong thread
           Product: gcc
           Version: 11.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: m.cencora at gmail dot com
  Target Milestone: ---

If waiter pool implementation is used in std::atomic<T>::wait/notify for given
T, then notify_one must underneath call notify_all to make sure that proper
thread is awaken.
I.e. if multiple threads call atomic<T>::wait() on different atomic<T>
instances, but all of them share same waiter, then notify_one on only one of
atomics will possibly wake the wrong thread.
This can lead to program hangs, deadlocks, etc.

Following test app reproduces the bug:
g++-11 -std=c++20 -lpthread

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

template <typename T>
struct atomics_sharing_same_waiter
{
   std::unique_ptr<std::atomic<T>> a[4];
};

unsigned get_waiter_key(void * ptr)
{
   return std::_Hash_impl::hash(ptr) & 0xf;
}

template <typename T>
atomics_sharing_same_waiter<T> create_atomics()
{
   std::vector<std::unique_ptr<std::atomic<T>>> non_matching_atomics;

   atomics_sharing_same_waiter<T> atomics;
   atomics.a[0] = std::make_unique<std::atomic<T>>(0);

   auto key = get_waiter_key(atomics.a[0].get());
   for (auto i = 1u; i < 4u; ++i)
   {
      while (true)
      {
         auto atom = std::make_unique<std::atomic<T>>(0);
         if (get_waiter_key(atom.get()) == key)
         {
            atomics.a[i] = std::move(atom);
            break;
         }
         else
         {
            non_matching_atomics.push_back(std::move(atom));
         }
      }
   }

   return atomics;
}


int main()
{
    // all atomic share the same waiter
    auto atomics = create_atomics<char>();

    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() allows this test to
pass

    verify(std::future_status::timeout ==
fut0.wait_for(std::chrono::milliseconds{100}));
    verify(std::future_status::timeout ==
fut1.wait_for(std::chrono::milliseconds{100}));
    verify(std::future_status::ready ==
fut2.wait_for(std::chrono::milliseconds{100}));
    verify(std::future_status::timeout ==
fut3.wait_for(std::chrono::milliseconds{100}));

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2021-05-17 22:31 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-29 13:22 [Bug libstdc++/100334] New: atomic<T>::notify_one() sometimes wakes wrong thread m.cencora at gmail dot com
2021-04-29 13:49 ` [Bug libstdc++/100334] " m.cencora at gmail dot com
2021-04-29 14:33 ` m.cencora at gmail dot com
2021-04-29 14:42 ` m.cencora at gmail dot com
2021-04-29 16:28 ` rodgertq at gcc dot gnu.org
2021-04-29 22:28 ` m.cencora at gmail dot com
2021-05-01 20:03 ` rodgertq at gcc dot gnu.org
2021-05-01 21:49 ` rodgertq at gcc dot gnu.org
2021-05-03 16:42 ` rodgertq at gcc dot gnu.org
2021-05-03 22:56 ` rodgertq at gcc dot gnu.org
2021-05-04  1:14 ` rodgertq at gcc dot gnu.org
2021-05-04  1:21 ` rodgertq at gcc dot gnu.org
2021-05-17 20:20 ` rodgertq at gcc dot gnu.org
2021-05-17 22:31 ` redi at gcc dot gnu.org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).