What are easy to understand sources to learn how to use the new IO?

Hello, I am trying to find sources like tutorial about how to use the new IO, but I can not find any

I would like to know the ones that cover :

  • how to spawn an IO task?
  • how to yield it?
  • how to resume?
  • how to wait the result?
  • how to cancel gracefully?
  • how to set timeout?

Does anyone know any resource?

1 Like

Most of these questions do not really apply to the Io interface. There is no explicitly yield/suspend/resume. The Io interface is essentially built like a standard blocking set of APIs, not what you would expect from an async engine you might know from another language.

I am sorry my English is not good, do you mean that the newest IO does not support non blocking yet? Because as far as I know, it is meant to I can turn it to blocking and non blocking just by changing the IO type, like the allocator

The interface does support it, but does so in an agnostic way, this allows you to write code that is portable across concurrency models!

All operations provide a blocking api, which you then wrap in io.async or io.concurrent to make non-blocking.
There are also lower level primitives like Io.Batch, etc.

How exactly that works depends on the runtime implementation, currently the only usable one in std is std.Io.Threaded, which just uses OS threads and blocking io.

There are other implementations e.g io uring, but they are at varying levels of not usable.

Ah I thought I can already use the non blocking. Will the yield and resume are already supported by the language in the roadmap? Like as far as I know, in non blocking it needs functionality to pause and save the current data to heap, then the runtime will run other tasks, then the runtime can rerun the previous paused task at the exact same point where it was paused using the saved heap data, will the saving data when yielding and resuming at the exact same code point be supported by the language or must be implemented manually in 3rd party library?

Yes it can already do that, not as a language feature, just as an implementation detail of the Io implementation.

Every blocking operation is an opportunity for it to yield, Io has its own synch primitives to also integrate with this

It would be best if you explain what do you want to do with Zig. Explain it high-level, not in implementation details.

Do you want to write a service that communicates over network or something similar? You can already do that with std.Io, you don’t need access to the non-blocking primitives for that.

If you want to do something low level, that really needs yield/suspend/resume, you can use my zio library, which implements coroutines and gives you access to these APIs, but you don’t really need them for any application-level code.

1 Like

I want to be able to run multiple non blocking tasks concurrently like calling multiple services at the same time, without blocking the network requests handler. And when I run blocking task, I can run it in independent runner so it will not block the threads that serves network requests. And I want to set timeout to make task will not run too long, plus when I stop my app, I want it cancel the task gracefully to prevent data corruption

What you are describing you can almost do with std.Io, see std.Io overview

However, the run blocking task in an independent runner, that is something that is foreign to std.Io, you would need to hack it around by having multiple io instances of different type. Or you use my zio API, which is exactly the model you seem to know, similar to Rust’s Tokio or Python’s asyncio.

For the timeouts, that is also something currently lacking in std.Io, see this blog post on how I solve it in zio: Timeouts in zio | Lukáš Lalinský

1 Like

To get started, you can check out exercises 85-95 from Ziglings: https://codeberg.org/ziglings/exercises/src/branch/main/exercises/085_async.zig

4 Likes

Wow after reading all the text, I think what I need is already there in Zio

  • spawn concurrent task = runtime.spawn, group.spawn for multiple
  • cancel = .cancel()
  • wait = .wait()
  • gracefull shutdown = signal

A bit clarification question about the non blocking, will when I pass Zio to the IO, it is auto running in non blocking without calling await? Like Golang Goroutine, I am familiar with Goroutine. Can I write code without minding blocking and non blocking? Like in Golang Goroutine. I saw there is spawn blocking in the API, but it says it is like Goroutine, so I am confused is it exactly like Goroutine or not, or stackfull coroutine but I have to manage which one is blocking and non blocking manually to prevent blocking the runtime

Yes, when you use zio as std.Io, it will run functions as coroutines and use non-blocking I/O without you explicitly having to await them. So you have Go-like code, but having it truly async on the background. But you can use even std.Io.Threaded to work like this, except that it will use threads instead of coroutines.

The only difference from Go is: if you have a really CPU-intensive work, like resizing an image, or something similar, Go will interrupt your function in order to run other work, but zio will not do that. So if you want to avoid blocking the runtime on pure CPU work like that, you can use zio.spawnBlocking(), but that should be rare. For other cases, you can use zio.yield() to give other tasks option to run, if you have a longer function that is doing no I/O.

Thank you for the explanation answer. I use it already, it is good. Will you create stackless coroutine version? It will have better performance plus can reach out of memory longer and ideal to be used in embedded

It’s not possible to have stackless coroutines without compiler support. There is a proposal to implement this into the compiler again, but I don’t really have high hopes.

However, from performance perspective, stackful coroutines are faster. The ONLY benefits of stackless coroutines are less memory used and no risk of stack overflow, but at the cost of requiring allocations for every single call of async function.

1 Like