EN JA
TIMEOUT(9)
TIMEOUT(9) FreeBSD Kernel Developer's Manual TIMEOUT(9)

名称

timeout, untimeout, callout_handle_init, callout_init, callout_init_mtx, callout_init_rm, callout_init_rw, callout_stop, callout_drain, callout_reset, callout_reset_on, callout_reset_curcpu, callout_reset_sbt, callout_reset_sbt_on, callout_reset_sbt_curcpu, callout_schedule, callout_schedule_on, callout_schedule_curcpu, callout_pending, callout_active, callout_deactivate指定された時間の後に関数を実行する

書式

#include < sys/types.h>
#include < sys/systm.h>

typedef void timeout_t (void *);


struct callout_handle
timeout( timeout_t *func, void *arg, int ticks);

void
callout_handle_init( struct callout_handle *handle);

struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle);


void
untimeout( timeout_t *func, void *arg, struct callout_handle handle);

void
callout_init( struct callout *c, int mpsafe);

void
callout_init_mtx( struct callout *c, struct mtx *mtx, int flags);

void();

callout_init_rm( struct callout *c, struct rmlock *rm, int flags);

void
callout_init_rw( struct callout *c, struct rwlock *rw, int flags);

int
callout_stop( struct callout *c);

int
callout_drain( struct callout *c);

int
callout_reset( struct callout *c, int ticks, timeout_t *func, void *arg);

int
callout_reset_on( struct callout *c, int ticks, timeout_t *func, void *arg, int cpu);

int
callout_reset_sbt_on( struct callout *c, sbintime_t sbt, sbintime_t pr, timeout_t *func, void *arg, int cpu, int flags);

int
callout_reset_curcpu( struct callout *c, int ticks, timeout_t *func, void *arg);

int
callout_schedule( struct callout *c, int ticks);

int
callout_schedule_on( struct callout *c, int ticks, int cpu);

int
callout_schedule_curcpu( struct callout *c, int ticks);

int
callout_pending( struct callout *c);

int
callout_active( struct callout *c);

callout_deactivate( struct callout *c);

解説

関数 timeout() は、 ticks/hz 秒後に行われる引数 func によって与えられた関数への呼び出しをスケジュールします。 ticks の正でない値は、値‘1’に黙って変換されます。 func は、 void * の引数を取る関数へのポインタであるべきです。呼び出し時に、 func は、ただ 1 つの引数として arg を受け取ります。 timeout() からの返り値は、スケジュールされたタイムアウトを取り消すことを要求する untimeout() 関数とともに使用することができる struct callout_handle です。 timeout() 呼び出しは、古いスタイルで、新しいコードは、 callout_*() 関数を使用するべきです。

副作用なしで返るためにハンドルをつけて untimeout() への任意の呼び出しを引き起こす状態へハンドルを初期化するために関数 callout_handle_init() を使用することができます。

CALLOUT_HANDLE_INITIALIZER() の値にコールアウトハンドルを割り当てることは、 callout_handle_init() と同じ機能を実行し、静的な宣言またはグローバルなコールアウトハンドルで使用するために提供されます。

関数 untimeout() は、ハンドルの正当性を確認するために funcarg 引数を使用して、 handle に関連付けられたタイムアウトを取り消します。ハンドルが引数 arg を取る関数 func とタイムアウトに対応していないなら、何も実行されません。 handle は、 untimeout() に渡される前に timeout(), callout_handle_init() への以前の呼び出し、または CALLOUT_HANDLE_INITIALIZER( &handle) の値を割り当てることによって初期化されなければなりません。初期化されていないハンドルで untimeout() を呼び出す振る舞いは、未定義です。 untimeout() 呼び出しは、古いスタイルで、新しいコードは、 callout_*() 関数を使用するべきです。

ハンドルがシステムによって再利用されるとして、両方の呼び出しが同じ関数ポインタと引数を使用し、最初のタイムアウトが 2 番目の呼び出しの前に、期限が切れるか、または取り消されるなら、 timeout() の 1 つの呼び出しからのハンドルが timeout() の別の呼び出しのハンドルと (ありそうもありませんが) 一致する可能性があります。タイムアウト機能は、 timeout() と untimeout() のために O(1) 実行時間を提供します。タイムアウトは、 Giant ロックを保持した状態で、 softclock() から実行されます。したがって、それらは、再入可能性 (re-entrancy) から保護されます。

関数 callout_init(), callout_init_mtx(), callout_init_rm(), callout_init_rw(), callout_stop(), callout_drain(), callout_reset() と callout_schedule() は、それら自体のコールアウト構造体を割り付けたいクライアントのための低レベルのルーチンです。

関数 callout_init() は、コールアウトを初期化するので、何の副作用なしに callout_stop(), callout_drain(), callout_reset() または callout_schedule() に渡すことができます。 mpsafe 引数が 0 であるなら、コールアウト構造体は、“マルチプロセッサセーフ” (multi-processor safe) であるとみなされません。すなわち、Giant ロックは、コールアウト関数を呼び出す前に獲得され、コールアウト関数が返るとき、解放されます。

callout_init_mtx() 関数は、 callout_init() に代わるものとして使用されます。パラメータ mtx は、コールアウト関数を呼び出す前にコールアウトサブシステムによって獲得され、コールアウト関数が返るとき、解放されるミューテックス (mutex) を指定します。次の flags が指定されます:

CALLOUT_RETURNUNLOCKED
コールアウト関数は、 mtx 自体を解放するので、コールアウトサブシステムは、コールアウト関数が返った後にそのアンロックを試みるべきではありません。

callout_init_rw() と callout_init_rm() 関数は、コールアウトに関連する rwlock と rmlock を使用する必要に対応します。関数は、特別の rw または rm 引数を指定する可能性がある callout_init() と同じです。 rm 引数が指定されるなら、ロックは、 RM_SLEEPABLE フラグを渡さずに作成されるべきです。コールアウトハンドラは、softclock swi で実行するので、それらは、スリープすることも、sx または lockmgr のようなスリープ可能なロックを獲得することもできないので、使用可能なロッククラスは、現在ミューテック、rwlock とスリープ可能でない rmlock に制限されます。次の flags が指定されます:

CALLOUT_SHAREDLOCK
ロックは、コールアウトハンドラを実行するときのみ、読み込みモードで獲得されます。 mtx に関連して使用されるとき、効果がありません。

関数 callout_stop() は、現在、保留中 (pending) であるなら、コールアウトを取り消します。コールアウトが保留中であるなら、 callout_stop() は、0 以外の値を返します。コールアウトが設定されていなくて、既にサービスされているか、または現在サービス中であるなら、0 が返されます。コールアウトに関連するミューテックスがあるなら、そのミューテックスは、この関数が呼び出されるとき、保持されなければなりません。

関数 callout_drain() は、コールアウトが既に進行中であるなら、完了をウェートすることを除いて、 callout_stop() と同一です。この関数は、コールアウトがブロックするかもしれないあらゆるロックを保持している間に、呼び出されてはなりません、そうでなければ、デッドロックの結果となります。コールアウトサブシステムが既に、このコールアウトを処理し始めたなら、コールアウト関数が callout_drain() の実行の間に呼び出されるかもしれないことに注意してください。しかしながら、コールアウトサブシステムは、 callout_drain() が返る前にコールアウトが完全に停止されることを保証します。

関数 callout_reset() は、最初にコールアウトを廃止するために callout_stop() と同等のことを実行し、次に、 timeout() と同じ方法で新しいコールアウトを確立します。既に、保留中のコールアウトがあり、それが再スケジュールされたなら、 callout_reset() は、0 以外の値を返します。コールアウトに関連しているミューテックスがあるなら、この関数が呼び出されるとき、そのミューテックスは、保持されなければなりません。関数 callout_schedule() は、新しい期間のために既存のコールアウトを (再) スケジュールします。 (できるかぎり低いオーバヘッドですが) コールアウト構造体から抽出された funcarg パラメータを付けて callout_reset() を呼び出しすことと同等です。

関数 callout_reset_on() と callout_schedule_on() は、 callout_reset() と callout_schedule() と同等ですが、コールアウトのためのターゲット CPU を指定する特別なパラメータを取ります。

関数 callout_reset_sbt_on() によって、相対的なチック (tick) のカウントの代わりに、相対的か、または絶対的な時間と正確さを取り、より高い時間の解像度を取得することができます。指定された時間が過ぎるなら、それは、できるだけ早くハンドラを実行するために静かに変換されます。

次の flags が指定されます:

C_ALSOLUTE
ブート以後のイベントの絶対時間として sbt 引数を操作します、そうでなければ、相対的な時間として操作します。
C_DIRECT_EXEC
softclock swi の代わりにハードウェア割り込みコンテキストから直接ハンドラを実行します。それは、より速くなりますが、ハンドラでより多くの制約の状態にします。ハンドラは、ロックのために spin mutex (ミューテック) だけを使用し、絶対的な優先度で実行するので、それらは、速いに違いはずです。
C_PREL()
受け付け可能な時間の偏差によって割られた時間の間隔の 2 進法の対数として相対的なイベント時間の正確さを指定します: 1 -- 1/2, 2 -- 1/4, その他。より小さな値によって、処理のオーバヘッドと電力消費量を縮小するために 1 つのタイマの割り込みのより多くのイベントを集めることができます。

関数 callout_reset_curcpu() と callout_schedule_curcpu() は、ターゲット CPU として現在の CPU を使用する callout_reset_on() と callout_schedule_on() のためのラッパ (wrapper) です。

マクロ callout_pending(), callout_active() と callout_deactivate() は、コールアウトの現在の状態へのアクセスを提供します。これらのマクロの注意深い使用は、非同期タイマ機能に特有である競合条件の多くを回避することができます。さらなる詳細については、下記の 競合条件を回避する を参照してください。 callout_pending() マクロは、コールアウトが 保留中 であるかどうかチェックします。コールアウトは、タイムアウトが設定されているが、時間にまだ到着していないとき、 保留中 であると見なされます。いったんタイムアウト時間に到着し、コールアウトサブシステムは、このコールアウトを処理し始め、 callout_pending() は、たとえコールアウト関数が実行を終了して (または、始められて) いなくても、 FALSE を返すことに注意してください。 callout_active() マクロは、コールアウトが アクティブ (active) としてマークされるかどうかチェックし、 callout_deactivate() マクロは、コールアウトの アクティブ (active) なフラグをクリアします。コールアウトサブシステムは、タイムアウトが設定されるとき、 アクティブ としてコールアウトをマークし、 callout_stop() と callout_drain() の アクティブ フラグをクリアしますが、通常、コールアウトがコールアウト関数の実行を通して期限切れになるとき、クリア しません

競合条件を回避する

コールアウトサブシステムは、それ自体のタイマのコンテキストからコールアウト関数を呼び出します。ある種類の同期なしで、コールアウト関数は、別のスレッドによってコールアウトを停止するか、またはリセットする試みと同時に、呼び出される可能性があります。特に、コールアウト関数は、一般的に、それらの最初の動作としてミューテックスを獲得するので、コールアウト関数は、既に呼び出されているかもしれませんが、別のスレッドがコールアウトをリセットするか、または停止することを試みる時点で、そのミューテックスを待ってブロックされるかもしれません。

コールアウトサブシステムは、これらの同期関連に対応するために、次の多くのメカニズムを提供しています:

  1. コールアウトに、 callout_init_mtx() 関数 (または、 FALSE に設定された mpsafe を付けて callout_init() を使用して、 Giant ミューテックスとして暗黙のうちに指定される) を使用して指定された、関連するミューテックスがあるなら、このミューテックスは、競合条件を回避するために使用されます。関連するミューテックスは、 callout_stop() または callout_reset() を呼び出す前に、呼び出し側によって獲得されなければなりませし、コールアウトが予想通りに正確に停止されるか、またはリセットされることが保証されます。コールアウトまたはその関連するミューテックスを破壊する前に、 callout_drain() を使用する必要がまだあることに注意してください。
  2. callout_stop() と callout_reset() からの返り値は、コールアウトが削除されたかどうかを示します。コールアウトが設定され、コールアウト関数がまだ実行されていないことが知られているなら、 FALSE の返り値は、コールアウト関数が呼び出されよとしていることを示します。例えば:

    if (sc->sc_flags & SCFLG_CALLOUT_RUNNING) { 
     if (callout_stop(&sc->sc_callout)) { 
      sc->sc_flags &= ~SCFLG_CALLOUT_RUNNING; 
      /* 成功して停止しました */ 
     } else { 
      /* 
       * コールアウトの期限が切れた, 
       * コールアウト関数は, 
       * 実行されようとしている 
       */ 
     } 
    }
  3. 競合条件を対処するために callout_pending(), callout_active() と callout_deactivate() マクロを一緒に使用することができます。コールアウトのタイムアウトが設定されるとき、コールアウトサブシステムは、 アクティブ保留中 の両方としてコールアウトをマークします。タイムアウトの時間に到着するとき、コールアウトサブシステムは、 保留中 フラグを最初にクリアすることによってコールアウトを処理し始めます。次に、 アクティブ フラグを変更しないで、コールアウト関数を呼び出し、コールアウト関数が返った後でさえ、 アクティブ フラグをクリアしません。ここで説明されたメカニズムは、 callout_deactivate() マクロを使用して、 アクティブ フラグをクリアすることをコールアウト関数自体に要求します。 callout_stop() と callout_drain() 関数は、返る前に アクティブ保留中 フラグの両方を常にクリアします。

    コールアウト関数は、最初に 保留中 フラグをチェックし、 callout_pending() が TRUE を返すなら、動作なしで返るべきです。これは、コールアウト関数が呼び出される直前に、コールアウトが callout_reset() を使用して再スケジュールされたことを示します。 callout_active() が FALSE を返すなら、コールアウト関数は、同様に動作なしで返るべきです。これは、コールアウトが停止されたことを示します。最後に、コールアウト関数は、 アクティブ フラグをクリアするために callout_deactivate() を呼び出すべきです。例えば:

    mtx_lock(&sc->sc_mtx); 
    if (callout_pending(&sc->sc_callout)) { 
     /* コールアウトは, リセットされた */ 
     mtx_unlock(&sc->sc_mtx); 
     return; 
    } 
    if (!callout_active(&sc->sc_callout)) { 
     /* コールアウトは, 停止された */ 
     mtx_unlock(&sc->sc_mtx); 
     return; 
    } 
    callout_deactivate(&sc->sc_callout); 
    /* コールアウト関数のリセット */

    上記で使用されるミューテックスのような適切な同期とともに、このアプローチは、競合なしでいつでも使用できるように callout_stop() と callout_reset() 関数に許可します。例えば:

    mtx_lock(&sc->sc_mtx); 
    callout_stop(&sc->sc_callout); 
    /* コールアウトは, 今, 事実上停止されます. */

    コールアウトがまだ保留中であるなら、これらの関数は、正常に動作しますが、コールアウトの処理が既に始まっているなら、コールアウト関数のテストによってさらなる動作なしで返ります。コールアウト関数と他のコードの間の同期は、コールアウト関数が callout_deactivate() 呼び出している間に、コールアウトを停止するか、またはリセットすることを決して試みないことを保証します。

    さらに、上記のテクニックは、 アクティブ フラグが、コールアウトを効果的に有効にするか、または無効にするかどうかを常に反射することを保証します。 callout_active() が偽 (false) で返るなら、たとえコールアウトサブシステムが実際にコールアウト関数を呼び出すところだとしても、コールアウト関数は、動作なしで返るので、コールアウトは、事実上に無効にされます。

コールアウトが最後に停止されているとき、考慮されなければならない 1 つの最後の競合条件があります。この場合、既に破壊されたか、または再利用されたデータオブジェクトをアクセスする必要があるので、コールアウト関数自体は、コールアウトが停止されたことを検出することは安全ではないかもしれません。コールアウトが完全に終了されることを保証するために、 callout_drain() への呼び出しが使用されるべきです。

戻り値

timeout() 関数は、 untimeout() に渡すことができる struct callout_handle を返します。 callout_stop() と callout_drain() 関数は、呼び出されたとき、コールアウトがまだ保留中であったなら、0 以外を返し、そうでなければ、0 を返します。

歴史

現在の timeout と untimeout ルーチンは、 Redesigning the BSD Callout and Timer Facilities と表題がつけられた技術報告書で公表された Adam M. CostelloGeorge Varghese の作業に基づいています、そして Justin T. Gibbs によって FreeBSD の導入のために少し修正されました。この実装で使用されるデータ構造の元の作業は、 Proceedings of the 11th ACM Annual Symposium on Operating Systems Principles. の論文 Hashed and Hierarchical Timing Wheels: Data Structures for the Efficient Implementation of a Timer FacilityG. VargheseA. Lauck によって公表されました。現在の実装は、長く続いている、O(n) の挿入と削除の実行時間を提供しますが、 untimeout 操作のためのハンドルを生成または要求しない BSD リンクリストのコールアウトメカニズムに置換されます。
August 23, 2013 FreeBSD