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

名称

taskqueue非同期タスクの実行

書式

#include < sys/param.h>
#include < sys/kernel.h>
#include < sys/malloc.h>
#include < sys/queue.h>
#include < sys/taskqueue.h>

typedef void (*task_fn_t)(void *context, int pending); 
 
typedef void (*taskqueue_enqueue_fn)(void *context); 
 
struct task { 
 STAILQ_ENTRY(task) ta_link; /* キューのためのリンク */ 
 u_short   ta_pending; /* キューに入れられた */ 
      /* 回数をカウント */ 
 u_short   ta_priority; /* キュー中のタスクの */ 
      /* 優先度 */ 
 task_fn_t  ta_func; /* タスクハンドラ */ 
 void   *ta_context; /* ハンドラの引数 */ 
}; 
 
enum taskqueue_callback_type { 
 TASKQUEUE_CALLBACK_TYPE_INIT, 
 TASKQUEUE_CALLBACK_TYPE_SHUTDOWN, 
}; 
 
typedef void (*taskqueue_callback_fn)(void *context); 
 
struct timeout_task;


struct taskqueue *
taskqueue_create( const char *name, int mflags, taskqueue_enqueue_fn enqueue, void *context);

struct taskqueue *
taskqueue_create_fast( const char *name, int mflags, taskqueue_enqueue_fn enqueue, void *context);

int
taskqueue_start_threads( struct taskqueue **tqp, int count, int pri, const char *name, ...);

void
taskqueue_set_callback( struct taskqueue *queue, enum taskqueue_callback_type cb_type, taskqueue_callback_fn callback, void *context);

void
taskqueue_free( struct taskqueue *queue);

int
taskqueue_enqueue( struct taskqueue *queue, struct task *task);

int
taskqueue_enqueue_fast( struct taskqueue *queue, struct task *task);

int
taskqueue_enqueue_timeout( struct taskqueue *queue, struct timeout_task *timeout_task, int ticks);

int
taskqueue_cancel( struct taskqueue *queue, struct task *task, u_int *pendp);

int
taskqueue_cancel_timeout( struct taskqueue *queue, struct timeout_task *timeout_task, u_int *pendp);

void
taskqueue_drain( struct taskqueue *queue, struct task *task);

void
taskqueue_drain_timeout( struct taskqueue *queue, struct timeout_task *timeout_task);

int
taskqueue_member( struct taskqueue *queue, struct thread *td);

void
taskqueue_run( struct taskqueue *queue);

TASK_INIT( struct task *task, int priority, task_fn_t func, void *context);

TASK_INITIALIZER( int priority, task_fn_t func, void *context);

TASKQUEUE_DECLARE( name);

TASKQUEUE_DEFINE( name, taskqueue_enqueue_fn enqueue, void *context, init);

TASKQUEUE_FAST_DEFINE( name, taskqueue_enqueue_fn enqueue, void *context, init);

TASKQUEUE_DEFINE_THREAD( name);

TASKQUEUE_FAST_DEFINE_THREAD( name);

TIMEOUT_TASK_INIT( struct taskqueue *queue, struct timeout_task *timeout_task, int priority, task_fn_t func, void *context);

解説

これらの関数は、コードの非同期実行のための単純なインタフェースを提供しています。

関数 taskqueue_create() は、新しいキューを作成するために使用されます。 taskqueue_create() への引数は、ユニークであるべき名前、 malloc() への呼び出しがスリープを許可されるどうかを指定する 1 組の malloc(9) フラグ、タスクがキューに追加されるとき、 taskqueue_enqueue() から呼び出される関数とキューをサービスするスレッドの識別が記録されるメモリ位置へのポインタを含みます。 taskqueue_enqueue() から呼び出される関数は、キューが処理されるように (例えば、ソフトウェア割り込みによるスケジューリングまたはカーネルスレッドが起こされることによって) 準備しなければなりません。スレッドの識別が記録されているメモリ位置は、サービススレッドを終了するシグナルのために使用されます -- この値が 0 に設定されて、スレッドがシグナルを受けるとき、それは終わります。キューが速い割り込みハンドラで使用するためのものであるなら、 taskqueue_create_fast() は、 taskqueue_create() の代わりに使用されるべきです。

関数 taskqueue_free() は、キューによって使用されるメモリを解放するために使用されるべきです。キューにある任意のタスクは、現時点では、スレッドのサービスキューが、それが終了するべきであるとシグナルが起こさる後に、実行されます。

いったんタスクキューが作成されると、そのスレッドは、 taskqueue_start_threads() を使用して開始されるべきです。コールバックは、 taskqueue_set_callback() を使用して、オプションで登録されます。現在、コールバックは、次の目的のために登録されます:

TASKQUEUE_CALLBACK_TYPE_INIT
このコールバックは、あらゆるタスクを実行する前に、タスクキュー中のすべてのスレッドによって呼び出されます。このコールバックは、タスクキューのスレッドが開始される前に、設定されなければなりません。
TASKQUEUE_CALLBACK_TYPE_SHUTDOWN
このコールバックは、その最後のタスクを実行した後に、タスクキュー中のすべてのスレッドによって呼び出されます。このコールバックは、タスクキュー構造体が変換される前に、常に呼び出されます。

キューに入れられたタスクのリストにタスクを追加するためには、キューへのポインタとタスクへのポインタを付けて taskqueue_enqueue() を呼び出します。タスクの ta_pending フィールドが 0 でないなら、タスクがキューに入れられた回数を反映するために USHRT_MAX の上限まで、単に増加させられます。そうでなければ、そのタスクは、より低い ta_priority 値を持っている最初のタスクの前のリストに追加されるか、またはタスクにより低い優先度がないなら、リストの終わりに追加されます。タスクをキューに入れることは、割り込みハンドラから適切な呼び出しを行うためにメモリ割り付けを実行しません。この関数は、キューが解放されているなら、 EPIPE を返します。

関数 taskqueue_enqueue_fast() は、高速割り込みハンドラからキューに入れることが発生するときには、 taskqueue_enqueue() の代わりに使用されるべきです。このメソッドは、高速割り込みコンテキスト内でスリープの可能性を避けるためにスピンロックを使用します。

タスクが実行されるときには、先ずそのタスクがキューから取り除かれ、 ta_pending の値が記録されそれからそのフィールドが 0 でクリアされます。 task 構造体の ta_func 関数は、 ta_context フィールドの値を最初の引数として、 ta_pending の値を 2 番目の引数として、呼び出されます。関数 ta_func が返った後に、 wakeup(9) は、 taskqueue_enqueue() に渡されたタスクポインタで呼び出されます。

taskqueue_enqueue_timeout() は、指定された量の ticks 後にエンキューをスケジュールするために使用されます。 timeout_task スケジューリングのためだけに速くないタスクキューを使用することができます。 ticks 引数が負であるなら、既にスケジュールされたキューは、再スケジュールされません。そうでなければ、タスクは、 ticks の絶対値が渡された後に、将来キューに入れるためにスケジュールされます。

taskqueue_cancel() 関数は、タスクをキャンセルする (取り消す) ために使用されます。 ta_pending カウントは、クリアされ、古い値は、参照パラメータ pendpNULL でなければ、そこに返されます。タスクが現在の実行中であるなら、 EBUSY が返され、そうでなければ、0 が返されます。実行しているタスクが終了するのを待つブロッキング taskqueue_cancel() を実装するためには、次に似ています:

while (taskqueue_cancel(tq, task, NULL) != 0) 
 taskqueue_drain(tq, task);

taskqueue_drain() のように、タスクがキャンセルされた後に再びキューに入れられないことを確実にすることに対して、呼び出し側に責任があることに注意してください。

同様に、 taskqueue_cancel_timeout() 関数は、スケジュールされているタスクの実行を中止するために使用されます。

taskqueue_drain() 関数は、タスクが終わるのを待つために使用され、 taskqueue_drain_timeout() 関数は、スケジュールされているタスクが終わるのを待つために使用されます。タスクが taskqueue_drain() の呼び出しの後にキューに入れられないという保証はありません。

taskqueue_member() 関数は、与えられたスレッド td が与えられたタスクキュー queue の一部であるなら、 1 を返し、そうでなければ、 0 を返します。

taskqueue_run() 関数は、指定された queue のすべての保留されているタスクを実行します。通常、この関数は、内部的にだけ使用されます。

便利なマクロ TASK_INIT( task, priority, func, context) は、 task 構造体を初期化するために提供されています。 TASK_INITIALIZER() マクロは、タスク構造体のための初期化プログラムを生成します。マクロ TIMEOUT_TASK_INIT( queue, timeout_task, priority, func, context) は、 timeout_task 構造体を初期化します。 priority, funccontext の値は、単純に task 構造体のフィールドにコピーされ、 ta_pending フィールドは、クリアされます。

5 つのマクロ TASKQUEUE_DECLARE( name), TASKQUEUE_DEFINE( name, enqueue, context, init), TASKQUEUE_FAST_DEFINE( name, enqueue, context, init), TASKQUEUE_DEFINE_THREAD( name) と TASKQUEUE_FAST_DEFINE_THREAD( name) は、グローバルなキューへの参照の宣言、そのキューの実装の定義、および所有するスレッドを使用するキューを宣言するために使用されます。 TASKQUEUE_DEFINE() マクロは、 name, enqueuecontext 引数の値で、システムの初期化の間に taskqueue_create() を呼び出すための手配を行います。 taskqueue_create() の呼び出しの後で、(割り込みハンドラの登録などの) その他の初期化が実行されることを可能にするために、このマクロへの init 引数が、C のステートメントとして実行されます。

TASKQUEUE_DEFINE_THREAD() マクロは、タスクを取り扱う自身が所有するカーネルスレッドで新しいタスクキューを定義します。変数 struct taskqueue *taskqueue_name は、そのキューにタスクを追加するために使用されます。

TASKQUEUE_FAST_DEFINE() と TASKQUEUE_FAST_DEFINE_THREAD() は、それぞれまるで TASKQUEUE_DEFINE() と TASKQUEUE_DEFINE_THREAD() のように動作しますが、タスクキューは、 taskqueue_create_fast() で作成されます。

事前定義タスクキュー

システムは、4 つのグローバルなタスクキュー taskqueue_fast, taskqueue_swi, taskqueue_swi_gianttaskqueue_thread を提供します。 taskqueue_fast キューは、速い割り込みハンドラからディスパッチされる swi ハンドラのためのものです。そこでは、スリープ mutex (複数) を使用することができません。 swi タスクキューは、ソフトウェア割り込みの仕組みを介して実行されます。 taskqueue_swi キューは、 Giant カーネルロックの保護なしで実行し、 taskqueue_swi_giant キューは、 Giant カーネルロックの保護有りで実行します。スレッドタスクキュー taskqueue_thread は、カーネルスレッドコンテキストで実行され、このスレッドから実行されるタスクは、 Giant カーネルロックの下で実行されません。呼び出し側が Giant の下で実行したいなら、呼び出し側は、タスクキューハンドラルーチンの中で、明確に Giant の獲得および解放を行なうべきです。

このキューを使用するためには、使用したいキュー ( taskqueue_swi, taskqueue_swi_giant または taskqueue_thread) のためのグローバルなタスクキュー変数の値で taskqueue_enqueue() を呼び出します。グローバルなタスクキュー変数 taskqueue_fast のためには、 taskqueue_enqueue_fast() を使用してください。

ソフトウェア割り込みキューは、例えば、ハンドラの中で著しい量の処理を実行しなければならない割り込みハンドラを実装するために、使用されることが可能です。ハードウェア割り込みハンドラは、その割り込みの最小の処理を実行し、それから作業を完了させるためにタスクをキューに入れます。これは、割り込みが無効化されて費やされる時間を最小量にまで縮小します。

スレッドキューは、例えば、スレッドコンテキストからのみ実行することが可能な何かを行なうカーネル関数を呼び出すことが必要な、割り込みレベルのルーチンによって使用されることが可能です。 (例えば、M_WAITOK フラグを伴った malloc の呼び出しです。)

taskqueue_swi などの共有されたタスクキューでキューに入れられたタスクは、実行の前の予測できない時間遅れるかもしれないことに注意してください。キューの遅れを許容することができないなら、プライベートなタスクキューは、専用の処理スレッドで作成されるべきです。

関連項目

ithread(9), kthread(9), swi(9)

歴史

このインタフェースは、 FreeBSD 5.0 ではじめて登場しました。 Linux カーネルに work_queue と呼ばれる同様の機能があります。

作者

このマニュアルページは、 Doug Rabson によって書かれました。
December 4, 2012 FreeBSD