.\" $NetBSD: timeout.9,v 1.2 1996/06/23 22:32:34 pk Exp $ .\" .\" Copyright (c) 1996 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This code is derived from software contributed to The NetBSD Foundation .\" by Paul Kranenburg. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the NetBSD .\" Foundation, Inc. and its contributors. .\" 4. Neither the name of The NetBSD Foundation nor the names of its .\" contributors may be used to endorse or promote products derived .\" from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED .\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR .\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE .\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .\" %FreeBSD: src/share/man/man9/timeout.9,v 1.23.2.2 2005/03/21 23:01:30 iedowse Exp % .\" $FreeBSD$ .\" .Dd February 6, 2005 .Dt TIMEOUT 9 .Os .Sh 名称 .Nm timeout , .Nm untimeout , .Nm callout_handle_init , .Nm callout_init , .Nm callout_stop , .Nm callout_drain , .Nm callout_reset , .Nm callout_pending , .Nm callout_active , .Nm callout_deactivate .Nd 明示された時間長の後の関数の実行 .Sh 書式 .In sys/types.h .In sys/systm.h .Pp .Bd -literal typedef void timeout_t (void *); .Ed .Ft struct callout_handle .Fn timeout "timeout_t *func" "void *arg" "int ticks" .Ft void .Fn callout_handle_init "struct callout_handle *handle" .Pp .Bd -literal struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle) .Ed .Ft void .Fn untimeout "timeout_t *func" "void *arg" "struct callout_handle handle" .Ft void .Fn callout_init "struct callout *c" "int mpsafe" .Ft int .Fn callout_stop "struct callout *c" .Ft int .Fn callout_drain "struct callout *c" .Ft void .Fn callout_reset "struct callout *c" "int ticks" "timeout_t *func" "void *arg" .Ft int .Fn callout_pending "struct callout *c" .Ft int .Fn callout_active "struct callout *c" .Fn callout_deactivate "struct callout *c" .Sh 解説 関数 .Fn timeout は .Fa ticks Ns No /hz 秒後に実行されるための引数 .Fa func によって与えられる関数を呼び出すためのスケジュールを行います。 正ではない値の .Fa ticks は暗黙のうちに値 .Sq 1 に変換されます。 .Fa func は .Fa void * の引数を取る関数へのポインタであるべきです。 実行時には、 .Fa func は .Fa arg をその唯一の引数として受け取ります。 .Fn timeout からの戻り値は、スケジュールされた timeout を取り消す要求のための .Fn untimeout 関数との接続に使用されることが可能な .Ft struct callout_handle です。 .Fn timeout の呼び出しは古いスタイルで、新しいコードは .Fn callout_* 関数を使用するべきです。 .Pp 関数 .Fn callout_handle_init はハンドルを初期化するために使用し、untimeout と共に 使用されても副作用無しに戻るようにします。 .Pp コールアウトハンドルに .Fn CALLOUT_HANDLE_INITIALIZER の値を割当てることは、 .Fn callout_handle_init と同様の機能を実行し、静的な宣言またはグローバルなコールアウトハンドルで 使用するために提供されています。 .Pp 関数 .Fn untimeout は、そのハンドルの正当性を確認するために .Fa func および .Fa arg 引数を使用して、 .Fa handle に関連付けられた timeout を取り消します。 そのハンドルが引数 .Fa arg を取る関数 .Fa func を持つ timeout と一致しない場合には、何も行いません。 .Fa handle は .Fn untimeout に渡される前に以前の .Fn timeout , .Fn callout_handle_init の呼び出し、または .Fn CALLOUT_HANDLE_INITIALIZER "&handle" の値の割当てによって初期化されなければなりません。 以前に初期化されたハンドルを伴なわない untimeout の呼び出しの振る舞いは 未定義です。 .Fn untimeout の呼び出しは古いスタイルで、新しいコードは .Fn callout_* 関数を使用するべきです。 .Pp ハンドルがシステムによって再利用されるので、 両方の呼び出しが同じ関数のポインタおよび引数を使用し、2 番目の呼び出しの前に 最初の timeout が終了するか取り消された場合には、1 つの .Fn timeout の実行からのハンドルが別の .Fn timeout の実行のハンドルが一致することができることが (意外ではあるが) 可能です。 timeout の設備は .Fn timeout および .Fn untimeout のための O(1) 実行時間を提供します。 timeout (訳注: および untimeout) は .Va Giant ロックが保持されている状態で、 .Fn softclock から実行されます。 従って、再入から保護されます。 .Pp 関数 .Fn callout_init , .Fn callout_stop , .Fn callout_drain および .Fn callout_reset は固有のコールアウト構造を割当てることを希望するクライアントのための、 低レベルのルーチンです。 .Pp 関数 .Fn callout_init はコールアウトを初期化し、そのためそのコールアウトは何の副作用もなしに .Fn callout_stop , .Fn callout_drain または .Fn callout_reset に渡されることができます。 .Fa mpsafe 引数が 0 の場合には、callout 構造体は .Dq マルチプロセッサセーフ であるとはみなされません。 すなわち、ジャイアントロックがコールアウト関数の呼出し前に 獲得され、コールアウト関数が戻るときに解放されるようにします。 .Pp 関数 .Fn callout_stop は、そのコールアウトが現在保留中の場合には、コールアウトを取り消します。 コールアウトが保留中の場合には、 .Fn callout_stop は 0 でない値を返します。 コールアウトが設定されていないか既に実行されているか現在実行中の場合には、 0 が返されます。 この関数が呼び出されるとき、コールアウトが .Va Giant ミューテックス (mutex) によって保護されている場合には、 .Va Giant を保持していなければなりません。 .Pp 関数 .Fn callout_drain は、コールアウトが既に進行中の場合にはその完了をウェイトすることを除いて、 .Fn callout_stop と同一です。 この関数は、そのコールアウトがブロックするかもしれないあらゆるロックを 保持している間は、決して呼び出されてはなりません。 さもないと結果としてデッドロックします。 コールアウトサブシステムが既にこのコールアウトを処理し始めたなら コールアウト関数が .Fn callout_drain の実行の間に呼び出されるかもしれないことに注意してください。 しかしながら、コールアウトサブシステムは、 .Fn callout_drain が返る前にコールアウトが完全に停止されることを保証します。 .Pp 関数 .Fn callout_reset は最初にそのコールアウトを廃止するために .Fn callout_stop と同様のことを実行し、それから新しいコールアウトを .Fn timeout と同じ流儀で確立します。 この関数が呼び出されるとき、コールアウトが .Va Giant ミューテックスによって保護される場合には、 .Va Giant は保持されなければなりません。 .Pp マクロ .Fn callout_pending , .Fn callout_active および .Fn callout_deactivate はコールアウトの現在の状態へのアクセスを提供します。 これらのマクロを慎重に使用すれば、非同期タイマ機構に 特有の多くの競合条件を避けることができます。 さらなる詳細については下記の .Sx "競合条件を回避する" を参照してださい。 .Fn callout_pending マクロは、コールアウトが .Em 保留中 であるかどうかチェックします。 コールアウトはタイムアウトが設定されているが時間がまだ到着していない時、 .Em 保留中 であると見なされます。 いったんタイムアウト時間が来て、コールアウトサブシステムがこのコールアウト を処理し始めれば、 .Fn callout_pending はたとえコールアウト関数が実行を終了して (または、始めて) いなくても .Dv FALSE を返すことに注意してください。 .\" even though = 〜にもかかわらず、たとえ〜としても、たとえ〜でも .Fn callout_active マクロはコールアウトが .Em アクティブ としてマークされているかどうかチェックし、 .Fn callout_deactivate マクロはコールアウトの .Em アクティブ フラグをクリアします。 コールアウトサブシステムは、タイムアウトが設定されているコールアウトを .Em アクティブ に設定し、 .Fn callout_stop と .Fn callout_drain では .Em アクティブ をクリアしますが、 コールアウト関数の実行を通して 通常どおりコールアウトの期限が切れた場合には、クリア .Em しません 。 .Ss "競合条件を回避する" コールアウトサブシステムはそれ自体のタイマコンテキストから コールアウト関数を呼び出します。 ある種の同期なしでは、コールアウト関数は他のスレッドからコールアウトの 停止やリセットを試みながら並行して起動される可能性があります。 特に、コールアウト関数は典型的には最初の動作として ミューテックスを獲得するので、 コールアウト関数は既に呼び出されたとしても、 別のスレッドがコールアウトをリセットするかまたは停止しようとするまで そのミューテックスを待ってブロックされてしまいます。 .Pp コールアウトサブシステムはこれらの同期関係に対処するために 多くのメカニズムを提供します。 .Bl -enum -offset indent .It .Fa mpsafe を .Dv FALSE に設定して .Fn callout_init を使用することで指定される) .Va Giant ミューテックスによってコールアウトが保護される場合は、 このミューテックスは競合条件を避けるために使用できます。 .Fn callout_stop か .Fn callout_reset を呼び出す前に、 .Va Giant ミューテックスは呼び出し側によって獲得されなければなりませんが、 コールアウトが想定したとおりに正しく停止されるかまたはリセットされる ことが保証されます。 .\" as expected = 案の定、予想どおり(に)、想定したとおり コールアウトかその関連のミューテックスを破壊する前に .Fn callout_drain を使用する必要がまだあることに注意してください。 .It .Fn callout_stop からのリターン値は、コールアウトが削除されたかどうかを示します。 コールアウトが設定され、 コールアウト関数がまだ実行されていないことがわかっている場合は、 .Dv FALSE の値はコールアウト関数がまさに呼び出されようとしていることを示します。 .\" about to = 《be 〜》まさに〜しようとしている 例えば: .Bd -literal -offset indent 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 */ } } .Ed .Pp .Fn callout_reset がコールアウトを停止したかどうか決定するための 同等なメカニズムがなにもないことに注意してください。 .It .Fn callout_pending , .Fn callout_active および .Fn callout_deactivate マクロは競合条件を回避するために連携して使用できます。 .\" work around = 問題に取り掛かる、対処する、 コールアウトのタイムアウトが設定されるとき、コールアウトサブシステムは ともに .Em アクティブ と .Em 保留中 の両方でコールアウトをマークします。 タイムアウト時間に達するとき、コールアウトサブシステムは .Em 保留中 のフラグを最初にクリアすることによってコールアウトを処理し始めます。 次に、 .Em アクティブ フラグを変えないでコールアウト関数を呼び出して、 コールアウト関数が戻った後でさえ .Em アクティブ フラグをクリアしません。 ここで説明されたメカニズムでは、 コールアウト関数自体が .Fn callout_deactivate マクロを使用して .Em アクティブ フラグをクリアすることを要求します。 .Fn callout_stop と .Fn callout_drain 関数は戻る前に、 常に .Em アクティブ と .Em 保留中 フラグの両方をクリアします。 .Pp .Fn callout_pending が .Dv TRUE を返す場合、 コールアウト関数は、最初に .Em 保留中 フラグをチェックして動作なしで戻るべきです。 これは、コールアウトがコールアウト関数が呼び出される直前に .Fn callout_reset を使用して再スケジュールされたことを示します。 .Fn callout_active が .Dv FALSE を返す場合、コールアウト関数は同様に動作なしで返るべきです。 これは、コールアウトが停止されたことを示します。 最後に、コールアウト関数は、 .Em アクティブ フラグをクリアするために .Fn callout_deactivate を呼び出すべきです。 例えば: .Bd -literal -offset indent 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 */ .Ed .Pp 上記で使用したミューテックスのような適切な同期をともに使うことにより、 このアプローチは .Fn callout_stop と .Fn callout_reset 関数がいつも競合なしで使用できます。 .\" together with = 〜とともに、〜に加えて 例えば: .Bd -literal -offset indent mtx_lock(&sc->sc_mtx); callout_stop(&sc->sc_callout); /* The callout is effectively stopped now. */ .Ed .Pp コールアウトがまだ保留中である場合、これらの関数は通常どおり動作しますが、 コールアウトの処理が既に始まっている場合、 コールアウト関数におけるテストにおいて、これらの関数はさらなる 動作なしで戻ることになります。 コールアウト関数と他のコードの間の同期は、コールアウト関数が .Fn callout_deactivate 呼び出しを終えるまでコールアウトの停止やリセットが行われないことを 確実にします。 .Pp さらに、上記のテクニックは、 .Em アクティブ フラグが実際にコールアウトが有効か無効かを反映することを確実にします。 .Fn callout_active が FALSE を返した場合、たとえコールアウトサブシステムが 実際にコールアウト関数を開始しようとしていたとしても、 コールアウト関数は動作なしに終了してしまうので、 実質的に無効化されています。 .\" since even if = たとえ〜でも〜なので、 .\" even if = たとえ〜でも .El .Pp 最後に、コールアウトを停止しようとしているときに 考慮しなければならない最後の競合条件が 1 つあります。 この場合、既に破壊されるかまたは再利用されたデータオブジェクトに アクセスする必要があるかもしれないので、 コールアウト関数自体に 停止されたコールアウトを検出するさせるために安全でないかもしれません。 コールアウトが完全に終了したことを保証するためには、 .Fn callout_drain 呼び出しを使用しなければなりません。 .Sh 戻り値 .Fn timeout 関数は .Fn untimeout に渡すことが可能な .Ft struct callout_handle を返します。 .Fn callout_stop および .Fn callout_drain 関数は呼び出された時にコールアウトが未だ保留中の場合には 0 以外を、 そうでない場合には 0 を返します。 .Sh 歴史 現在の timeout および untimeout ルーチンは .An Adam M. Costello および .An George Varghese の .%T "Redesigning the BSD Callout and Timer Facilities" と名付けられた技術レポートで発表された作業に基づいています。 また、 .Fx への導入のために .An Justin T. Gibbs によって少し修正されています。 この実装で使用されているデータ構造の元の作業は、 .An G. Varghese および .An A. Lauck によって .%B "Proceedings of the 11th ACM Annual Symposium on Operating Systems Principles" の .%T "Hashed and Hierarchical Timing Wheels: Data Structures for the Efficient Implementation of a Timer Facility" で発表されました。 現在の実装は、長らく存在していた、挿入および削除の O(n) 実行時間を 提供するが untimeout 操作のためのハンドルを生成または要求しなかった .Bx リンクリストのコールアウト機構に、取って代りました。