Boost logo

Boost :

From: Yuval Ronen (ronen_yuval_at_[hidden])
Date: 2007-08-21 18:02:25

Howard Hinnant wrote:
> On Aug 21, 2007, at 3:22 PM, Yuval Ronen wrote:
>> Howard Hinnant wrote:
>>> On Aug 21, 2007, at 8:46 AM, Yuval Ronen wrote:
>>>> 1. I couldn't understand what defer_lock is good for, even after
>>>> reading
>>>> Q.9 of the FAQ. I believe the use-case shown in Q.9 should actually
>>>> use
>>>> accept_ownership instead. Can you elaborate please?
>>> See if this is any clearer:
>> I'm afraid not...
>> This example has 3 lines, the first 2 create unique_lock with
>> defer_lock, and the 3rd calls std::lock. Those unique_lock don't lock,
>> because std::lock to lock. OK. But who unlocks? The unique_locks don't
>> own the mutexes, and therefore don't unlock them. But someone needs to
>> unlock, and it sounds logical that the unique_locks would... Had we
>> used
>> accept_ownership, the unique_locks would have owned the mutexes, and
>> unlock them. That's the difference between defer_lock and
>> accept_ownership, the ownership, isn't it?
> Ok, the lightbulb went off in my head and I think I understand your
> question now. Thanks for not giving up on me.
> I've tried again here:
> and see the next question (#10) as well. If that doesn't do it, see:
> and search for "defer_lock_type" and "accept_ownership_type" for the
> unique_lock implementation of these constructors. Neither constructor
> does anything to the mutex, and simply sets the owns() flag to false
> or true respectively. The unique_lock destructor will unlock the
> mutex iff it owns()'s the mutex.

Not yet... :)

One of the added sentences is "After std::lock locks l1 and l2, these
locks now own their respective mutexes". How does that happen? I looked
at the implementation code, but could see nothing that changes owns()
from false to true after construction. Have I missed it?

Another related question is why std::lock works with locks and not
mutexes? Can't see the benefit in that.

>>>> Either way, I believe this design wouldn't meet the use case which I
>>> didn't effectively communicate in #14:
>>> Given a read-write mutex and its associated condition variable:
>>> my::shared_mutex rw_mut;
>>> std::condition<my::shared_mutex> cv(rw_mut);
>>> client code wants to wait on that cv in two different ways:
>>> 1. With rw_mut read-locked.
>>> 2. With rw_mut write-locked.
>>> If we initialized the condition variable with cv(rw_mut.exclusive()),
>>> then cv.wait() would wait with rw_mut write-locked, but we wouldn't
>>> be
>>> able to wait on cv with rw_mut read-locked.
>>> If we initialized the condition variable with cv(rw_mut.shared()),
>>> then cv.wait() would wait with rw_mut read-locked, but we wouldn't be
>>> able to wait on cv with rw_mut write-locked.
>>> This use case desires *both* types of waits on the *same* mutex/cv
>>> pair.
>> The last sentence starts with "This use case", but I see no use
>> case. Do
>> we really have such a use case? I haven't seen one yet. But even if we
>> had, then maybe the solution is the same solution to the requirement
>> you
>> phrased as "The freedom to dynamically associate mutexes with
>> condition
>> variables" or "The ability to wait on general mutex / lock types"
>> (what's the difference between those two sentences anyway?) in your
>> response to Peter. Add a 'set_mutex(mutex_type &)', or maybe even
>> 'set_mutex(mutex_type *)' to std::condition. I think it will solve
>> this
>> rare case.
> Ok, perhaps I'll clean the following use case up, and include it in
> the faq. First I'll try it out here. :-)
> I've got a "communication node" class. It serves as a node in a
> network. It gets data from somewhere and temporarily stores it while
> it uses several threads to forward (or relay) the information to other
> nodes in the network. For simplicity I'm using vector<int> for the
> data, and only two relay threads. The example is incomplete (not even
> compiled), just illustrative right now:
> class communication_node
> {
> std::vector<int>* from_;
> std::vector<int> data_;
> std::vector<int> to_[2];
> typedef std::tr2::shared_mutex Mutex;
> Mutex mut_;
> std::condition<Mutex> cv_;
> bool get_data_;
> bool fresh_data_;
> bool data_relayed_[2];
> public:
> void supplier()
> {
> while (true)
> {
> std::unique_lock<Mutex> write_lock(mut_);
> while (!get_data_ || !data_relayed_[0] || !
> data_relayed_[1])
> cv_.wait(write_lock);
> std::copy(from_->begin(), from_->end(), data_.begin());
> get_data_ = false;
> fresh_data_ = true;
> data_relayed_[0] = false;
> data_relayed_[1] = false;
> cv_.notify_all();
> }
> }
> void relayer(int id)
> {
> while (true)
> {
> std::tr2::shared_lock<Mutex> read_lock(mut_);
> while (data_relayed_[id])
> cv_.wait(read_lock);
> std::copy(data_.begin(), data_.end(), to[id].begin());
> data_relayed_[id] = true;
> cv_.notify_all();
> }
> }
> };

The relayer, which supposed to be a reader, actually is a writer, to
data_relayed_[id], so I believe a read_lock is not enough...

> Because of this, it is
> not possible (in the above use case) for there to be a set_mutex on
> the condition to change the facade, since both facades are
> simultaneously in use.

Yes, I've realized that too late. My set_mutex() function is useless
because it has to be atomic with the wait().

> Did I make more sense this time? I often complain when people use too
> much English and not enough C++ in their arguments, and then I find
> myself being guilty of the same thing. :-)

You keep on improving. I hope we all are ;-)

Boost list run by bdawes at, gregod at, cpdaniel at, john at