Threaded and Evented coexistence

Just to clean mess in my head.

Let’s suppose that we have two “execution contexts”:

  • under Io.Threaded

  • under Io.Evented

And shared struct with Io.Mutex used guard

As far as I understand the way of safely access to this struct is

  • use “neitral” 3rd Io - std.Io.Threaded.global_single_threaded

Or I miss something?

And this problem simply doesn’t exist

1 Like

Use zio, as the evented io, for communicating with the evented io from threaded io, and also for debug_io. std.Io.Mutex used through zio will work no matter if you are in zio’s runtime or any other thread.

does it mean , that it depends on Io implementation?

If you don’t know the implementations that share the data, then you do need some synchronisation that works across them, which could be another Io though, know that only Threaded qualifies for that currently (ignoring 3rd party Io).

You could also use something outside any Io implementation. Regardless the synchronisation will never be ideally optimal.

It is also possible that you one, and which, of the existing Io qualifies, you could just use that instead creating/using another.

Yes, it does. The behavior of everything in Io depends on the implementation

means - simply use one of them (via saving io within struct)?

It’s true that this is probably a blind spot in the design of std.Io currently.

One usecase I have for that is a subsystem somewhere that relies on a thread pool to do some work. I’d like to have cross-io synchronization between the thread pool io and the main io (that can be evented or threaded).

Currently is there a simple way to do that ?

for simple access synchronization you can use Mutex with the same io(choice evented only or threaded only) see above advice of @vulpesx , regarding Cancel - use uncancelable lock

for more advcanced case i think there is not generic solution
elaborate please

There is no way to do that using std.Io.

I’m sorry for being spammy, but I really recommend you to give zio a try. It’s built explicitly for the model that you have evented I/O for your application, but you can delegate some work to a thread pool (zio.spawnBlocking/zio.blockInPlace). It’s what people are used to in Tokio, asyncio, etc.

For my own applications, I use the zio APIs internally, and std.Io only when I need to integrate external library.

There is this ticket opened by Andrew, as a response to me pointing our why I think zio APIs are better for app development than std.Io, https://codeberg.org/ziglang/zig/issues/31112

I don’t think there will ever be an universal solution for cross-io communication.

That does not work. If you call mutex.lock(threaded_io) inside an event loop, you will block the event loop. You can do that for short critical sections, but definitely don’t do that for queues, condition variables anything that actually needs to wait.

1 Like

Thanks, but requiring a third-party is exactly why it doesn’t scale. Tokio, asyncio, etc have the same problem in common: they fragment the ecosystem.

The promise of std.Io is to have a common interface for every library developer. You don’t have to force a runtime for the user.

Also, I’m perfectly fine if the answer is “it’s not possible today”. I just think that this is a problem that will need to be addressed later.

That is precisely why it implements the std.Io interface, so you can use it as std.Io with extra stuff that is not in std.Io.

For example, if you want to use std.Io.Threaded and have additional thread pool, will need to build the thread pool on top of std.Io.Threaded (not any std.Io, this one in particular) and then depend on the fact that you can use std.Io.Queue across the io instances, because their synchronization primitives are compatible. It will even work across std.Io.Threaded and std.Io.Evented (when finished), but only on Linux, because they both use Linux futexes.

As I said, I really doubt there will ever be an universal solution, this will always depend on the particular implementations you are using.

1 Like

for several systems co-existance of Evented(network) and Threaded(pipe-lining) is must

My guess - without Cancel flow it may be implemented some way

For now just C based network + Io.Threaded

@lalinsky @vulpesx @Corendos — maybe this deserves a separate brainstorming post?

something like “evented&threaded co-existance/communication/synchronisation/…”

I think that you may need to back up and rethink how you got here. This maybe an XY problem.

I don’t think that’s true. As long as the evented side of std.Io is cooperative, not preemptive, you need multiple executors for different kind of work. Java (virtual threads) and Go (goroutines) get away with just a single executor, because they can preempt long running CPU tasks. That requires language changes and accepting the performance penalties it brings. If you only have cooperative evented I/O, you need to mindful of not blocking the event loop.

For now I see only one solution for Threaded(processing engine) <==> Evented(network engine) negotiation

  • stream oriented sockets(UDS or loopback).

Without deep dive - 4 sockets fd/handle/…

  connection

S1 ========= S2

  connection

S3 ========= S4

Threaded => Evented : Multiply Thr. Senders(threaded mutex as guard) S1 - One Evnt. Receiver S2

Evented => Threaded : Multiply Evnt. Senders(evented mutex as guard) S3 - One Thr.Receiver S4

Is this a theoretical discussion? The problem could be easily solved, if the architecture of the std.Io was different. If you look at how I designed zio, I essentially have both threaded and evented backends and they naturally coexist. The problem is that e.g. std.Io.Mutex directly uses futexes from the Io implementation, but it doesn’t have to he that way. std.Io could require the implementation to provide a “waiter” abstraction and implement mutex in term of wait queues, which is how it’s typically done in the kernel of operating systems. Heck, it could even build the waiter out of the current futexes without changes to the vtable. Then all the mutexes would suddenly become compatible. Do the same treatment for std.Io.Conditon and you enable the way for cross-io std.Io.Queue which is enough for most communication. I’d even go as far as to say that this is an easy problem to solve, it’s just that there would have to be interest in solving it.

Given the bugs I’ve recently found in the stdlib sync primitives, I might create a library that uses the current interface, and provides cross-io synchronization primitives.

1 Like

Absolutely not - most networked servers are mix of Threaded processing and Evented network.
But in current situation I should to solve it via workarounds

How to raise/get attention of io architects?

I can understand “now it is not right time for nework”.

Question to io architects - When will be right time to provide threaded-evented coexistence?

As far as I understand your solution - it’s special Io just for sync primitives - the rest of vtable entries - 'notimplemented"

is it right understanding?