Bug 113588 - NPTL pthread_mutex_destroy return EBUSY even if mutex is not locked...
NPTL pthread_mutex_destroy return EBUSY even if mutex is not locked...
Status: CLOSED NOTABUG
Product: Red Hat Enterprise Linux 3
Classification: Red Hat
Component: kernel (Show other bugs)
3.0
All Linux
medium Severity medium
: ---
: ---
Assigned To: Arjan van de Ven
Brian Brock
:
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2004-01-15 11:39 EST by Umesh R. Patil
Modified: 2007-11-30 17:07 EST (History)
5 users (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2004-01-15 12:55:37 EST
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)

  None (edit)
Description Umesh R. Patil 2004-01-15 11:39:08 EST
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4) Gecko/20030922

Description of problem:
pthread_mutex_destroy should return EBUSY if the mutex being destroyed
is currently locked.

If ptread_mutex_unlock is called without calling pthread_mutex_lock,
pthread_mutex_destroy in NPTL returns EBUSY.

This scenario can happen in cleanup handlers where a mutex is being
unlocked when a thread exits.

Version-Release number of selected component (if applicable):
glibc-2.3.2-95.6, kernel-2.4.21-4.EL, RHE ES 3.0, gcc-3.2.3-20

How reproducible:
Always

Steps to Reproduce:
1.  prog.c:

#include <stdio.h>
#include <string.h>
#include <pthread.h>

int main(int argc, char **argv)
{
    pthread_mutex_t mutt;
    int status;

    if ((status = pthread_mutex_init(&mutt, NULL)) == 0)
    {
        /*
        call unlock without first locking the mutex
        */
        if ((status = pthread_mutex_unlock(&mutt)) == 0)
        {
            /*
            destroy results in EBUSY...
            */
            if ((status = pthread_mutex_destroy(&mutt)) != 0)
            {
                char buf[256];
                char *strErr = strerror_r(status, buf, 256);
                fprintf(stderr, "status=%d, Error: %s\n", status, strErr);
            }
        }
    }

    return 0;
}

2. gcc -o prog prog.c -lpthread


Actual Results:  ./prog prints "status=16, Error: Device or resource
busy".

Expected Results:  Since the mutex was not locked,
pthread_mutex_destroy should have returned 0.

Additional info:

This happens in NPTL.  

LinuxThreads returns 0 for the scenario described above.

Using tcsh:

env LD_ASSUME_KERNEL=2.4.19 prog

does not print the EBUSY error.
Comment 1 Jakub Jelinek 2004-01-15 11:54:27 EST
I don't see anything wrong about this behaviour.
http://www.opengroup.org/onlinepubs/007904975/functions/pthread_mutex_trylock.html
says clearly that this program has undefined behaviour.
Returning EBUSY from pthread_mutex_destroy on such a mutex is
a conforming behaviour.
You can use PTHREAD_MUTEX_ERRORCHECK mutex if you want to get an
error from pthread_mutex_unlock and not undefined behaviour.
Comment 2 Umesh R. Patil 2004-01-15 12:07:12 EST
However,
http://www.opengroup.org/onlinepubs/007904975/functions/pthread_mutex_destroy.html

indicates:

"It shall be safe to destroy an initialized mutex that is unlocked.
Attempting to destroy a locked mutex results in undefined behavior."

In the example, the mutex is not locked.

Also:

"[EBUSY]
    The implementation has detected an attempt to destroy the object
referenced by mutex while it is locked or referenced (for example,
while being used in a pthread_cond_timedwait() or pthread_cond_wait())
by another thread."

The mutex in the example is neither locked nor referenced by another
thread.  EBUSY shouldn't be returned in this case...
Comment 3 Jakub Jelinek 2004-01-15 12:15:31 EST
That doesn't matter.
Once your program hits undefined behaviour once, the standard doesn't
cover anything in it any more.
The implementation may choose to lock a mutex in pthread_mutex_unlock
called on an unlocked mutex, format your disk or whatever else it decides.
Comment 4 Ulrich Drepper 2004-01-15 12:55:37 EST
You have put the mutex in an undefined state by incorrectly calling
pthread_mutex_unlock.  Anything can happen after that.  Stop arguing
that you are so innocent and the bad implementation should help you
poor developer.  It's your fault alone, live with the consequences. 
If you cannot write correct code, use the error checking mutex type.
Comment 5 Umesh R. Patil 2004-01-15 13:36:52 EST
Ulrich:

The code given to you is a sample for reproducing the case and I am 
not arguing anything here.  I was just pointing out what the spec 
says.

Mutex unlocks are done in cleanup handlers so that an exiting thread, 
that hit a cancellation point, doesn't leave it locked.  David 
Butenhof's book describes this mechanism in detail in Chapter 5 page 
147!!!

Your response here and elsewhere on the web are always rude and 
obnoxious.  Looks like you get angry when someone points out problems 
in your poorly written code!!!

You talk about bad implementation?  You should start with NPTL 
because any reasonable implementation would have basic checks before 
putting a resource in a funny condition.

Your stance is that if the "spec" doesn't say something - do 
something crazy (like format your disk - Thanks Jakub...)
Comment 6 Jakub Jelinek 2004-01-15 14:56:08 EST
POSIX specifically has error checking mutexes which are supposed to
do basic checks while normal mutexes are not required to do them for
performance reasons.
Slowing down all conforming code out there because of a few buggy
programs is a bad idea.
BTW: You can also use PTHREAD_MUTEX_RECURSIVE, which is required
similarly to PTHREAD_MUTEX_ERRORCHECK to fail pthread_mutex_unlock
if called on unlocked mutex.

Mutex unlock in cleanup handlers can be written so that you never unlock
an unlocked mutex.
Comment 7 Umesh R. Patil 2004-01-17 14:42:09 EST
I am not implying that normal mutexes do more error checks in 
pthread_mutex_unlock.  

Getting an EBUSY led us down the path of trying to figure out where we
were destroying a locked mutex when the problem was of a different
nature with an extra pthread_mutex_unlock of an unlocked mutex.  

This was the right behavior for a normal mutex but the error code is
misleading.

In glibc's pthread_mutex_unlock.c, mutex->__data.__nusers becomes
UINT_MAX when decremented from a value of 0 and
pthread_mutex_destroy.c reports an EBUSY because
mutex->__data.__nusers is ! 0.  

Would it make sense to prevent mutex->__data.__nusers from being
decremented when it is zero?  This way you don't break compliance with
the spec, the error codes will be correct and undefined behaviors will
remain what they are.  

This is just a suggestion since our code doesn't rely on this behavior...

Note You need to log in before you can comment on or make changes to this bug.