イベントループの高度な使い方

libuvはイベントループを細かく制御する機能を提供し、複数のループを組み合わせて興味深い結果を得られます。また、libuvのイベントループを、別のイベントループを使うライブラリに組み込むこともできます。たとえば、QtベースのUIで、Qtのイベントループが、負荷の高いシステムレベルのタスクを行うlibuvバックエンドを動かす場合です。

イベントループの停止

uv_stop()は、イベントループの停止に使えます。ループの実行が停止するのは、最も早くても、次の反復時で、それより後になることもあります。つまり、ループの現在の反復で処理可能になっているイベントは、引き続き処理されます。そのため、uv_stop()を即時停止スイッチとして使うことはできません。uv_stop()を呼び出すと、この反復では、ループは、ブロックせずにI/Oを扱います。これらの意味は少しわかりにくいので、制御の流れ全体を処理する、uv_run()を見てみましょう。

src/unix/core.c - uv_run

 1  uv_signal_t* sh;
 2
 3  /* Note: while the handle is in the UV_HANDLE_CLOSING state now, it's still
 4   * possible for it to be active in the sense that uv__is_active() returns
 5   * true.
 6   *
 7   * A good example is when the user calls uv_shutdown(), immediately followed
 8   * by uv_close(). The handle is considered active at this point because the
 9   * completion of the shutdown req is still pending.
10   */
11  assert(handle->flags & UV_HANDLE_CLOSING);
12  assert(!(handle->flags & UV_HANDLE_CLOSED));
13  handle->flags |= UV_HANDLE_CLOSED;
14
15  switch (handle->type) {
16    case UV_PREPARE:
17    case UV_CHECK:
18    case UV_IDLE:
19    case UV_ASYNC:
20    case UV_TIMER:
21    case UV_PROCESS:

stop_flagは、uv_stop()が設定します。libuvのコールバックはすべてイベントループ内で呼び出されるため、コールバックから、uv_stop()を呼び出しても、ループの現在の反復は進みます。まずlibuvがタイマーを更新し、その後、保留中のタイマー・idle・prepareコールバックを実行し、保留中のI/Oコールバックを呼び出します。これらのいずれかで、uv_stop()を呼び出すと、stop_flagが設定されます。その結果、uv_backend_timeout()が、0を返すため、ループはI/Oでブロックしません。一方、checkハンドラのいずれかで、uv_stop()を呼び出した場合は、I/Oはすでに終了しているので、影響を受けません。

uv_stop()は、結果が計算できたときやエラーが起きたときに、各ハンドラを1つずつ停止させることなく、ループを停止するために役立ちます。

ループを停止しつつ、現在の反復の処理が引き続き行われることを示す単純な例を挙げます。

uvstop/main.c

 1#include <stdio.h>
 2#include <uv.h>
 3
 4int64_t counter = 0;
 5
 6void idle_cb(uv_idle_t *handle) {
 7    printf("Idle callback\n");
 8    counter++;
 9
10    if (counter >= 5) {
11        uv_stop(uv_default_loop());
12        printf("uv_stop() called\n");
13    }
14}
15
16void prep_cb(uv_prepare_t *handle) {
17    printf("Prep callback\n");
18}
19
20int main() {
21    uv_idle_t idler;
22    uv_prepare_t prep;
23
24    uv_idle_init(uv_default_loop(), &idler);
25    uv_idle_start(&idler, idle_cb);
26
27    uv_prepare_init(uv_default_loop(), &prep);
28    uv_prepare_start(&prep, prep_cb);
29
30    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
31
32    return 0;
33}