Opened 10 years ago

Closed 10 years ago

#7334 closed Bugs (duplicate)

Fix bug concerning condition_variable_any reported in [c++std-lib-32966]

Reported by: viboes Owned by: Anthony Williams
Milestone: Component: thread
Version: Boost 1.51.0 Severity: Problem
Keywords: Cc:

Description

the following is ok:

  Thread A                    Thread B
   ...                        lk.lock()
   ...                        cv.wait(lk)
  lk.lock()                      ...
  cv.notify_one()                ...
  cv.~condition_variable_any()   ...
  lk.unlock()                    ...
    ...                       finally exits cv.wait  // ok, not a data race

Below is a complete C++11 HelloWorld that is a simple translation of the example in the POSIX pthread_cond_destroy spec. It should run and not crash or assert:

#include <list>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>
#include <chrono>
#include <cassert>

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

If we substitute in boost::condition_variable_any for std::condition_variable_any for the notbusy_ data member of class elt, we get:

Assertion failed: (!pthread_mutex_unlock(m)), function ~interruption_checker, file /Users/hhinnant/Development/boost-dev/boost-trunk/boost/thread/pthread/thread_data.hpp, line 171.
Abort trap: 6

Change History (1)

comment:1 by viboes, 10 years ago

Milestone: To Be Determined
Resolution: duplicate
Status: newclosed

Duplicated of #7319.

Note: See TracTickets for help on using tickets.