|
Boost : |
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2004-07-12 14:45:50
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk