Bug 54352 - relaxed data race rules for ~condition_variable_any
Summary: relaxed data race rules for ~condition_variable_any
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.8.0
: P3 normal
Target Milestone: 4.9.0
Assignee: Not yet assigned to anyone
URL:
Keywords: ABI
Depends on:
Blocks:
 
Reported: 2012-08-22 20:28 UTC by Jonathan Wakely
Modified: 2013-07-21 19:23 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
patch against 4.8 trunk (1.18 KB, patch)
2012-08-22 20:43 UTC, Jonathan Wakely
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan Wakely 2012-08-22 20:28:53 UTC
(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>
Comment 1 Jonathan Wakely 2012-08-22 20:43:34 UTC
Created attachment 28066 [details]
patch against 4.8 trunk
Comment 2 Jonathan Wakely 2012-08-22 20:58:10 UTC
(In reply to comment #1)
> Created attachment 28066 [details]
> patch against 4.8 trunk

That patch needs some additional exports:

    _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policy*;
    _ZNKSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policy*;
    _ZT[ISV]St16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE;
Comment 3 Jonathan Wakely 2013-07-21 19:23:08 UTC
Fixed for 4.9