#Confused with zig new async

1 messages · Page 1 of 1 (latest)

runic zenith
#
pub fn testingPrint(io: *const Io) void {
    io.sleep(std.Io.Duration.fromSeconds(10), std.Io.Clock.real) catch {
        std.debug.print("Io was cancelled before it could finish sleeping\n", .{});
    };
    io.sleep(std.Io.Duration.fromSeconds(10), std.Io.Clock.real) catch {
        std.debug.print("Io was cancelled before it could finish sleeping\n", .{});
        return;
    };
    std.log.debug("test", .{});
}

pub fn main(init: std.process.Init) !void {
    const io = init.io;

    var res = io.async(testingPrint, .{&io});
    res.cancel(io);
    res.cancel(io);
}

Expected:

❯ zig build run
Io was cancelled before it could finish sleeping
Io was cancelled before it could finish sleeping

but got:

❯ zig build run
Io was cancelled before it could finish sleeping
debug: test
#

My initial understanding was that the sleep does not execute before the second cancel but I dissproved this by adding another print after the second cancel which executed after the first one.

#

So the second sleep blocks the main thread which is confusing to me.

#

I am realizing this is probably the bad way to do this 😅

#

This was my second attempt but now even the first doesn't get cancelled.

const Testing = struct {
    task: std.Io.Future(std.Io.Cancelable!void) = undefined,
    lock: std.Io.Mutex = .init,

    pub fn testingPrint(self: *Testing, io: Io) !void {
        try self.lock.lock(io);
        self.task = io.async(std.Io.sleep, .{ io, std.Io.Duration.fromSeconds(2), std.Io.Clock.real });
        self.lock.unlock(io);
        self.task.await(io) catch {
            std.debug.print("Cancelled 1\n", .{});
        };
        try self.lock.lock(io);
        self.task = io.async(std.Io.sleep, .{ io, std.Io.Duration.fromSeconds(2), std.Io.Clock.real });
        self.lock.unlock(io);
        self.task.await(io) catch {
            std.debug.print("Cancelled 2\n", .{});
        };
        std.log.debug("test\n", .{});
    }

    pub fn cancel(self: *Testing, io: Io) void {
        self.task.cancel(io) catch {
            std.debug.print("Cancelled 3\n", .{});
        };
    }
};

pub fn main(init: std.process.Init) !void {
    const io = init.io;

    var testing: Testing = .{};
    try testing.testingPrint(io);
    try testing.lock.lock(io);
    testing.cancel(io);
    testing.lock.unlock(io);
    testing.cancel(io);
    testing.lock.unlock(io);
    std.debug.print("testing\n", .{});
}
serene flower
#

after the first io.sleep returns error.Canceled, you are supposed to exit

the second io.sleep, which you allow to run, will sleep and not get canceled

#

two future.cancels() do nothing, you can only cancel one and then it waits until the future completes

runic zenith
#

Yeah that's what I realised hence my second code, thanks for confirming.

serene flower
#

maybe easier to explain if I knew what are you actually trying to do, this example seems arbitrary, and breaks the basic rule, to always propagate error.Canceled

runic zenith
#

I imagine having the sleep (just a placeholder for long running task) in a while loop so it would either get executed or get timed out.

#

I know there is other api for that just exploring, not a real project.

#

I realised the first one was stupid

#

But what about the second one I wonder why that doesn't work.

serene flower
#

what do you expect it to do? there are many bugs that I see

runic zenith
#

Cancel both and not print (the second catch should return just a mistake I made)

serene flower
#

the normal pattern is::

var task1 = io.async(...);
defer task1.cancel(io);

var task2 = io.async(...);
defer task2.cancel(io);

// here you can try await, or do something else

and if something fails (e.g. gets canceled), both sub-tasks get canceled

your example has really strange flow

but testingPrint is a blocking operation, so by the time it ends, there is nothing to cancel

runic zenith
#

Oh yeah I am stupid

#

Await blocks it 🤦

cold solar
serene flower
#

Io.Future stores the result, so the second cancel or await just returns it

#

however, neither are thread safe, so you cant cancel from another task

runic zenith
#

Thank you for the help

#

The async model is different from other languages, it makes it hard for me to wrap my head around.

serene flower
#

if you have used threads in other languages, it;s best to think of io.async and io.concurrent as if you were spawning a new thread

runic zenith
#

Well I only know java async 😅

#

For C I always relied on libraries

#

Same for rust

#

I've used rtems though so that is similiar

serene flower
#

java async? what is that?

#

I thought java only had threads (plus the virtual ones)

runic zenith
runic zenith
serene flower
#

oh, interesting, it was a long time since I've done something with java 🙂

runic zenith
runic zenith
serene flower
#

try to explain what you want to achieve in the example and I'll write a working code

runic zenith
#

I don't have a goal to achieve I am just trying to get familiar with the new interfaces

#

And couldn't understand why the code I wrote didn't work

serene flower
#

I mean, for the example, it would be easier to give you idiomatic code

runic zenith
#

I am probably gonna look at the code

#

So I know how it works internally so I see the intended way to work with it

serene flower
#

btw, unless you need the results, Io.Group might be easier to work with, if you need to cancel multiple tasks

runic zenith
#

Thanks 👍