総合手引 | セクション 9 | English | オプション |
#include <sys/types.h>
#include <sys/systm.h>
typedef void timeout_t (void *);
struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle)
関数 callout_handle_init() はハンドルを初期化するために使用し、untimeout と共に 使用されても副作用無しに戻るようにします。
コールアウトハンドルに CALLOUT_HANDLE_INITIALIZER() の値を割当てることは、 callout_handle_init() と同様の機能を実行し、静的な宣言またはグローバルなコールアウトハンドルで 使用するために提供されています。
関数 untimeout() は、そのハンドルの正当性を確認するために func および arg 引数を使用して、 handle に関連付けられた timeout を取り消します。 そのハンドルが引数 arg を取る関数 func を持つ timeout と一致しない場合には、何も行いません。 handle は untimeout() に渡される前に以前の timeout(), callout_handle_init() の呼び出し、または CALLOUT_HANDLE_INITIALIZER(&handle) の値の割当てによって初期化されなければなりません。 以前に初期化されたハンドルを伴なわない untimeout の呼び出しの振る舞いは 未定義です。 untimeout() の呼び出しは古いスタイルで、新しいコードは callout_*() 関数を使用するべきです。
ハンドルがシステムによって再利用されるので、 両方の呼び出しが同じ関数のポインタおよび引数を使用し、2 番目の呼び出しの前に 最初の timeout が終了するか取り消された場合には、1 つの timeout() の実行からのハンドルが別の timeout() の実行のハンドルが一致することができることが (意外ではあるが) 可能です。 timeout の設備は timeout() および untimeout() のための O(1) 実行時間を提供します。 timeout (訳注: および untimeout) は Giant ロックが保持されている状態で、 softclock() から実行されます。 従って、再入から保護されます。
関数 callout_init(), callout_stop(), callout_drain() および callout_reset() は固有のコールアウト構造を割当てることを希望するクライアントのための、 低レベルのルーチンです。
関数 callout_init() はコールアウトを初期化し、そのためそのコールアウトは何の副作用もなしに callout_stop(), callout_drain() または callout_reset() に渡されることができます。 mpsafe 引数が 0 の場合には、callout 構造体は "マルチプロセッサセーフ" であるとはみなされません。 すなわち、ジャイアントロックがコールアウト関数の呼出し前に 獲得され、コールアウト関数が戻るときに解放されるようにします。
関数 callout_stop() は、そのコールアウトが現在保留中の場合には、コールアウトを取り消します。 コールアウトが保留中の場合には、 callout_stop() は 0 でない値を返します。 コールアウトが設定されていないか既に実行されているか現在実行中の場合には、 0 が返されます。 この関数が呼び出されるとき、コールアウトが Giant ミューテックス (mutex) によって保護されている場合には、 Giant を保持していなければなりません。
関数 callout_drain() は、コールアウトが既に進行中の場合にはその完了をウェイトすることを除いて、 callout_stop() と同一です。 この関数は、そのコールアウトがブロックするかもしれないあらゆるロックを 保持している間は、決して呼び出されてはなりません。 さもないと結果としてデッドロックします。 コールアウトサブシステムが既にこのコールアウトを処理し始めたなら コールアウト関数が callout_drain() の実行の間に呼び出されるかもしれないことに注意してください。 しかしながら、コールアウトサブシステムは、 callout_drain() が返る前にコールアウトが完全に停止されることを保証します。
関数 callout_reset() は最初にそのコールアウトを廃止するために callout_stop() と同様のことを実行し、それから新しいコールアウトを timeout() と同じ流儀で確立します。 この関数が呼び出されるとき、コールアウトが Giant ミューテックスによって保護される場合には、 Giant は保持されなければなりません。
マクロ callout_pending(), callout_active() および callout_deactivate() はコールアウトの現在の状態へのアクセスを提供します。 これらのマクロを慎重に使用すれば、非同期タイマ機構に 特有の多くの競合条件を避けることができます。 さらなる詳細については下記の 競合条件を回避する を参照してださい。 callout_pending() マクロは、コールアウトが 保留中 であるかどうかチェックします。 コールアウトはタイムアウトが設定されているが時間がまだ到着していない時、 保留中 であると見なされます。 いったんタイムアウト時間が来て、コールアウトサブシステムがこのコールアウト を処理し始めれば、 callout_pending() はたとえコールアウト関数が実行を終了して (または、始めて) いなくても FALSE を返すことに注意してください。 callout_active() マクロはコールアウトが アクティブ としてマークされているかどうかチェックし、 callout_deactivate() マクロはコールアウトの アクティブ フラグをクリアします。 コールアウトサブシステムは、タイムアウトが設定されているコールアウトを アクティブ に設定し、 callout_stop() と callout_drain() では アクティブ をクリアしますが、 コールアウト関数の実行を通して 通常どおりコールアウトの期限が切れた場合には、クリア しません 。
コールアウトサブシステムはこれらの同期関係に対処するために 多くのメカニズムを提供します。
if (sc->sc_flags & SCFLG_CALLOUT_RUNNING) { if (callout_stop(&sc->sc_callout)) { sc->sc_flags &= ~SCFLG_CALLOUT_RUNNING; /* successfully stopped */ } else { /* * callout has expired and callout * function is about to be executed */ } }
callout_reset() がコールアウトを停止したかどうか決定するための 同等なメカニズムがなにもないことに注意してください。
callout_pending() が TRUE を返す場合、 コールアウト関数は、最初に 保留中 フラグをチェックして動作なしで戻るべきです。 これは、コールアウトがコールアウト関数が呼び出される直前に callout_reset() を使用して再スケジュールされたことを示します。 callout_active() が FALSE を返す場合、コールアウト関数は同様に動作なしで返るべきです。 これは、コールアウトが停止されたことを示します。 最後に、コールアウト関数は、 アクティブ フラグをクリアするために callout_deactivate() を呼び出すべきです。 例えば:
mtx_lock(&sc->sc_mtx); if (callout_pending(&sc->sc_callout)) { /* callout was reset */ mtx_unlock(&sc->sc_mtx); return; } if (!callout_active(&sc->sc_callout)) { /* callout was stopped */ mtx_unlock(&sc->sc_mtx); return; } callout_deactivate(&sc->sc_callout); /* rest of callout function */
上記で使用したミューテックスのような適切な同期をともに使うことにより、 このアプローチは callout_stop() と callout_reset() 関数がいつも競合なしで使用できます。 例えば:
mtx_lock(&sc->sc_mtx); callout_stop(&sc->sc_callout); /* The callout is effectively stopped now. */
コールアウトがまだ保留中である場合、これらの関数は通常どおり動作しますが、 コールアウトの処理が既に始まっている場合、 コールアウト関数におけるテストにおいて、これらの関数はさらなる 動作なしで戻ることになります。 コールアウト関数と他のコードの間の同期は、コールアウト関数が callout_deactivate() 呼び出しを終えるまでコールアウトの停止やリセットが行われないことを 確実にします。
さらに、上記のテクニックは、 アクティブ フラグが実際にコールアウトが有効か無効かを反映することを確実にします。 callout_active() が FALSE を返した場合、たとえコールアウトサブシステムが 実際にコールアウト関数を開始しようとしていたとしても、 コールアウト関数は動作なしに終了してしまうので、 実質的に無効化されています。
最後に、コールアウトを停止しようとしているときに 考慮しなければならない最後の競合条件が 1 つあります。 この場合、既に破壊されるかまたは再利用されたデータオブジェクトに アクセスする必要があるかもしれないので、 コールアウト関数自体に 停止されたコールアウトを検出するさせるために安全でないかもしれません。 コールアウトが完全に終了したことを保証するためには、 callout_drain() 呼び出しを使用しなければなりません。
TIMEOUT (9) | February 6, 2005 |
総合手引 | セクション 9 | English | オプション |
このマニュアルページサービスについてのご意見は Ben Bullock にお知らせください。 Privacy policy.
“ | Today, the Unix equivalent of a power drill would have 20 dials and switches, come with a nonstandard plug, require the user to hand-wind the motor coil, and not accept 3/8" or 7/8" drill bits (though this would be documented in the BUGS section of its instruction manual). | ” |
— The Unix Haters' handbook |