|
Boost : |
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2008-05-04 13:45:17
----- Original Message -----
From: "Anthony Williams" <anthony_at_[hidden]>
To: <threads-devel_at_[hidden]>
Sent: Monday, April 14, 2008 5:34 PM
Subject: Re: [Threads-devel] [boost] Expected behaviour of condition
variable wait when unique_lock is not locked
> Quoting "vicente.botet" <vicente.botet_at_[hidden]>:
>
>> which is the expected behaviour of condition variable wait when
>> unique_lock is not locked
>
> Undefined behaviour: a precondition has not been met.
IMO this is not satisfactory. At least an exception should be thrown. And
the C++0x recomendation states this.
"void wait(unique_lock<mutex>& lock);
Precondition:
lock is locked by the current thread, and either:
No other thread is waiting on this condition_variable object, or
The lock arguments supplied by all concurrently waiting threads (via
wait or timed_wait) return the same value for lock.mutex().
Effects:
Atomically calls lock.unlock() and blocks on *this.
When unblocked, calls lock.lock() (possibly blocking on the lock) and
returns.
The function will unblock when this thread is signaled by a call to
this->notify_one(), a call to this->notify_all(), or spuriously.
If the function exits via an exception, lock.lock() will still be called
prior to exiting the function scope.
Postconditions:
lock is locked by the current thread.
Throws:
system_error when the effects or postconditions cannot be achieved.
"
I'm wondering why the condition_variable wait operation do not requires a
strict lock (the one introduced by Andrei Alexandrescu in his article about
external locking "Multithreading and the C++ Type System") instead of a
unique lock or whatever. In this way the interface will force the
precondition. Please let me know if this has already been discused in this
list.
template <typename Lockable>
class strict_lock : private boost::noncopyable /*< Is not copyable >*/ {
BOOST_CONCEPT_ASSERT((LockableConcept<Lockable>));
public:
typedef Lockable lockable_type;
explicit strict_lock(lockable_type& obj)
: obj_(obj) { obj.lock(); } /*< locks on construction >*/
~strict_lock() { obj_.unlock(); } /*< unlocks on destruction >*/
typedef bool (strict_lock::*bool_type)() const; /*< safe bool idiom >*/
operator bool_type() const { return &strict_locker::owns_lock; }
bool operator!() const { return false; } /*< always owned >*/
bool owns_lock() const { return true; }
const lockable_type* mutex() const { return &obj_; }
bool is_locking(lockable_type* l) const { return l==mutex(); } /*<
strict lockers specific function >*/
/*< no possibility to unlock >*/
private:
lockable_type& obj_;
strict_lock(); /*< disable default constructor >*/
BOOST_NON_ALIAS(strict_lock); /*< disable aliasing >*/
BOOST_NON_HEAP_ALLOCATED(strict_lock); /*< disable heap allocation >*/
};
namespace boost {
class condition_variable
// ...
void wait(strict_lock<mutex>& l) {
# ifndef BOOST_THREAD_CONDITION_VARIABLE_DONT_CHECK_SAME
/*< define BOOST_THREAD_CONDITION_VARIABLE_DONT_CHECK_SAME
if you don't want to check locker check the same lockable >*/
if (!l.is_locking(&m)) throw lock_error(); /*< run time check throw
if not locks the same >*/
# endif
// ... do as for unique_lock
}
};
}
Evidently we can have more that one way to implement a strict lock but we
have no way to check at compile time that a class is a model of a strict
lock, so we need to relai on the "parolle" of the library author and add
some optional run time checks.
The condition_variable interface can be extended to accept arbitrary locks
satisfying some constraints.
template <class Locker>
void wait(Locker& l) {
BOOST_CONCEPT_ASSERT((StrictLockerConcept<Locker>));
BOOST_STATIC_ASSERT((is_strict_lock<Locker>::value));
/*< Locker is a strict lock "sur parolle" >*/
BOOST_STATIC_ASSERT((is_same<
Lockable,
typename lockable_type<Locker>::type>::value));
/*< that locks the same lockable type >*/
# ifndef BOOST_THREAD_CONDITION_VARIABLE_DONT_CHECK_OWNERSHIP
/*< define BOOST_THREAD_CONDITION_VARIABLE_NO_CHECK_OWNERSHIP
if you don't want to check locker ownership >*/
if (! l) throw lock_error(); /*< run time check throw if no locked
>*/
# endif
# ifndef BOOST_THREAD_CONDITION_VARIABLE_DONT_CHECK_SAME
/*< define BOOST_THREAD_CONDITION_VARIABLE_DONT_CHECK_SAME
if you don't want to check locker check the same lockable >*/
if (!l.is_locking(&m)) throw lock_error(); /*< run time check throw
if not locks the same >*/
# endif
// ... do as for unique_lock
}
is_strict_lock must be specialized by the strict locker implementer to state
"sur parolle" that the class is a strict lock.
Any comments?
____________________
Vicente Juan Botet Escriba
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk