
Batov, Vladimir wrote:
Part of the point, though, is that try_lock doesn't actually work that way: one of the try_lock constructors does a non-blocking try_lock(), and the other does a blocking lock(). Without looking, do you always remember which one does which? I don't.
Well, first, the behavior that I was talking about in my prev. email (ones I was happy about) were the 'basic' constructors (that I called 'default' constructors -- what an embarassment). The following is IMHO good and I would not like see them complicated:
1. scoped_lock l(m); // Blocks until it locks. 2. scoped_lock l(m, true); // As above. 3. scoped_lock l(m, false); // Only declaration. Does nothing. Needed if I need to declare but use it later. 4. try_lock l(m); // Tries to lock. Always falls through. I need to check the result. 5. try_lock l(m, false); // Only declaration. Does nothing. 6. timed_lock l(m, time); // Blocks until locks or 'time' expires. 7. timed_lock l(m, false); // Only declaration. Does nothing.
I can agree with all of these.
I have to agree that the following behavior
8. try_lock l(m, true); 9. timed_lock l(m, true);
that according to the documentation call lock() instead of respective try_lock() and timed_lock() look wrong.
I'm glad you agree, since this is the most important of the issues I was trying to address.
I'd be temted to say that merely making (8) work as (4) would be sufficient fo fix try_lock.
This would be best; unfortunately, this would silently change the meaning of existing code.
(9) does not appear "fixable" in a similar way.
Which is one reason I suggested the solution I did (adding an additional "blocking" parameter).
I am tempted to suggest something similar to overloaded 'operator new':
struct nolock_t {}; nolock_t nolock;
1. scoped_lock l(m); 2. scoped_lock l(m, nolock); 3. try_lock l(m); 4. try_lock l(m, nolock); 5. timed_lock l(m, time); 6. timed_lock l(m, nolock);
It removes redundancy (as with the original scoped_lock(m) and scoped_lock(m, true)) and makes everything simple and consistent.
I considered something like this, but since I was also working on the read/write mutex at the time, I was trying to be consistent with that as well. The read/write mutex has three states, of course; read-clocked, write-locked, and unlocked, so this parameter uses an enumeration instead of a bool (or the nolock parameter you suggest above). Of course the same approach could be used with the read/write lock, and it would solve the problem of degenerate cases like try_lock(m, false, blocking). Thanks for your comments; I'll think about this some more. Mike