
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