在 EOWNERDEAD 状态互斥锁上的 Trylock

map*_*_88 5 c mutex pthreads

当我尝试使用 'trylock' 锁定互斥锁时,我发现了意外行为,而前任所有者已死而未解锁。
第一个使用 'trylock' 的进程按预期获得 EOWNERDEAD 状态,因此使用 'unlock' 函数来释放互斥锁:

lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
{
    pthread_mutex_unlock(mutex);
    printf("P1 mutex status: EOWNERDEAD\n");
}
else if (lock_status == ENOTRECOVERABLE)
    {printf("P1 mutex status: ENOTRECOVERABLE\n");}
else if (lock_status == EBUSY)
    {printf("P1 mutex status: EBUSY\n");}
else {printf("P1 mutex status: %d\n", lock_status);}
Run Code Online (Sandbox Code Playgroud)

第二个执行相同的代码,如预期的那样获得 ENOTRECOVERABLE 状态。
但是当第三个执行相同的操作时会获得 EBUSY 状态,这是出乎意料的。
状态应该是 ENOTRECOVERABLE,正如这里报告的那样
我尝试使用 'lock' 函数而不是 'trylock',并返回了正确的状态。
这是一个错误吗?出于我的目的,我必须在执行任何操作之前检查互斥锁状态,因此我无法使用“锁定”功能,否则可能会发生死锁。
当 ENOTRECOVERABLE 状态返回时,我想销毁互斥锁​​:

else if (lock_status == ENOTRECOVERABLE)
{
    pthread_mutex_destroy(mutex);
    printf("P1 mutex status: ENOTRECOVERABLE\n");
}
Run Code Online (Sandbox Code Playgroud)

但也许有比这种极端解决方案更好的方法,是吗?完整代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

main(void)
{
int lock_status, mutex_fdesc;
pid_t process_id;
pthread_mutex_t* mutex;
pthread_mutexattr_t m_att;

//Process 1 die with locked mutex
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
    usleep(100000);
    mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
    mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
                PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
    close(mutex_fdesc);
    pthread_mutex_lock(mutex);
    exit(0);
}
///Process 2 try to lock mutex and gets EOWNERDEAD then make an unlock
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
    usleep(200000);
    mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
    mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
                PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
    close(mutex_fdesc);
    lock_status = pthread_mutex_trylock(mutex);
    if (lock_status == EOWNERDEAD)
        {
        pthread_mutex_unlock(mutex);
        printf("P2 mutex status: EOWNERDEAD\n");}
    else if (lock_status == ENOTRECOVERABLE)
        {printf("P2 mutex status: ENOTRECOVERABLE\n");}
    else if (lock_status == EBUSY)
        {printf("P2 mutex status: EBUSY\n");}
    else {printf("P2 mutex status: %d\n", lock_status);}
    exit(0);
}
///Process 2 try to lock mutex and gets ENOTRECOVERABLE then do nothing
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
    usleep(400000);
    mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
    mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
                PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
    close(mutex_fdesc);
    lock_status = pthread_mutex_trylock(mutex);
    if (lock_status == EOWNERDEAD)
        {
        pthread_mutex_unlock(mutex);
        printf("P3 mutex status: EOWNERDEAD\n");}
    else if (lock_status == ENOTRECOVERABLE)
        {printf("P3 mutex status: ENOTRECOVERABLE\n");}
    else if (lock_status == EBUSY)
        {printf("P3 mutex status: EBUSY\n");}
    else {printf("P3 mutex status: %d\n", lock_status);}
    exit(0);
}

mutex_fdesc = shm_open("/mutex", O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG);
ftruncate(mutex_fdesc, sizeof(pthread_mutex_t));
mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
            PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
close(mutex_fdesc);
pthread_mutexattr_init(&m_att);
pthread_mutexattr_setpshared(&m_att, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&m_att, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(mutex, &m_att);
pthread_mutexattr_destroy(&m_att);
///Parent process try to lock the mutex and gets EBUSY
usleep(800000);
lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
        {printf("Pparent mutex status: EOWNERDEAD\n");}
    else if (lock_status == ENOTRECOVERABLE)
        {printf("Pparent mutex status: ENOTRECOVERABLE\n");}
    else if (lock_status == EBUSY)
        {printf("Pparent mutex status: EBUSY\n");}
    else {printf("Pparent mutex status: %d\n", lock_status);}

pthread_mutex_destroy(mutex);
munmap((void*)mutex, sizeof(pthread_mutex_t));
shm_unlink("/mutex");
wait(NULL);
wait(NULL);
wait(NULL);
exit(0);
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ger 0

EOWNERDEAD我认为您所看到的是,当互斥体返回时(这对于恢复语义是必要的,因此只有一个线程尝试恢复)以及它返回时,锁定互斥体的尝试被认为是成功的ENOTRECOVERABLE,我同意这一点令人惊讶,并且似乎与文档相反。文档说,如果接收线程EOWNERDEAD在解锁之前没有使互斥锁保持一致,那么所有后续锁定它的尝试都将返回ENOTRECOVERABLE。我在文档中没有看到任何内容表明这不应该适用pthread_mutex_trylock()pthread_mutex_lock().

如果您想将其报告为错误,那么您可以针对提供 pthreads 实现的任何库进行报告。这至少会因操作系统而异。

出于我的目的,我必须在执行任何操作之前检查互斥体状态,因此我无法使用“锁定”功能,否则可能会发生死锁。

听起来很可疑。您已经在使用强大的互斥锁,否则您一开始就不会获得EOWNERDEAD或。即使不是完全不合适,也可能是矫枉过正。如果在锁定互斥锁失败的情况下可以做有用的工作,则可以使用它。也许你的情况有,但那是不寻常的。例如,如果您只是在重试之前延迟,那么您最好一开始就使用。ENOTRECOVERABLEpthread_mutex_trylock()pthread_mutex_lock()