|
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