ロック lint の解析力にも限界はあります。こうした限界の最大の要因は、ロック lint がユーザーの変数の値を知らないという現実にあります。
ロック lint は、ありがちな原因を無視したり、想定を単純化することで、さまざまな種類の問題を解決します。その他の問題も、アプリケーションの中で条件付きでコンパイルされたコードを使用することで回避できます。-Zll オプションを使ってコンパイルを行うと、コンパイラはつねにプリプロセッサマクロ __lock_lint を定義します。このマクロを利用することで、コードがあいまいになることをかなり回避できます。
ロック lint は以下の問題についての推論ができません。
関数ポインタがどの関数を指示しているか。こうした関数の割り当てをロック lint は推論することができません (「declare」を参照) 。関数ポインタに新しい割り当てを追加するには、declare サブコマンドが利用できます。
関数ポインタによる呼び出しに注目する際、ロック lint はその関数ポインタの、可能性のあるすべての値に対応する呼び出し経路をテストします。実行されない呼び出しシーケンスの存在が明らかな場合、あるいはその疑いがある場合は、disallow および reallow サブコマンドを使って、どのシーケンスを実行するかを指定してください。
if (x) pthread_mutex_lock(&lock1);
この場合、2 つの実行パス (1 つはロックを保持し、もう 1 つはロックを保持しない) が作成され、unlock 呼び出しはおそらく副作用メッセージを生成する原因となります。__lock_lint マクロを利用し、ロックは無条件で行われるよう、ロック lint に強制的に扱わせることによって、こうした問題を処理できる場合もあります。
#ifdef __lock_lint pthread_mutex_lock(&lock1); #else if (x) pthread_mutex_lock(&lock1); #endif
以下のようなコードの解析ではロック lint にはなんの問題も生じません。
if (x) { pthread_mutex_lock(&lock1); foo(); pthread_mutex_unlock(&lock1); }
この場合、実行パスは 1 つだけしかなく、そのパスに沿ってロックが獲得および解放され、副作用は起こりません。
構造の要素が使用されているコードにおいて、どの変数およびロックが使用されているか (「ロックの逆転」を参照してください)。
struct foo* p; pthread_mutex_lock(p->lock); p->bar = 0;
配列のどの要素がアクセスされているか。これは前のケースと類似的に扱われます。つまり、インデックスは無視されます。
longjmps に関する一切。
いつループから抜けるか、あるいは再帰を脱出するか (そのため、自身がループしていることが判明するとすぐに、あるいは 1 度の再帰の後、パスに沿った処理は中止されます)。
その他のロック lint の問題点には以下のものがあります。
ロック lint は、相互排他ロックおよび読み取り書き込みロックの使用状況についてのみ解析を行います。ロック lint は、条件変数とともに使用されている相互排他ロックの限られた整合性のチェックを実行します。しかし、ロック lint はセマフォおよび条件変数を、ロックとは認識しません。こうした解析においても、ロック lint がその意味を理解できる対象は限られています。
ロック lint が 2 つの変数が同じ変数である、あるいは 1 つの変数を 2 つの異なる変数であると思い込んでしまう状況があります (「ロックの逆転」を参照)。
(ポインタを介した) スレッド間での自動変数の共有は可能ですが、ロック lint は自動変数は非共有であると想定し、通常はそれらを無視します (ロック lint の対象となるのは、それらが関数ポインタの場合のみです)。
ロック lint は、ロックの副作用に整合性のない関数について不具合を報告します。関数がそうした副作用を持つ持たないに関わらず、ロック lint に単純化したコードを与えるためには、#ifdef およびアサーションが使用されなければなりません。
解析中、ロック lint は rw_upgrade と呼ばれるロック操作についてのメッセージを生成する場合もあります。そうした呼び出しは現実には存在しませんが、ロック lint は以下のようなコードをその次のコードのように書き直します。
if (rw_tryupgrade(&lock1)) { ... }
if () { rw_tryupgrade(&lock1); ... }
rw_tryupgrade() が起こる場所では、ロック lint は常にそれが成功しているものとみなします。
すでに保持されているロックを獲得しようとした場合、ロック lint はエラーとしてフラグを立てます。しかし、ロックに名前未定の場合 (たとえば、foo::lock)、この名前は単独のロックではなく、一連のロックを参照するため、こうしたエラーは抑制されます。しかし、名前未定のロックが常に同じロックを参照する場合は、ロック lint がこの種の潜在的デッドロックを報告できるように、declare one サブコマンドを使ってください。
これらのロックから独自のロックを構築した場合 (たとえば、再帰的な相互排他はときに通常の相互排他から構築されます)、ロック lint はそれらについて知ることはありません。一般的には、#ifdef を使って、通常の相互排他が操作されているかのようにロック lint に提示します。名前未定のロックが再帰的にロックされても、エラーが生成されることはないので、再帰的なロックに対しては、名前未定のロックを使用してください。以下に例を示します。
void get_lock() { #ifdef __lock_lint. struct bogus *p; pthread_mutex_lock(p->lock); #else <the real recursive locking code> #endif }