ロック lint は、スレッドやロックに関連したいくつかのアサーションを認識します。(詳細については、「assert」のマニュアルページを参照してください)。
アサーションは、ステートメントが許可されている関数定義の内側でのみ作成が可能です。
ASSERT() はカーネルおよびドライバコードにおいて利用される一方、assert() はユーザー (アプリケーション) コードで利用されます。ただし、このマニュアルでは、特にことわり書きがない限り、いずれに対しても assert() を使用しています。
assert(NO_LOCKS_HELD);
このアサーションによって、コードのこのポイントに到達したなら、このテストを実行するスレッドによってロックは保持されるべきでないと、ロック lint は認識します。このアサーションに対する違反は、解析中に報告されます。ブロックを行うルーチンは、スレッドがブロックまたは終了するときには、保持されるロックがないように、このようなアサーションを使用できます。
また、このアサーションには、獲得されたいかなるロックもその位置では必ず解放させることを、コードを修正しようとする第三者に思い出させる働きもあります。
唯一必要なことは、このアサーションを、ブロックするリーフレベルの関数で使用することです。ブロックするほかの関数を呼び出すためだけに、呼び出す側の関数がブロックするのであれば、呼び出される側の関数のみにアサーションを含めておくだけで呼び出す側に含める必要はありません。そのため、このアサーションは、特にロック lint 用に記述されたライブラリのバージョン (lint ライブラリのように) において (libc など) もっとも頻繁に出現することになるでしょう。
ファイル synch.h は、まだ別途定義されていない場合は、NO_LOCKS_HELD を 1 と定義し、このアサーションが適合するようにします。つまり、実行時にはアサーションは効果的に無視されます。note.h または synch.h のいずれかをインクルードする前に (順番はどちらが先でもかまいません)、NO_LOCKS_HELD を定義することによって、デフォルトの実行時の状態を上書きできます。たとえば、コードの本体に a と b と呼ばれる 2 つのロックだけが使用されている場合は、以下の定義で十分でしょう。
#define NO_LOCKS_HELD (!MUTEX_HELD(&a) && !MUTEX_HELD(&b)) #include <note.h> #include <synch.h>
このようにしても、ロック lint によるアサーションのテストに影響はありません。ほかのロックが保持されていれば (a と b だけでなく)、ロック lint はその件について報告を行います。
assert(NO_COMPETING_THREADS);
このアサーションにより、コードのこのポイントに到達したなら、このコードを実行中のスレッドと競合するスレッドがほかにあってはいけないと、ロック lint は認識します。このアサーションに対する違反は (特定の NOTE 形式のアサーションによって提供される情報に基づき)、解析中に報告されます。保護用のロックを保持せずに変数にアクセスする関数は (同じデータにアクセスする関連スレッドはほかにはないとの想定の下で動作しています)、必ずそのようにマークされなければなりません。
デフォルトでは、このアサーションは実行時に無視されます (つまり、いつも成功するということです)。どのスレッドが競合するかという概念にはアプリケーションの情報が含まれるため、NO_COMPETING_THREADS は実行時についての一般的な意味を持つものではありません。たとえば、同じデバイスに対するドライバにおいて、実行中のスレッドがほかに存在しないことを示すため、こうしたアサーションを作成します。一般的な意味ではないため、synch.h は、まだ別途定義されていないならば、NO_COMPETING_THREADS を 1 と定義します。
しかし、note.h または synch.h のいずれかをインクルードする前に (インクルードする順番はどちらが先でもかまいません)、NO_COMPETING_THREADS を定義することによって、デフォルトの意味を上書きできます。たとえば、プログラムが num_threads という変数に実行中のスレッドの数のカウントを保存する場合は、以下の定義で十分です。
#define NO_COMPETING_THREADS (num_threads == 1) #include <note.h> #include <synch.h>
このようにしても、ロック lint によるアサーションのテストに影響はありません。
assert(MUTEX_HELD(lock_expr) && ...);
このアサーションは、カーネル内において広く利用されます。アサーションが有効状態にある場合は、実行時チェックを行います。同じ機能はユーザーコードにも存在します。
このコードは、ロック lint の解析中、有効なアサーションとともにコードが実際に実行されているときほぼ同じことを行います。つまり、実行中のスレッドが指定されたとおりのロックを保持しない場合、エラーを報告します。
スレッドライブラリは、いずれかのスレッドがロックを取得することをチェックするのみという、緩やかなテストを行います。一方、ロック lint はもっと強力なテストを実行します。
ロック lint は、MUTEX_HELD()、RW_READ_HELD()、RW_WRITE_HELD()、および RW_LOCK_HELD() マクロの使用、そしてその否定を認識します。こうしたマクロ呼び出しは、&& 演算子によって組み合わせることができます。たとえば、以下のアサーションで、ロック lint は、相互排他ロックが保持されておらず、読み取り書き込みロックが書き込み用に保持されていることをチェックします。
assert(p && !MUTEX_HELD(&p->mtx) && RW_WRITE_HELD(&p->rwlock));
ロック lint は以下のような式も認識します。
MUTEX_HELD(&foo) == 0