プログラムのパフォーマンス解析

ロックの逆転

関数が、呼び出される時点で既に確保されていたロックを開放する場合、その関数は、ロックを逆転するといいます。以下に例を挙げます。


foo() {
    pthread_mutex_unlock(&mtx);
    ...
    pthread_mutex_lock(&mtx);
}

ロックの逆転は、問題となる競合状態の潜在的な原因となります。というのも、ロックの保護の下に行われた観察の結果は、逆転によって無効にされてしまう可能性があるためです。以下の例では、もしも foo()mtx を逆転した場合、その戻り時には、zort_listNULL となるかもしれません (ロックが欠落している間、ほかのスレッドがリストを空白にするかもしれません)。


ZORT* zort_list;
    /* 変数は mtx で保護されている: zort_list */

void f() {
    pthread_mutex_lock(&mtx);
    if (zort_list == NULL) {/* ここで注意 */
        pthread_mutex_unlock(&mtx);
        return;
    }
    foo();
    zort_list->count++; /* しかしここで zort_list は NULL になるかもしれない */
    pthread_mutex_unlock(&mtx);
}

ロックの逆転は以下のコマンドによって見つけることができます。


% lock_lint funcs [directly] inverting lock ...
% lock_lint locks [directly] inverted by func ...

「その関数が行う呼び出しによって逆転されてしまうロックを獲得するのは、どの関数か」この質問は、どの関数が無効なデータを所有する危険性をはらんでいるか、という意味に解釈できます。この質問の答えは、以下の (Bourne シェルの) コードによって得ることができます。


$ LOCKS=`lock_lint locks`
$ lock_lint funcs calling `lock_lint funcs inverting $LOCKS`

以下のコードは、同様の出力を各ロックごとに提供します。


for lock in `lock_lint locks`
do
    echo "functions endangered by inversions of lock $lock"
    lock_lint funcs calling `lock_lint funcs inverting $lock`
done