Zen言語のasync標準ライブラリ紹介〜event.Channel①〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

async関連のライブラリはstd.event下にあります。

今回はproducerとconsumerが多対多な (バッファ) 通信を実現するevent.Channelです。

event.Channel

サンプル

getter()putter()の2つのasync関数を使い、putter()でチャネルに書き込み、getter()でチャネルから読み込みます。

getter()では、チャネルから1234を取得できることをテストしています。 先にgetter()を実行していますが、チャネルが空なので、putter()に制御が移行し、putter()1234をチャネルに書いた後、再びgetter()に制御が移るため、テストが成功します。

const std = @import("std");
const Channel = std.event.Channel;
const testing = std.testing;

test "std.event.Channel" {
    var buf: [2]i32 = [_]i32{ 0 } ** 2;
    var channel: Channel(i32) = undefined;
    channel.init(&buf);
    defer channel.deinit();

    var handle = async getter(&channel);
    var sender = async putter(&channel);

    await handle;
    await sender;
}

fn getter(channel: *Channel(i32)) void {
    const value1 = channel.get();
    testing.equal(@to(i32, 1234), value1);
}

fn putter(channel: *Channel(i32)) void {
    channel.put(1234);
}

eventを使うテストでは、--test-evented-ioオプションを追加しなければいけません。

$ zen test src/main.zen --test-evented-io
1/1 test "std.event.Channel"...OK
All 1 tests have succesfully passed.

Channelget()put()とは、両方suspendポイントを持っています。

実行順は非決定的

上記プログラムに、大体実行順になるように番号を出力するコードを追加します。

const std = @import("std");
const Channel = std.event.Channel;
const warn = std.debug.warn;
const testing = std.testing;

test "std.event.Channel" {
    var buf: [2]i32 = [_]i32{ 0 } ** 2;
    var channel: Channel(i32) = undefined;
    channel.init(&buf);
    defer channel.deinit();

    warn("\n", .{});
    warn("seq: 1\n", .{});
    var handle = async getter(&channel);
    warn("seq: 3\n", .{});
    var sender = async putter(&channel);
    warn("seq: 5\n", .{});

    await handle;
    warn("seq: 7\n", .{});
    await sender;
}

fn getter(channel: *Channel(i32)) void {
    warn("seq: 2\n", .{});
    const value1 = channel.get();
    warn("seq: 6\n", .{});
    testing.equal(@to(i32, 1234), value1);
}

fn putter(channel: *Channel(i32)) void {
    warn("seq: 4\n", .{});
    channel.put(1234);
    warn("seq: 8\n", .{});
}

イベントループがディスパッチするタイミング次第で、2つの実行順序を取る可能性があるようです。

$ zen test src/main.zen --test-evented-io
1/1 test "std.event.Channel"...
seq: 1
seq: 2
seq: 3
seq: 4
seq: 5
seq: 6
seq: 7
seq: 8
OK
$ zen test src/main.zen --test-evented-io
1/1 test "std.event.Channel"...
seq: 1
seq: 2
seq: 3
seq: 4
seq: 5
seq: 8
seq: 6
seq: 7
OK