#async/await usage? Why is an error reported here?

1 messages · Page 1 of 1 (latest)

hearty flare
#

Why isn't this working? Can someone please help me? Thank you.

const std = @import("std");
const print = std.debug.print;
const assert = std.debug.assert;

fn _get_data(i: *i32) void {
    print("_get_data 1\n", .{});

    i.* = 1;

    suspend {}

    print("_get_data 2\n", .{});

    i.* = 2;

    suspend {}

    print("_get_data 3\n", .{});

    i.* = 3;
}

fn get_data(i: *i32) void {
    print("get_data | before call _get_data(i:{})\n", .{i.*});
    await async _get_data(i);
    print("get_data | after call _get_data(i:{})\n", .{i.*});
}

pub fn main() void {
    print("hello, zig!\n", .{});

    var i: i32 = 0;

    var f = async _get_data(&i); // ok (1)
    // var f = async get_data(&i); // not ok (2)

    assert(i == 1);

    resume f;

    assert(i == 2);

    resume f;

    assert(i == 3);
}
# root @ vm in ~/chinadns-ng on git:master x [16:45:56] 
$ zig run  a.zig -fstage1 # ok (1)
hello, zig!
_get_data 1
_get_data 2
_get_data 3

# root @ vm in ~/chinadns-ng on git:master x [16:46:04] 
$ zig run  a.zig -fstage1 # not ok (2)
hello, zig!
get_data | before call _get_data(i:0)
_get_data 1
thread 64444 panic: awaiting function resumed
/root/chinadns-ng/a.zig:25:5: 0x2378ba in get_data (a)
    await async _get_data(i);
    ^
/root/chinadns-ng/a.zig:39:5: 0x234d16 in main (a)
    resume f;
    ^
/usr/lib/zig/std/start.zig:604:22: 0x207dee in std.start.posixCallMainAndExit (a)
            root.main();
                     ^
/usr/lib/zig/std/start.zig:376:5: 0x207bd1 in std.start._start (a)
    @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
    ^
[1]    64444 IOT instruction (core dumped)  zig run a.zig -fstage1

# root @ vm in ~/chinadns-ng on git:master x [16:46:17] C:134
waxen wedge
#

You are calling resume f where f is the frame of get_data. But get_data is awaiting the return of _get_data. This isn't allowed, so the error message exactly describes what is happening.

Async stuff in Zig is low level, it won't deduce that when you call resume f you mean to resume the frame of _get_data because get_data is awaiting _get_data.

hearty flare
#

Thanks, how should I change it, should I call resume inside _get_data?

waxen wedge
#

I haven't actually used async in a long time since I mostly stick with master, I'm not sure what the correct way to do something like this is...

hearty flare
#

okay

sweet elk
#

You need to store the frame that actually suspended somewhere and resume that (instead of the top-level one awaiting it). In _get_data, you can get the current frame using @frame() and, say, store it in a global. Here's an example on the zig version when it was still around: https://ziglang.org/documentation/0.10.1/#Async-Functions

#

Btw, await (async f()) is the same as f() when f is an async function. That's what allows zig to have supposedly "colorless async"

hearty flare
#

Thanks for your reply, do you mean: in zig, if the target function to be called (foo) is asynchronous (i.e., internally it will execute suspend), then for the caller (main), there is no difference between foo() and await async foo()?

#

can you tell me how to fix this error ?

#
const std = @import("std");
const net = std.net;
const print = std.debug.print;
const ArrayList = std.ArrayList(anyframe);

pub const io_mode = .evented;

pub fn main() !void {
    const addr = try net.Address.parseIp("127.0.0.1", 7000);

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var list: ArrayList = ArrayList.init(allocator);

    var i: i32 = 0;
    while (i < 100) : (i += 1) {
        var f = async send_message(addr);
        list.append(f);
    }

    for (list) |f| {
        await f;
    }
}

fn send_message(addr: net.Address) !void {
    print("[send_message] try connect to {}\n", .{addr});

    var socket = try net.tcpConnectToAddress(addr);
    defer socket.close();

    print("[send_message] connected to {}\n", .{addr});

    _ = try socket.write("Hello World!\n");

    print("[send_message] write done, close {} {}\n", .{ addr, socket });
}

# root @ vm in ~/zig-learn [18:52:31] C:1
$ zig run a.zig -fstage1
./a.zig:19:21: error: expected type 'anyframe', found '@Frame(send_message)'
        list.append(f);
                    ^
./a.zig:27:1: note: @Frame(send_message) declared here
fn send_message(addr: net.Address) !void {

how to store frame to ArrayList container ?

#

How should I declare the element type of the ArrayList and what is the actual type of the frame?

sweet elk
#

var f = async function(); is the same (conceptualy) as var f: @Frame(function) = undef; resume &f
So f is invalidated on the next loop while its async frame is still active in memory. You need to pin the frame in memory somewhere before starting it + only invalidate/free it once the function for the frame completes

hearty flare
#
const std = @import("std");
const net = std.net;
const print = std.debug.print;
const ArrayList = std.ArrayList(@Frame(send_message));

pub const io_mode = .evented;

pub fn main() !void {
    const addr = try net.Address.parseIp("127.0.0.1", 7000);

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var list = ArrayList.init(allocator);

    var i: i32 = 0;
    while (i < 100) : (i += 1) {
        var f = async send_message(addr);
        try list.append(f);
    }

    for (list.items) |f| {
        await f;
    }
}

fn send_message(addr: net.Address) !void {
    print("[send_message] try connect to {}\n", .{addr});

    var socket = try net.tcpConnectToAddress(addr);
    defer socket.close();

    print("[send_message] connected to {}\n", .{addr});

    _ = try socket.write("Hello World!\n");

    print("[send_message] write done, close {} {}\n", .{ addr, socket });
}
# root @ vm in ~/zig-learn [19:15:07] C:1
$ zig run a.zig -fstage1
./a.zig:22:23: error: expected type 'anyframe->@typeInfo(@typeInfo(@TypeOf(send_message)).Fn.return_type.?).ErrorUnion.error_set!void', found '*const @Frame(send_message)'
    for (list.items) |f| {

how to fix this compile error, thanks !

waxen wedge
#

That particular error was caused because it wanted the frame to not be constant, which could be fixed by using |*f| in the capture.

But there are other problems: at the time of the async call I believe that you need a fixed place in memory for your frame to live until it completes. E.g. allocate each frame individually:

const std = @import("std");
const net = std.net;
const print = std.debug.print;
const ArrayList = std.ArrayList(*@Frame(send_message));

pub const io_mode = .evented;

pub fn main() !void {
    const addr = try net.Address.parseIp("127.0.0.1", 7000);

    var aa = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer aa.deinit();
    const allocator = aa.allocator();

    var list = ArrayList.init(allocator);

    var i: i32 = 0;
    while (i < 100) : (i += 1) {
        var f = try allocator.create(@Frame(send_message));
        f.* = async send_message(addr);
        try list.append(f);
    }

    for (list.items) |f| {
        try await f;
    }
}

fn send_message(addr: net.Address) !void {
    print("[send_message] try connect to {}\n", .{addr});

    var socket = try net.tcpConnectToAddress(addr);
    defer socket.close();

    print("[send_message] connected to {}\n", .{addr});

    _ = try socket.write("Hello World!\n");

    print("[send_message] write done, close {} {}\n", .{ addr, socket });
}
hearty flare
#

Thank you very much for your answer.

waxen wedge
hearty flare
#

But currently you can only use async in 0.10.1 (-fstage1)

#

Do you know of any other way to perform asynchronous non-blocking IO? Like what this example above does with 100 simultaneous non-blocking tcp connections.

waxen wedge
hearty flare
#

Thanks for the reply, if epoll is used, does it mean that the writing is similar to C (callbacks + an epoll_wait loop)?

#

Is it possible to avoid using callbacks, callback hell is horrible, I've written too many callbacks in C.

waxen wedge
#

I kind of like poll wait loops, so I wouldn't call it hell personally 🙂 But yeah, if you want something more along the lines of coroutines then maybe take a look at zigcoro

#

There is also just multi-threading.

hearty flare
#

Okay, thanks.

hearty flare
#

If it was just a handful of connections, this might work, but that's not what I'm aiming for, haha.

waxen wedge
hearty flare
#

Okay, I'm looking at zigcoro.

hearty flare
#

I found this and I now completely understand how async works.