I read about APUE 3rd, 11.6.1 Mutexes, there is an example about lock and unlock a mutex in this chapter:
struct foo {
int f_count;
pthread_mutex_t f_lock;
int f_id;
/* ... more stuff here ... */
};
struct foo *
foo_alloc(int id) /* allocate the object */
{
struct foo *fp;
if ((fp = malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 1;
fp->f_id = id;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
free(fp);
return(NULL);
}
/* ... continue initialization ... */
}
return(fp);
}
void
foo_hold(struct foo *fp) /* add a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
}
void
foo_rele(struct foo *fp) /* release a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
if (--fp->f_count == 0) { /* last reference */
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
} else {
pthread_mutex_unlock(&fp->f_lock);
}
}
In foo_rele
, there is a race condition between pthread_mutex_unlock
and pthread_mutex_destroy
: B thread can call pthread_mutex_lock
between pthread_mutex_unlock
and pthread_mutex_destroy
in A thread which will cause undefined behavior ("Attempting to destroy a locked mutex results in undefined behavior").
Am I right? If I'm right, then, how to make it work right or how to safely and correctly destroy a mutex in Linux using pthread_mutex_destroy
?
It shall be safe to destroy an initialized mutex that is unlocked. Attempting to destroy a locked mutex results in undefined behavior. The pthread_mutex_init() function shall initialize the mutex referenced by mutex with attributes specified by attr.
If you don't, then a typical mutex is held in process memory and will simply cease to exist along with anything that might have access to it when the process terminates.
Yes, it is a blocking call and will block until it gets the lock.
The POSIX spec for pthread_mutex_destroy()
says:
It shall be safe to destroy an initialized mutex that is unlocked.
Which means that if thread B calls pthread_mutex_unlock()
in the else
clause of the if
statement in foo_rele()
then it's safe for thread A to call pthread_mutex_destroy()
because it can only have gotten there after thread B's pthread_mutex_unlock()
call has unlocked the mutex.
All of this is assuming that the reference counting is correct, such that some other thread cannot increment the count from 0 -> 1 after thread A has unlocked the mutex. In other words, at the point where the refcount drops to 0, there can't be another thread that might possibly call foo_hold()
.
APUE mentions this in the explanation right after the example code:
In this example, we have ignored how threads find an object before calling
foo_hold
. Even though the reference count is zero, it would be a mistake forfoo_rele
to free the object’s memory if another thread is blocked on the mutex in a call tofoo_hold
. We can avoid this problem by ensuring that the object can’t be found before freeing its memory. We’ll see how to do this in the examples that follow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With