
On Jul 12, 2004, at 11:28 AM, Peter Dimov wrote:
Howard Hinnant wrote:
On Jul 11, 2004, at 8:46 AM, Peter Dimov wrote:
[...]
Mutex * mutex() const; // returns: the associated mutex;
Why return a pointer instead of a reference? What about this instead?
Mutex& mutex(); const Mutex& mutex() const;
Because of the use case shown below:
void f( scoped_lock & lock ) { // check for lock validity assert( lock.locked() && lock.mutex() == &my_mutex );
// proceed with operation }
A pointer makes it clear that it is the identity of the associated mutex that is being queried. A (const-"correct") reference return implies that the purpose of the accessor is to return, well, a reference to the mutex object, presumably so that the client can do something with it.
That would put to rest any questions about mutex() transferring mutex ownership, e.g.:
delete lock.mutex();
I considered
bool is_associated_with( Mutex const & m ) const;
but that's a bit too much of a handholding for my taste, and doesn't allow us to use mutex() in the Effects clauses. The kind of a person that would delete lock.mutex() would never get a multithreaded program correct anyway.
If we expose either a Mutex* or a Mutex&, and we standardize a public Mutex interface (with lock(), unlock(), etc.), then we are saying that one can call functions on the pointer or reference returned by mutex(). And now that I write that sentence my skin is beginning to crawl. ;-) I have occasionally seen a need for using a mutex outside of a scoped_lock, when the lock/unlock pattern is not as neat as what scoped_lock provides. Alexander's read/write lock algorithm is a good example: http://groups.google.com/groups?selm=3B166244.F923B993%40web.de . And so I do not object to standardizing a public mutex interface so that they can be used outside of a scoped_lock. But allowing a mutex to be manipulated within a scoped_lock seems dangerous. Brainstorming: template <class Mutex> bool operator==(const scoped_lock<Mutex>&, const Mutex&); template <class Mutex> bool operator==(const Mutex&, const scoped_lock<Mutex>&); instead of Mutex* scoped_lock::mutex() const; ? Might look like: void f( scoped_lock & lock ) { // check for lock validity assert( lock.locked() && lock == my_mutex ); // proceed with operation } Haven't even prototyped it, so I'm not even sure it will work. Just a thought.
Now that we got rid of the excess locks, how about doing the same with the mutexes?
That worries me a lot. We need several different flavors of mutex because the more functionality you put into a mutex, the more expensive it is both in terms of size and speed. Why pay for a recursive mutex if you don't need one? Why pay for a timed mutex? But when you need one of these more expensive types, then the price is worth it.
First, please note that I never said anything about mutex being recursive (and the specification says that relocking is undefined behavior). A recursive mutex is a separate entity (and its specification is slightly different).
That aside. It is precisely the assertion that (1) a try_mutex is more expensive than a mutex, and (2) that a timed_mutex is more expensive than a try_mutex, that I am challenging. I see no evidence for (1). (2) is more interesting. You will note that POSIX indeed makes you explicitly state whether you want a recursive mutex or not - because recursive mutexes are more expensive. However no such requirement exists for "timed mutexes". Either all mutexes are timed, or they are not. This, for me, indicates that a timed mutex is not more expensive than a try mutex, just that pthread_mutex_timedlock is a late addition to the standard.
Do you have data on whether your platforms support pthread_mutex_timedlock?
I have one platform to support where I preferred a different implementation for mutex and try_mutex: Win98. Admittedly that one is quickly going obsolete. Nevertheless I currently still need to support it. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ dllproc/base/tryentercriticalsection.asp
TryEnterCriticalSection
Requires Windows XP, Windows 2000 Professional, or Windows NT Workstation 4.0.
Mac OS X up through 10.3.4 (most current) does not appear to support pthread_mutex_timedlock, and so on this platform my mutex implementation differs from my timed_mutex implementation.
I can't speak for the boost implementation (haven't carefully studied it), but the Metrowerks implementation also supports conditions operating on a recursive mutex.
Interesting. I presume that this was a deliberate design decision. There is a school of thought that says that a recursive mutex can only be used to sweep bugs under the carpet and is never needed in a correctly designed multithreaded program, which probably applies doubly to conditions operating on a recursive mutex locked more than once. But I'm not qualified to judge.
I have heard that school of thought too. It sounds somewhat like the same argument I've heard against goto. I agree with neither, but admittedly am more familiar with goto than I am with recursive mutexes. I have code where I use a recursive mutex. I wrote it knowing the bad karma surrounding recursive mutexes, but I used it anyway. I am sure that code could be restructured to use a non-recursive mutex. But the recursive mutex was both convenient for me, and provided correct and reasonable code in this instance (the code can not be considered buggy by any black box test I'm aware of). Maybe some day that code will bubble up my priority list and I'll rewrite it to use a non-recursive mutex. But that would be a significant rewrite, and I'm grateful that I am not forced into that decision by my threads library. -Howard