interprocess_condition::wait(scoped_lock<interprocess_upgradable_lock> &) missing

Hi boost users, I've discovered that the following doesn't compile. using namespace boost::interprocess; interprocess_upgradable_mutex mutex; interprocess_condition cv; boost::posix_time::ptime deadline; { scoped_lock<interprocess_upgradable_mutex> lock(mutex); sharable_lock<interprocess_upgradable_mutex> lock2(mutex); cv.wait(lock); // doesn't compile cv.timed_wait(lock, deadline); // doesn't compile. } It is failing because boost doesn't define the following methods: void interprocess_condition::do_wait(interprocess_upgradable_mutex &mut); bool interprocess_condition::do_timed_wait(const syssrv_boost::posix_time::ptime &abs_time, interprocess_upgradable_mutex &mut); I was thinking of making a basic implementation like so: Define a scoped_unlock class, which behaves just like scoped_lock except the constructor unlocks and the destructor locks. Then define the following: inline void interprocess_condition::do_wait(interprocess_upgradable_mutex &mut) { scoped_unlock<interprocess_upgradable_mutex> unlock(mut); interprocess_mutex &internal_mutex = mut.m_mut; scoped_lock<interprocess_mutex> internal_lock(internal_mutex); this->wait(internal_lock); } inline bool interprocess_condition::do_timed_wait (const syssrv_boost::posix_time::ptime &abs_time, interprocess_upgradable_mutex &mut) { scoped_unlock<interprocess_upgradable_mutex> unlock(mut); interprocess_mutex &internal_mutex = mut.m_mut; scoped_lock<interprocess_mutex> internal_lock(internal_mutex); return this->timed_wait(internal_lock, abs_time); } These functions simply do the following: - unlock the exclusive lock on the interprocess_upgradable_mutex (which is emulated with an internal interprocess_mutex) - then lock the internal interprocess_mutex - Call interprocess_condition::wait or interprocess_condition::time_ wait on the implementation lock which does all the work - unlock the internal interprocess_mutex - relock the exclusive lock on the interprocess_upgradable_mutex Intuitively, this should work. Is there anything I missed that would make this an incorrect implementation? Cheers, -John

Oops. Left some name mangling in there. Fixed now. On 3 February 2011 12:16, John Ky <newhoggy@gmail.com> wrote:
Hi boost users,
I've discovered that the following doesn't compile.
using namespace boost::interprocess; interprocess_upgradable_mutex mutex; interprocess_condition cv; boost::posix_time::ptime deadline;
{ scoped_lock<interprocess_upgradable_mutex> lock(mutex); sharable_lock<interprocess_upgradable_mutex> lock2(mutex); cv.wait(lock); // doesn't compile cv.timed_wait(lock, deadline); // doesn't compile. }
It is failing because boost doesn't define the following methods:
void interprocess_condition::do_wait(interprocess_upgradable_mutex &mut);
bool interprocess_condition::do_timed_wait(const boost::posix_time::ptime &abs_time, interprocess_upgradable_mutex &mut);
I was thinking of making a basic implementation like so:
Define a scoped_unlock class, which behaves just like scoped_lock except the constructor unlocks and the destructor locks.
Then define the following:
inline void interprocess_condition::do_wait(interprocess_upgradable_mutex &mut) { scoped_unlock<interprocess_upgradable_mutex> unlock(mut); interprocess_mutex &internal_mutex = mut.m_mut; scoped_lock<interprocess_mutex> internal_lock(internal_mutex); this->wait(internal_lock); }
inline bool interprocess_condition::do_timed_wait (const boost::posix_time::ptime &abs_time, interprocess_upgradable_mutex &mut) { scoped_unlock<interprocess_upgradable_mutex> unlock(mut); interprocess_mutex &internal_mutex = mut.m_mut; scoped_lock<interprocess_mutex> internal_lock(internal_mutex); return this->timed_wait(internal_lock, abs_time); }
These functions simply do the following:
- unlock the exclusive lock on the interprocess_upgradable_mutex (which is emulated with an internal interprocess_mutex) - then lock the internal interprocess_mutex - Call interprocess_condition::wait or interprocess_condition::time_ wait on the implementation lock which does all the work - unlock the internal interprocess_mutex - relock the exclusive lock on the interprocess_upgradable_mutex
Intuitively, this should work.
Is there anything I missed that would make this an incorrect implementation?
Cheers,
-John

El 03/02/2011 2:16, John Ky escribió:
Hi boost users,
I've discovered that the following doesn't compile.
using namespace boost::interprocess; interprocess_upgradable_mutex mutex; interprocess_condition cv; boost::posix_time::ptime deadline;
Condition variables are only compatible with interprocess_mutex. I think they have no sense with other types of locks, because a mutex and a condition variable should know each other. In POSIX, condition variables are not compatible even with POSIX recursive locks, not to mention user defined locking primitives. Best, Ion

Hi Ion, What do you mean by "a mutex and a condition variable should know each other". In the proposed interprocess_upgradable_mutex implementation, the condition variable is associated with the underlying interprocess_mutex, so it should be fine right? Cheers, -John 2011/2/4 Ion Gaztañaga <igaztanaga@gmail.com>
El 03/02/2011 2:16, John Ky escribió:
Hi boost users,
I've discovered that the following doesn't compile.
using namespace boost::interprocess; interprocess_upgradable_mutex mutex; interprocess_condition cv; boost::posix_time::ptime deadline;
Condition variables are only compatible with interprocess_mutex. I think they have no sense with other types of locks, because a mutex and a condition variable should know each other. In POSIX, condition variables are not compatible even with POSIX recursive locks, not to mention user defined locking primitives.
Best,
Ion _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

El 04/02/2011 0:59, John Ky escribió:
Hi Ion,
What do you mean by "a mutex and a condition variable should know each other".
In the proposed interprocess_upgradable_mutex implementation, the condition variable is associated with the underlying interprocess_mutex, so it should be fine right?
But upgradable mutex could be implemented without using interprocess_mutex, that's an implementation detail. I don't see the need to use an upgradable lock. POSIX does not support condition variables with read-write locks (although I think Windows Vista does) so Interprocess does not offer any support for this. Ion

El 04/02/2011 1:20, Ion Gaztañaga escribió:
El 04/02/2011 0:59, John Ky escribió:
Hi Ion,
What do you mean by "a mutex and a condition variable should know each other".
In the proposed interprocess_upgradable_mutex implementation, the condition variable is associated with the underlying interprocess_mutex, so it should be fine right?
But upgradable mutex could be implemented without using interprocess_mutex, that's an implementation detail. I don't see the need to use an upgradable lock. POSIX does not support condition variables with read-write locks (although I think Windows Vista does) so Interprocess does not offer any support for this.
I think you are trying to support a condition variable that could accept any kind of lock. That sounds like condition_variable_any, a class that is not implemented in Interprocess yet. The interface of condition_variable might suggest that it supports any kind of lock, but it is not true. I think I should state clearly this requirement in the documentation and the code and maybe try to support condition_variable_any in the future. Best, Ion

Hi Ion, Yes that makes sense. It would be nice to have a condition_variable_any that supports any kind of lock. It's probably okay if there were a condition_variable_* class for each lock type as well. Or did you mean something like condition_variable_any<Mutex>? Cheers, -John 2011/2/4 Ion Gaztañaga <igaztanaga@gmail.com>
El 04/02/2011 1:20, Ion Gaztañaga escribió:
El 04/02/2011 0:59, John Ky escribió:
Hi Ion,
What do you mean by "a mutex and a condition variable should know each other".
In the proposed interprocess_upgradable_mutex implementation, the condition variable is associated with the underlying interprocess_mutex, so it should be fine right?
But upgradable mutex could be implemented without using interprocess_mutex, that's an implementation detail. I don't see the need to use an upgradable lock. POSIX does not support condition variables with read-write locks (although I think Windows Vista does) so Interprocess does not offer any support for this.
I think you are trying to support a condition variable that could accept any kind of lock. That sounds like condition_variable_any, a class that is not implemented in Interprocess yet. The interface of condition_variable might suggest that it supports any kind of lock, but it is not true. I think I should state clearly this requirement in the documentation and the code and maybe try to support condition_variable_any in the future.
Best,
Ion _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

On Feb 3, 2011, at 8:14 PM, John Ky wrote:
It would be nice to have a condition_variable_any that supports any kind of lock.
It's probably okay if there were a condition_variable_* class for each lock type as well. Or did you mean something like condition_variable_any<Mutex>?
A condition_variable_any<Mutex> is counter-productive. What you really need is: class condition_variable_any { public: condition_variable_any(); ~condition_variable_any(); condition_variable_any(const condition_variable_any&) = delete; condition_variable_any& operator=(const condition_variable_any&) = delete; void notify_one(); void notify_all(); template <class Lock> void wait(Lock& lock); template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred); template <class Lock, class Clock, class Duration> cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time); template <class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred); template <class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time); template <class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred); }; I.e. only the wait functions are templated on the lock. This allows one cv to be waited on by two different kinds of locks at the same time. For example one thread could wait on the thread with a shared_lock while another thread waited on the same cv with a unique_lock (but using the same underlying shared_mutex): shared_mutex mut; condition_variable_any cv; void wait_in_shared_ownership_mode() { shared_lock<shared_mutex> shared_lk(mut); // mut is now shared-locked // ... while (not_ready_for_shared_to_proceed()) cv.wait(shared_lk); // shared-lock released while waiting // mut is now shared-locked // ... } // mut is now unlocked void wait_in_unique_ownership_mode() { unique_lock<shared_mutex> lk(mut); // mut is now unique-locked // ... while (not_ready_for_unique_to_proceed()) cv.wait(lk); // unique-lock released while waiting // mut is now unique-locked // ... } // mut is now unlocked A third thread could change either the not_ready_for_shared_to_proceed predicate, the not_ready_for_unique_to_proceed predicate, or both, and then cv.notify_all(). This is very powerful stuff when you need it (most of the time condition_variable and mutex are all you need). In this set condition_variable_any and unique_lock are in C++0x. I hope to propose shared_lock and shared_mutex for tr2 and ultimately c++1x. Field experience here (good or bad) would help. Here is a tutorial and implementation of the shared and upgrade mutexes and locks: http://home.roadrunner.com/~hinnant/mutexes/locking.html Here is an implementation of condition_variable_any: http://llvm.org/svn/llvm-project/libcxx/trunk/include/condition_variable -Howard

Thanks Howard for the very well written response. I appreciate the effort. I will definitely look at this condition_variable_any class. It is a good design and I want to stay close to the standard as well. Cheers, -John On 4 February 2011 13:05, Howard Hinnant <howard.hinnant@gmail.com> wrote:
On Feb 3, 2011, at 8:14 PM, John Ky wrote:
It would be nice to have a condition_variable_any that supports any kind of lock.
It's probably okay if there were a condition_variable_* class for each lock type as well. Or did you mean something like condition_variable_any<Mutex>?
A condition_variable_any<Mutex> is counter-productive. What you really need is:
class condition_variable_any { public: condition_variable_any(); ~condition_variable_any();
condition_variable_any(const condition_variable_any&) = delete; condition_variable_any& operator=(const condition_variable_any&) = delete;
void notify_one(); void notify_all();
template <class Lock> void wait(Lock& lock); template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred);
template <class Lock, class Clock, class Duration> cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);
template <class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);
template <class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);
template <class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred); };
I.e. only the wait functions are templated on the lock. This allows one cv to be waited on by two different kinds of locks at the same time. For example one thread could wait on the thread with a shared_lock while another thread waited on the same cv with a unique_lock (but using the same underlying shared_mutex):
shared_mutex mut; condition_variable_any cv;
void wait_in_shared_ownership_mode() { shared_lock<shared_mutex> shared_lk(mut); // mut is now shared-locked // ... while (not_ready_for_shared_to_proceed()) cv.wait(shared_lk); // shared-lock released while waiting // mut is now shared-locked // ... } // mut is now unlocked
void wait_in_unique_ownership_mode() { unique_lock<shared_mutex> lk(mut); // mut is now unique-locked // ... while (not_ready_for_unique_to_proceed()) cv.wait(lk); // unique-lock released while waiting // mut is now unique-locked // ... } // mut is now unlocked
A third thread could change either the not_ready_for_shared_to_proceed predicate, the not_ready_for_unique_to_proceed predicate, or both, and then cv.notify_all(). This is very powerful stuff when you need it (most of the time condition_variable and mutex are all you need).
In this set condition_variable_any and unique_lock are in C++0x. I hope to propose shared_lock and shared_mutex for tr2 and ultimately c++1x. Field experience here (good or bad) would help. Here is a tutorial and implementation of the shared and upgrade mutexes and locks:
http://home.roadrunner.com/~hinnant/mutexes/locking.html
Here is an implementation of condition_variable_any:
http://llvm.org/svn/llvm-project/libcxx/trunk/include/condition_variable
-Howard
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

El 04/02/2011 3:05, Howard Hinnant escribió:
In this set condition_variable_any and unique_lock are in C++0x. I hope to propose shared_lock and shared_mutex for tr2 and ultimately c++1x. Field experience here (good or bad) would help. Here is a tutorial and implementation of the shared and upgrade mutexes and locks:
http://home.roadrunner.com/~hinnant/mutexes/locking.html
Here is an implementation of condition_variable_any:
http://llvm.org/svn/llvm-project/libcxx/trunk/include/condition_variable
Thanks Howard! Just two comments: a) Windows Vista condition variables natively supports Slim Reader/Writer Locks (http://msdn.microsoft.com/en-us/library/ms686304%28v=VS.85%29.aspx), the implementation could take advantage of this if shared_mutex is based on this primitive. b) Is there any reason to store shared_ptr<mutex> instead of a mutex? Best, Ion

On Feb 4, 2011, at 12:54 PM, Ion Gaztañaga wrote:
El 04/02/2011 3:05, Howard Hinnant escribió:
In this set condition_variable_any and unique_lock are in C++0x. I hope to propose shared_lock and shared_mutex for tr2 and ultimately c++1x. Field experience here (good or bad) would help. Here is a tutorial and implementation of the shared and upgrade mutexes and locks:
http://home.roadrunner.com/~hinnant/mutexes/locking.html
Here is an implementation of condition_variable_any:
http://llvm.org/svn/llvm-project/libcxx/trunk/include/condition_variable
Thanks Howard! Just two comments:
a) Windows Vista condition variables natively supports Slim Reader/Writer Locks (http://msdn.microsoft.com/en-us/library/ms686304%28v=VS.85%29.aspx), the implementation could take advantage of this if shared_mutex is based on this primitive.
<nod> Agreed.
b) Is there any reason to store shared_ptr<mutex> instead of a mutex?
Yes. I missed this subtlety for years. I think it was Peter Dimov who pointed it out to me: Thread A notfies the condition_variable_any, which thread B is waiting on. Then thread A destructs the condition_variable_any before thread B wakes and returns from the wait. This is supposed to work. The POSIX condition variable also works this way. The shared_ptr is used to share ownership of the mutex between the condition_variable_any, and the thread running the wait function. If the condition_variable_any destructs while the wait is running, the mutex stays alive long for the wait function to lock and unlock it, and then destructs when the wait returns. -Howard
participants (3)
-
Howard Hinnant
-
Ion Gaztañaga
-
John Ky