c - pthread_mutex_timedlockとデッドロック

c linux posix

通常、タスク1がロックAを保持し、ロックBを取得したいが、別のタスク2がロックBを取得し、タスク1)が保持するロックAを待機している場合、デッドロックが発生します。

しかし、pthread_mutex_timedlockに関しては、指定されたタイムアウトの後で、mutexロックまたはタイムアウトを試みます。

私は、最終的にタイムアウトになるはずの時限ロックを取得しようとしていたデッドロックシナリオにぶつかりました。

編集:デッドロックは、より良い設計をすることで回避できます。これは私がやったことですが、デッドロックを回避するために、mutexロックを取得する順序が同じであることを確認しました
しかし、私はタイムロックを選択したのでデッドロックを回避できるかどうかについての質問は未解決のままです

誰かがこの動作を説明できますか?

編集:シナリオをより明確にするためにサンプルコードを添付します(実際のタスクはかなり複雑で数千行に及ぶ)

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 == EDEADLKkindPTHREAD_MUTEX_ERRORCHECK_NPまたはPTHREAD_MUTEX_RECURSIVE_NPのどちらかです。もう1つの注意点は、タイムアウトはこのチェックの前に処理されることです。つまり、タイムアウトに達しません。

カーネルではfutex_lock_pi_atomic()がEDEADLKコードを返します。

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

 /*


上記の部分は、ミューテックスをロックしたスレッドのTIDとミューテックスを取得しようとするスレッドのTIDを比較します。それらが同じである場合、スレッドがすでに取得しているミューテックスを取得しようとしていることを示しています。
関連記事

c - リアルタイム信号の意義は?

c - gmpの配列(初期化)

c - ANSI警告なしでstdoutをファイルにリダイレクトする

c - Maven2 +ネイティブMavenプラグイン

c - HC-128 / 256をサポートするC暗号ライブラリ?

c - SWIGを使用してオプションの引数を持つTCL関数を作成する方法

c - 何らかのファイルアロケーションテーブルを使用してEEPROMに構造体を保存する

c - Ansi CのSOAPリクエストメッセージ

c - Solarisスレッドの最大数

c - Autotoolsを使用してライブラリをプログラムにリンクするには、Makefile.amとソースに何を表示する必要がありますか?