Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2007-10-30 20:01:43


On Oct 30, 2007, at 3:43 PM, Yuval Ronen wrote:

>>> * If C++0x is going to have lambda support (is it?), then maybe the
>>> not-accepting-predicate overloads of condition::wait are no longer
>>> needed, and can be removed? I think the major reason why people use
>>> that
>>> overload is because they are too lazy to write a functor. With core
>>> support for lambda, it's a breeze. This will solve the other problem
>>> (mentioned in some other thread in this ML) about absolute vs.
>>> relative
>>> times.
>>
>> I would be very hesitant to force one programming style over
>> another. Sometimes, for whatever reasons, one might really need an
>> explicit loop (say to handle more complex flow control than just
>> while (!pred) cv.wait(lk)).
>
> Is there any known use case for that? The common practice, and as
> far as
> I remember, it's also defined by POSIX, that *all* calls to wait
> must be
> like while (!pred) cv.wait(), so there is no other way. Is it?

I was thinking about "embellished" while loops. Maybe something like:

while (!pred())
{
     cv.timed_wait(lk, std::get_system_time() + std::milliseconds(100));
     bool is_canceled = true;
     if (std::atomic_compare_and_swap(&cancel, &is_canceled, false))
     {
         cv2.notify_one();
         while (!exceptional_pred())
             cv.wait(lk);
         throw thread_canceled();
     }
}

All of this could probably be packed into a predicate. But it might
be inconvenient. Sometimes it is easier to just write your own loop.

>>> 2. Make sizeof(unique_lock) smaller - no need for bool owns.
>>
>> Even if we remove defer_lock, unique_lock will still need the
>> internal bool to support try and timed locks. This functionality
>> still retains a reference to the mutex even if the try/timed lock
>> fails (so that further action with the mutex can be easily taken).
>
> This is something I don't understand. Why should the lock retain a
> reference to the mutex even if the try/timed lock fails?

Because if the try_lock fails, I might want to take some corrective
action and then do a blocking lock() on the mutex. If the lock has
retained the reference to the mutex, I don't have to store that
reference elsewhere "just in case" the try_lock fails.

>>> 3. Make unique_lock more similar to unique_ptr, which makes it more
>>> intuitive.
>>> 4. Make std::lock simpler in the way that it doesn't need to accept
>>> locks, only mutexes.
>>
>> std::lock isn't further complicated by accepting both mutexes and
>> locks. It accepts anything that supports lock(), try_lock() and
>> unlock(). To disallow locks, we would have to make std::lock more
>> complicated by detecting lock template arguments and then actively
>> disabling them. I see no motivation to add this complication.
>
> Oh no, I wasn't suggesting something like that at all. By "make
> std::lock simpler" I meant simpler in our heads. Make our thinking
> of it
> simpler. Thinking about it (and documenting it) as something that
> deals
> with mutexes is simpler than if it was dealing with both mutexes
> and locks.

The current spec just says:

Each template parameter type must supply the following member
functions with semantics corresponding to the Mutex concept, except
that try_lock is allowed to throw an exception [Note: The unique_lock
class template meets these requirements when suitable instantiated. --
end note]

void lock();
bool try_lock();
void unlock();

I suppose we could remove the note. But I really don't think that
simplifies anything.

>>> In general it seems you paid a lot of attention to being able to
>>> pass a
>>> lock instead of a mutex wherever possible. I think it's
>>> absolutely not
>>> necessary. It's an undue complication.
>>
>> Actually I tried to pay attention to what the minimal requirements
>> were on template parameters. It just turned out that some generic
>> code would accept either locks or mutexes.
>
> But you made the effort of mentioning it several times in the
> document.
> That implies importance. My claim is that there is no importance to
> it,
> because there's no use for it, even if it happens that we named
> unique_lock::lock the same way we named mutex::lock. We could've have
> named them differently. And while I'm thinking of it, why is there a
> lock() method for unique_lock anyway? Isn't it unnecessary?

The original answer is because that's the way boost::scoped_lock
specified its API. After further study and experience, I find myself
in complete agreement with the boost API in this regard, at least for
unique_lock. I think the member lock() functions on unique_lock are
quite convenient. That being said, please see lock_guard in N2447
which both lacks the defer_lock functionality, and lacks member lock,
try_lock and unlock functions. It also lacks the internal bool you
would like to get rid of. I think we may already have what you're
looking for, just under a different name.

-Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk