libuv 0.10から1.0.0への移行ガイド

1.0.0の開発過程で、一部のAPIは大きく変わりました。このガイドでは、0.10のリリース後に行われた、特に重要な変更に対応する移行方法を説明します。

ループの初期化と終了

libuv 0.10以前では、ループを次の関数で作成していました: uv_loop_new。この関数は新しいループのメモリーを確保して初期化します。ループの破棄には次の関数を使っていました: uv_loop_delete。この関数はループを破棄し、メモリーを解放します。1.0以降、これらの関数は非推奨となり、ユーザーがメモリーを確保してからループを初期化する必要があります。

libuv 0.10

uv_loop_t* loop = uv_loop_new();
...
uv_loop_delete(loop);

libuv 1.0

uv_loop_t* loop = malloc(sizeof *loop);
uv_loop_init(loop);
...
uv_loop_close(loop);
free(loop);

注記

簡潔にするため、エラー処理は省略しています。次の関数の文書を確認してください: uv_loop_init()およびuv_loop_close()。

エラー処理

libuv 1.0では、エラー処理を大幅に見直しました。libuv 0.10では、一般に関数の戻り値とstatusパラメーターは成功時に0、失敗時に-1となり、ユーザーは次の関数を使っていました: uv_last_error。この関数で、正の数であるエラーコードを取得する必要がありました。

1.0では、関数の戻り値とstatusパラメーターに実際のエラーコードが入ります。成功時は0、エラー時は負の数です。

libuv 0.10

... assume 'server' is a TCP server which is already listening
r = uv_listen((uv_stream_t*) server, 511, NULL);
if (r == -1) {
  uv_err_t err = uv_last_error(uv_default_loop());
  /* err.code contains UV_EADDRINUSE */
}

libuv 1.0

... assume 'server' is a TCP server which is already listening
r = uv_listen((uv_stream_t*) server, 511, NULL);
if (r < 0) {
  /* r contains UV_EADDRINUSE */
}

スレッドプールの変更

libuv 0.10では、Unixはデフォルトで4スレッドのスレッドプールを使い、Windowsは次のAPIを使っていました: QueueUserWorkItem。このAPIはWindows内部のスレッドプールを使い、そのデフォルトはプロセスごとに512スレッドです。

1.0では両者の実装を統一したため、WindowsもUnixと同じ実装を使うようになりました。スレッドプールのサイズは、次の環境変数をエクスポートして設定できます: UV_THREADPOOL_SIZE。次の文書を参照してください: スレッドプールの作業スケジューリング。

メモリー確保コールバックのAPI変更

libuv 0.10では、コールバックは、値を設定した次の型の構造体を返す必要がありました: uv_buf_t。値渡しで返します:

uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) {
    return uv_buf_init(malloc(size), size);
}

libuv 1.0では、バッファへのポインターがコールバックに渡され、ユーザーがその内容を設定する必要があります:

void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
    buf->base = malloc(size);
    buf->len = size;
}

IPv4とIPv6のAPIの統一

libuv 1.0では、IPv4とIPv6のAPIを統一しました。従来は次の2つの関数がありました: uv_tcp_bindとuv_tcp_bind6。現在は次の関数だけです: uv_tcp_bind()。

IPv4の関数は、次の構造体を値渡しで受け取っていました: struct sockaddr_in。IPv6の関数が受け取っていた型は、次のとおりです: struct sockaddr_in6。現在は、次の型を受け取ります: struct sockaddr*(ポインターである点に注意してください)。構造体はスタック上に確保できます。

libuv 0.10

struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", 1234);
...
uv_tcp_bind(&server, addr)

libuv 1.0

struct sockaddr_in addr;
uv_ip4_addr("0.0.0.0", 1234, &addr)
...
uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0);

IPv4とIPv6の構造体を作成する関数(uv_ip4_addr()およびuv_ip6_addr())も変わりました。必ず文書を確認してください。

..note::

この変更は、IPv4アドレスとIPv6アドレスを区別していたすべての関数に適用されます。

ストリームとUDPのデータ受信コールバックのAPI変更

ストリームとUDPのデータ受信コールバックは、現在、次の型のバッファへのポインターを受け取ります: uv_buf_t。構造体を値渡しで受け取る方式ではありません。

libuv 0.10

void on_read(uv_stream_t* handle,
             ssize_t nread,
             uv_buf_t buf) {
    ...
}

void recv_cb(uv_udp_t* handle,
             ssize_t nread,
             uv_buf_t buf,
             struct sockaddr* addr,
             unsigned flags) {
    ...
}

libuv 1.0

void on_read(uv_stream_t* handle,
             ssize_t nread,
             const uv_buf_t* buf) {
    ...
}

void recv_cb(uv_udp_t* handle,
             ssize_t nread,
             const uv_buf_t* buf,
             const struct sockaddr* addr,
             unsigned flags) {
    ...
}

パイプ経由のハンドル受信のAPI変更

libuv 0.10以前では、次の関数を使っていました: uv_read2_start。これはパイプ上のデータ読み取りを開始する関数であり、そのパイプ経由でハンドルを受信する場合もありました。この関数のコールバックは次の形でした:

void on_read(uv_pipe_t* pipe,
             ssize_t nread,
             uv_buf_t buf,
             uv_handle_type pending) {
    ...
}

libuv 1.0では、uv_read2_startを削除しました。ユーザーは読み取りコールバック内で、次の関数を使い、保留中のハンドルがあるか確認する必要があります: uv_pipe_pending_count()およびuv_pipe_pending_type():

void on_read(uv_stream_t* handle,
             ssize_t nread,
             const uv_buf_t* buf) {
    ...
    while (uv_pipe_pending_count((uv_pipe_t*) handle) != 0) {
        pending = uv_pipe_pending_type((uv_pipe_t*) handle);
        ...
    }
    ...
}

ハンドルからのファイルディスクリプターの取り出し

APIではサポートされていませんでしたが、たとえばTCPハンドルのファイルディスクリプターを取得するため、ユーザーがlibuvの内部にアクセスすることがよくありました。

fd = handle->io_watcher.fd;

現在は、次の関数で正式に公開しています: uv_fileno()。

uv_fs_readdirの改名とAPI変更

uv_fs_readdirは、libuv 0.10では、完了時に次のフィールドへ文字列のリストを返していました: req->ptr。1.0では、この関数を次の名前へ変更しました: uv_fs_scandir()。これは実際の実装が次の関数を使っているためです: scandir(3)。

また、文字列のリスト全体を確保する代わりに、ユーザーは次の関数で結果を1件ずつ取得できます: uv_fs_scandir_next()。この関数ではスレッドプールへ処理を往復させる必要がありません。libuvが次の項目のリストを保持しているためです: dents。このリストは、次の関数が返したものです: scandir(3)。