Boost logo

Boost :

From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2007-08-24 13:05:58


On Aug 22, 2007, at 11:55 AM, Howard Hinnant wrote:

> Here's checked_condition:

After investigating the use cases for the "unchecked condition", I
realized that my earlier checked_condition that I posted here wasn't
quite right, at least if you considered it a debug tool. To this end
I've rewritten it, and renamed it to condition_debug to emphasize its
role. The previous version was allowing default constructed
conditions to wait on different mutexes at the same time. This
revised condition_debug allows default constructed conditions to only
wait on different mutexes at different times. All simultaneous waits
must be on the same mutex (even for a default constructed condition).

The reason I'm posting it here is that I believe this is a little more
complicated, and has a larger sizeof than we first anticipated (if we
are to check for error cases on the default constructed condition).

In general it adds 3 data members: a mutex, a Mutex*, and an unsigned
int. The mutex is for the case that the lock isn't an exclusive lock,
but a shared lock. Updating the Mutex* and unsigned int must be
protected. There may be a way to do this with atomics, but I haven't
figured it out yet. For efficiency purposes, the condition_debug is
specialized on exclusive mutex types, which eliminates the need for
the external mutex.

#include <mutex>
#include <condition>
#include <type_traits>

template <class Mutex> struct
is_exclusive : public std::false_type {};
template <> struct
is_exclusive<std::mutex> : public std::true_type {};
template <> struct
is_exclusive<std::recursive_mutex> : public std::true_type {};
template <> struct
is_exclusive<std::timed_mutex> : public std::true_type {};
template <> struct
is_exclusive<std::recursive_timed_mutex> : public std::true_type {};

template <class Mutex, bool = is_exclusive<Mutex>::value>
class condition_debug
{
public:
     typedef Mutex mutex_type;
private:
     std::condition<mutex_type> cv_;
     std::mutex guard_ckeck_;
     mutex_type* mut_;
     unsigned wait_count_;
public:

     condition_debug() : mut_(0), wait_count_(0) {}
     explicit condition_debug(mutex_type& m) : mut_(&m),
wait_count_(1) {}

     void notify_one() {cv_.notify_one();}
     void notify_all() {cv_.notify_all();}

     template <class Lock>
         void wait(Lock& lock)
         {
             precheck(lock);
             cv_.wait(lock);
             postcheck();
         }

     template <class Lock, class Predicate>
         void wait(Lock& lock, Predicate pred)
         {
             precheck(lock);
             cv_.wait(lock, std::move(pred));
             postcheck();
         }

     template <class Lock>
         bool timed_wait(Lock& lock, const std::utc_time& abs_time)
         {
             precheck(lock);
             cv_.timed_wait(lock, abs_time);
             postcheck();
         }

     template <class Lock, class Predicate>
         bool timed_wait(Lock& lock, const std::utc_time& abs_time,
Predicate pred)
         {
             precheck(lock);
             cv_.timed_wait(lock, abs_time, std::move(pred));
             postcheck();
         }

private:
     template <class Lock>
     void precheck(const Lock& lock)
     {
         std::scoped_lock<std::mutex> _(guard_ckeck_);
         if (!lock.owns() || (mut_ != 0 && lock.mutex() != mut_))
             throw std::runtime_error("Or whatever error handling
policy you want");
         ++wait_count_;
         mut_ = lock.mutex();
     }

     void postcheck()
     {
         std::scoped_lock<std::mutex> _(guard_ckeck_);
         if (--wait_count_ == 0)
             mut_ = 0;
     }
};

template <class Mutex>
class condition_debug<Mutex, true>
{
public:
     typedef Mutex mutex_type;
private:
     std::condition<mutex_type> cv_;
     mutex_type* mut_;
     unsigned wait_count_;
public:

     condition_debug() : mut_(0), wait_count_(0) {}
     explicit condition_debug(mutex_type& m) : mut_(&m),
wait_count_(1) {}

     void notify_one() {cv_.notify_one();}
     void notify_all() {cv_.notify_all();}

     template <class Lock>
         void wait(Lock& lock)
         {
             precheck(lock);
             cv_.wait(lock);
             postcheck();
         }

     template <class Lock, class Predicate>
         void wait(Lock& lock, Predicate pred)
         {
             precheck(lock);
             cv_.wait(lock, std::move(pred));
             postcheck();
         }

     template <class Lock>
         bool timed_wait(Lock& lock, const std::utc_time& abs_time)
         {
             precheck(lock);
             cv_.timed_wait(lock, abs_time);
             postcheck();
         }

     template <class Lock, class Predicate>
         bool timed_wait(Lock& lock, const std::utc_time& abs_time,
Predicate pred)
         {
             precheck(lock);
             cv_.timed_wait(lock, abs_time, std::move(pred));
             postcheck();
         }

private:
     template <class Lock>
     void precheck(const Lock& lock)
     {
         if (!lock.owns() || (mut_ != 0 && lock.mutex() != mut_))
             throw std::runtime_error("Or whatever error handling
policy you want");
         ++wait_count_;
         mut_ = lock.mutex();
     }

     void postcheck()
     {
         if (--wait_count_ == 0)
             mut_ = 0;
     }
};

-Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk