From: Noob on
Eric Sosman wrote:

> Noob wrote:
>
>> David Schwartz wrote:
>>
>>> Noob wrote:
>>>
>>>> But this library has specific requirements for the delete function:
>>>>
>>>> If delete is called on a locked mutex, the function must return an
>>>> error code to signal the error to the function's caller.
>>>
>>> That is, IMO, fundamentally busted behavior, unless it only means
>>> locked by the calling thread.
>>
>> Could you expand on /why/ you think it is busted behavior?
>> (I need ammo to try to convince the library authors.)
>>
>> The exact requirements are:
>>
>> 1) Return an error when attempting to delete "a mutex currently owned by
>> another thread".
>
> This is broken, period. Suppose thread T1 tries to delete the
> mutex and discovers (somehow) that it is locked by T2. T1 leaves
> the mutex alone, the deletion attempt returns an error, and all is
> well.

Except that one is left to wonder why T1 ever thought it would be a good
idea to delete the mutex...

> But suppose T1 comes along at a moment when the mutex is unlocked,
> and succeeds in deleting it. Two clock cycles later, T2 tries to lock
> the destroyed mutex, and the fertilizer hits the fan.

The library also requires "return an error when attempting to lock an
uninitialized mutex".

I don't know what they do in that library... Perhaps this explains why
they do not provide the source code.

> The fact that T1
> found the mutex unlocked is no proof that the mutex is "unused," or
> that T2 might not attempt to use it again.

Obviously.

> If T1 deletes a mutex that T2 might still want to use, the
> application is broken. If T1 can find the mutex locked by T2, it
> must be the case that T2 still wants to use it. Therefore the mere
> possibility that delete-while-locked-elsewhere could occur is proof
> that the design is broken.
>
>> 2) Return an error when attempting to delete "a mutex currently owned by
>> the calling thread".
>
> This isn't quite as clear-cut, but it still seems dubious.
>
>> [...]
>> Trying to delete a locked mutex sounds like a serious programmer error.
>
> Yes, but the point is more general: Trying to delete an "in use"
> mutex is a serious programmer error. A mutex in the locked state is
> obviously in use, but it is a mistake to reason that a mutex in the
> unlocked state is not in use.

Are there situations, algorithms, where many mutexes are created then
deleted (in a loop). It seems to me that mutexes are typically long
lived objects, initialized at application start-up, and destroyed
shortly before termination.

>> According to the library's spec, delete may be called at any time, by
>> any thread, and it is the user's responsibility to return an error and
>> leave the mutex untouched if the mutex is currently locked...
>
> Broken. The danger isn't when deletion fails because the mutex is
> locked, but when deletion succeeds because the mutex is unlocked -- but
> is still in use.

Sigh.

Regards.
From: Noob on
M�ns Rullg�rd wrote:

> David Schwartz wrote:
>
>> Noob wrote:
>>
>>> But this library has specific requirements for the delete function:
>>>
>>> If delete is called on a locked mutex, the function must return an error
>>> code to signal the error to the function's caller.
>>
>> That is, IMO, fundamentally busted behavior, unless it only means
>> locked by the calling thread.
>
> I agree. I wouldn't go anywhere near that library. Find another one
> written by someone with half a brain.

I have no such freedom. I must use that proprietary library.
It is a MAFIAA requirement (DVB Conditional Access).

> That said, you could use pthread_mutex_trylock() to find out whether
> or not the mutex is locked.

Any way I slice it, their requirement seems to impose high overhead
on lock and unlock operations for no gain whatsoever.

Regards.
From: Dave Butenhof on
Noob wrote:
> [ Cross-posted to c.p.t and c.u.p ]
>
> Hello,
>
> I'm using a proprietary library (no source code) which requires the user
> to provide 4 mutex-related functions (create, delete, lock, unlock).
>
> AFAICT, if I were using a POSIX OS, I could make trivial wrappers around
>
> pthread_mutex_init
> pthread_mutex_destroy
> pthread_mutex_lock
> pthread_mutex_unlock
>
> But this library has specific requirements for the delete function:
>
> If delete is called on a locked mutex, the function must return an error
> code to signal the error to the function's caller.
>
> However, the documentation for pthread_mutex_destroy explicitly states:
> http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_init.html
>
> """
> It shall be safe to destroy an initialized mutex that is unlocked.
> Attempting to destroy a locked mutex results in undefined behavior.
> """
>
> This means that my delete function can't just call pthread_mutex_destroy
> because I would invoke UB if the mutex is locked, and I wouldn't be able
> to return the requested error code, right?

Strictly speaking, what this means is that POSIX doesn't *require* an
implementation to know whether the mutex is already locked.

That's due to concerns over the performance of tracking mutex ownership
on some platforms... as well as (as you mention below) concerns that all
implementations be penalized by extra code that only helps broken
applications. Correct (production level) code doesn't destroy a mutex to
which any other thread might hold or request a reference. And as already
mentioned in a reply, once you get to that point you're lost anyway
because there are plenty of errors you won't be able to catch.

> So... Does this mean I have to keep track whether the mutex is locked?
> (It would appear so.)
>
> Or perhaps I could mess with the OS code? (shudder)
>
> I'd like to tell the library author that it is unreasonable to impose
> overhead on every lock and unlock operation just to catch an obvious (?)
> programming error on delete. What do you think?
>
> N.B. the library requires mutex with recursive semantic.

This requirement probably simplifies things. You really want a recursive
POSIX mutex (mutex type PTHREAD_MUTEX_RECURSIVE), and you have to track
mutex ownership to do that. Regardless of formal requirements, it's
highly unlikely a platform implementing recursive mutexes wouldn't
report EBUSY on an attempt to unlock a locked mutex.

But, again, your requirement is beyond the scope of the API standard and
into implementation defined territory. You need to check the
documentation for the platform(s) you care about to see what they'll
guarantee, or "roll your own".

If you're interested in a particular platform, this should be simple. If
you're interested in general portability, you'll pretty much need to
assume the answer is "roll your own" or "declare defeat and give up".
From: Eric Sosman on
Noob wrote:
> Eric Sosman wrote:
>
>> Noob wrote:
>>
>>> David Schwartz wrote:
>>>
>>>> Noob wrote:
>>>>
>>>>> But this library has specific requirements for the delete function:
>>>>>
>>>>> If delete is called on a locked mutex, the function must return an
>>>>> error code to signal the error to the function's caller.
>>>> That is, IMO, fundamentally busted behavior, unless it only means
>>>> locked by the calling thread.
>>> Could you expand on /why/ you think it is busted behavior?
>>> (I need ammo to try to convince the library authors.)
>>>
>>> The exact requirements are:
>>>
>>> 1) Return an error when attempting to delete "a mutex currently owned by
>>> another thread".
>> This is broken, period. Suppose thread T1 tries to delete the
>> mutex and discovers (somehow) that it is locked by T2. T1 leaves
>> the mutex alone, the deletion attempt returns an error, and all is
>> well.
>
> Except that one is left to wonder why T1 ever thought it would be a good
> idea to delete the mutex...

Right: This can only happen if there's something fundamentally
wrong with the T1/T2 program itself. The program is still using
something that it thinks it doesn't need; put differently, the
program believes that `X & !X' is true. A library that tries to
assist the program in such beliefs is at best quixotic.

>> But suppose T1 comes along at a moment when the mutex is unlocked,
>> and succeeds in deleting it. Two clock cycles later, T2 tries to lock
>> the destroyed mutex, and the fertilizer hits the fan.
>
> The library also requires "return an error when attempting to lock an
> uninitialized mutex".

Does it also require an error for attempting to lock a chunk of
memory containing random garbage that just sort of happens to look
like a mutex? What other miracles does this library demand?

No, look, seriously: malloc some memory, initialize a mutex M1 in
it, and let T1 and T2 use it for a while. Now T1 decides (erroneously)
that M1 is no longer needed, so it deletes it and frees the memory.
Now call malloc again, get the same chunk of memory back, and create a
brand-new mutex M2 at the same address where the now-defunct M1 used
to live. When T2 tries to lock M1, supplying an address that points to
the perfectly valid M2, you're supposed to be able to detect it?

Lotsa luck ...

> Are there situations, algorithms, where many mutexes are created then
> deleted (in a loop). It seems to me that mutexes are typically long
> lived objects, initialized at application start-up, and destroyed
> shortly before termination.

In an object-oriented design you might have a mutex inside each
instance of some class or other, protecting some of the instance's
own internals. If you create and destroy lots of instances of the
class, you'll create and destroy a lot of mutexes. (Mutices?)

This is pretty common in classes that represent containers or
collections or things of that sort, that tend to be fairly long-lived.
But even a short-lived object might need a mutex if it needs to preserve
an invariant involving two or more fields, or a field that can't be
updated atomically. You might have a Color class, for example, that
allows you to get/set any of RGB, HSV, CMYK, ... and needs to keep
all the representations in agreement; such a class might well have a
private mutex to serialize the getters and setters.

The good news on that sort of mutex, I guess, is that it's often
"internal" to the class, not accessible to J. Random Outsider. It
lives and dies with the class instance, so whenever it's safe to
delete the object it's also safe to delete the object's private mutex.

--
Eric.Sosman(a)sun.com
From: Scott Lurndal on
=?iso-8859-1?Q?M=E5ns_Rullg=E5rd?= <mans(a)mansr.com> writes:
>David Schwartz <davids(a)webmaster.com> writes:
>
>> On Oct 28, 6:27�am, Noob <r...(a)127.0.0.1> wrote:
>>
>>> But this library has specific requirements for the delete function:
>>>
>>> If delete is called on a locked mutex, the function must return an error
>>> code to signal the error to the function's caller.
>>
>> That is, IMO, fundamentally busted behavior, unless it only means
>> locked by the calling thread.
>
>I agree. I wouldn't go anywhere near that library. Find another one
>written by someone with half a brain.
>
>That said, you could use pthread_mutex_trylock() to find out whether
>or not the muxtex is locked.
>

My first thought too, use _trylock in the delete function. However, that
won't work for recursive mutexes, because it won't fail if the current thread
has the mutex.

scott