http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54352
Bug #: 54352
Summary: relaxed data race rules for ~condition_variable_any
Classification: Unclassified
Product: gcc
Version: 4.8.0
Status: UNCONFIRMED
Keywords: ABI
Severity: normal
Priority: P3
Component: libstdc++
AssignedTo: [email protected]
ReportedBy: [email protected]
(related to PR 54185, but for condition_variable_any)
As Howard pointed out in c++std-lib-32966 this should work:
#include <list>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>
#include <chrono>
#include <cassert>
#include <algorithm>
template <class T>
class locked_list
{
std::mutex mut_;
std::list<T> list_;
public:
typedef typename std::list<T>::iterator iterator;
typedef typename T::key key;
template <class ...Args>
void emplace_back(Args&& ...args)
{list_.emplace_back(std::forward<Args>(args)...);}
iterator find(const key& k)
{
std::unique_lock<std::mutex> lk(mut_);
while (true)
{
iterator ep = std::find(list_.begin(), list_.end(), k);
if (ep == list_.end())
return ep;
if (!ep->busy())
{
ep->set_busy();
return ep;
}
ep->wait(lk);
}
}
void erase(iterator i)
{
std::lock_guard<std::mutex> _(mut_);
assert(i->busy());
i->notify_all();
list_.erase(i);
}
iterator end() {return list_.end();}
};
template <class Key>
class elt
{
Key key_;
std::condition_variable_any notbusy_;
bool busy_;
public:
typedef Key key;
explicit elt(const Key& k) : key_(k), busy_(false) {}
bool busy() const {return busy_;}
void set_busy() {busy_ = true;}
void unset_busy() {busy_ = false;}
template <class Lock>
void wait(Lock& lk) {notbusy_.wait(lk);}
void notify_all() {notbusy_.notify_all();}
bool operator==(const Key& k) const {return key_ == k;}
};
void
f1(locked_list<elt<int>>& list)
{
auto i = list.find(1);
assert(i != list.end());
std::this_thread::sleep_for(std::chrono::milliseconds(500));
list.erase(i);
}
void
f2(locked_list<elt<int>>& list)
{
auto i = list.find(1);
assert(i == list.end());
}
int main()
{
locked_list<elt<int>> list;
list.emplace_back(1);
std::thread t1 = std::thread(f1, std::ref(list));
std::this_thread::sleep_for(std::chrono::milliseconds(250));
std::thread t2 = std::thread(f2, std::ref(list));
t1.join();
t2.join();
}
This test doesn't actually crash with libstdc++, but valgrind shows it's
faulty.
Fixing this involves replacing std::condition_variable_any::_M_mutex with a
std::shared_ptr<std::mutex>