C语言 pthread_mutex_timedlock and the deadlock



通常情况下,如果一个task1持有锁a,想要获得锁B,而另一个task2已经获得了锁B,正在等待被task1持有的锁a,这将导致死锁。

但是当涉及到pthread_mutex_timmedlock时,它会在指定的超时之后尝试互斥锁或超时。

我遇到了死锁场景,我试图采取定时锁,它最终会超时,这让我感到困惑。

edit:通过更好的设计可以避免死锁,这就是我最终所做的,我确保获得互斥锁的顺序相同,以避免死锁但问题仍然是,如果死锁可以避免,因为我选择了timelock

谁能给我解释一下这种行为?

编辑:附加示例代码以使场景更清晰(实际任务相当复杂,运行到数千行)

T1

pthread_mutex_lock(&lockA);
//call some API, which results in a lock of m2
pthread_mutex_lock(&lockB);
//unlock in the order
pthread_mutex_unlock(&lockB);
pthread_mutex_unlock(&lockA);
T2

pthread_mutex_lock(&lockB);
//call some API, which results in locking m1
pthread_mutex_timedlock(&lockA,<10 sec>); 

崩溃出现在T2的上下文中,bt:

Program terminated with signal 6, Aborted.
#0  0x57edada0 in raise () from /lib/libc.so.6
(gdb) bt
#0  0x57edada0 in raise () from /lib/libc.so.6
#1  0x57edc307 in abort () from /lib/libc.so.6
#2  0x57ed4421 in __assert_fail () from /lib/libc.so.6
#3  0x57bb2a7c in pthread_mutex_timedlock () from /lib/libpthread.so.0

我跟踪错误到以下

pthread_mutex_timedlock: Assertion `(-(e)) != 35 || (kind != PTHREAD_MUTEX_ERRORCHECK_NP && kind != PTHREAD_MUTEX_RECURSIVE_NP)' failed.

在glibc源代码pthread_mutex_timedlock()中,这个断言是这样的:

    int e = INTERNAL_SYSCALL (futex, __err, 4, &mutex->__data.__lock,
                  __lll_private_flag (FUTEX_LOCK_PI,
                          private), 1,
                  abstime);
    if (INTERNAL_SYSCALL_ERROR_P (e, __err))
      {
    if (INTERNAL_SYSCALL_ERRNO (e, __err) == ETIMEDOUT)
      return ETIMEDOUT;
    if (INTERNAL_SYSCALL_ERRNO (e, __err) == ESRCH
        || INTERNAL_SYSCALL_ERRNO (e, __err) == EDEADLK)
      {
        assert (INTERNAL_SYSCALL_ERRNO (e, __err) != EDEADLK
            || (kind != PTHREAD_MUTEX_ERRORCHECK_NP
            && kind != PTHREAD_MUTEX_RECURSIVE_NP));
        /* ESRCH can happen only for non-robust PI mutexes where
           the owner of the lock died.  */
        assert (INTERNAL_SYSCALL_ERRNO (e, __err) != ESRCH
            || !robust);

e == EDEADLKkind可能是PTHREAD_MUTEX_ERRORCHECK_NPPTHREAD_MUTEX_RECURSIVE_NP。另一件要注意的事情是,超时是在这个检查之前处理的,也就是说,你没有达到超时。

在内核中是futex_lock_pi_atomic()返回EDEADLK代码:

 /*
  * Detect deadlocks.
  */
 if ((unlikely((curval & FUTEX_TID_MASK) == vpid)))
         return -EDEADLK;
 /*

上面的代码比较了已经锁定互斥锁的线程的TID和试图获取互斥锁的线程的TID。如果它们是相同的,则表明线程正在尝试获取它已经获得的互斥锁。

首先,timeout指定的时间是多少?它大吗?

pthread_mutex_timmedlock在三种情况下失败1>检测到死锁条件或当前线程已经拥有互斥锁。2>无法获取互斥锁,因为已经超过了互斥锁的最大递归锁数。3>互斥锁指定的值不指向已初始化的互斥锁对象。

是您的代码受到上述任何一种影响。

代码片段也可以帮助我们清楚地看到问题。

最新更新