
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