如何使用 pthread_mutex_destroy 安全正确地销毁 Linux 中的互斥量?

How to safely and correctly destroy a mutex in Linux using pthread_mutex_destroy?

我读过 APUE 3rd, 11.6.1 Mutexes,本章有一个关于锁定和解锁互斥锁的例子:

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);
    }
}

foo_rele中,pthread_mutex_unlockpthread_mutex_destroy之间存在竞争条件:B线程可以调用pthread_mutex_lock 中的 pthread_mutex_unlockpthread_mutex_destroy 线程 将导致未定义的行为 ("Attempting to destroy a locked mutex results in undefined behavior")。

我说得对吗?如果我是对的,那么,如何让它正常工作或如何使用 pthread_mutex_destroy?

安全正确地销毁 Linux 中的互斥量

pthread_mutex_destroy() 的 POSIX 规范说:

It shall be safe to destroy an initialized mutex that is unlocked.

这意味着如果线程 B 在 foo_rele() 中的 if 语句的 else 子句中调用 pthread_mutex_unlock() 那么线程 A 调用 [=10 是安全的=] 因为它只能在线程 B 的 pthread_mutex_unlock() 调用解锁了互斥体后到达那里。

所有这些都假设引用计数是正确的,这样在线程 A 解锁互斥锁后,其他线程无法从 0 -> 1 递增计数。换句话说,在引用计数下降到 0 时,不可能有另一个线程可能调用 foo_hold().

APUE 在示例代码之后的解释中提到了这一点:

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 for foo_rele to free the object’s memory if another thread is blocked on the mutex in a call to foo_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.