I am developing a cross-platform queue library and have conducted some basic performance tests. However, the removal of std.Thread.mutex and std.Thread.futex from the synchronization primitives saddens me. I believe these should have been retained. Currently, using io.futexwait seems to spawn a new thread, which is not what I want.
I’ve completed the migration to Zig 0.16 and ran some basic benchmarks. The lockfree wait queue (using futex for waiting) has a noticeable performance drop.
Use mutex.lockUncancelable instead of mutex.lock.
Use cond.waitUncancelable instead of cond.wait.
Use io.futexWaitUncancelable instead of futexWait.
The new std.Io is interesting, but I still wish there was an option for users. I’d really like to see std.Thread.Mutex, std.Thread.Futex, and std.Thread.Condition added back. It’s great, but sometimes I don’t need std.Io, and this leaves me with no choice. On the other hand, I don’t mind providing an interface with std.Io for libraries, but I’d prefer to keep a set of interfaces withoutstd.Io as well—that’s really useful in certain situations.
At least bring futex back.
Do I really have to implement futex, mutex, and condition manually?
This is getting bit off-topic for this thread. The reason i think std doesn’t have Io-independent synchronization primitives or threads is because they would not mix with Io, the Io has to be aware of the synchronization.
Now zig being systems programming language, whether only putting all balls into the std.Io basket and not having lower level abstractions like in zig 0.15 and before is a design decision for sure. But you could also say that if you write system specific code, you could probably also write better code than those portable abstractions.
One thing is sure, that if zig 0.16 program does Io without going through given Io instance, the program may block for uncertain amount of time, or in worst case infinitely if mixing Io synchronization primitives with OS ones.
std.posix had code that did not work properly on some platforms, or was suboptimal because usually using the posix api as the model that every implementation had to fit to. Also freestanding had no way to implement std.posix.
No, but those can’t interact with each other seamlessly. I think one of goals with ziglibc is that it can integrate into std.Io meaning it is transparent to C code that they are actually going through a zig Io instance.
Who’s talking about those std.posix interfaces? I’m talking about the most fundamental synchronization primitives of the language. As a library author, one should avoid having to implement these basic language features themselves, because that fragments the ecosystem—when every library that needs them goes off and implements its own version, it effectively creates fragmentation. You have no idea what that means… and you’re off there answering about std.posix, completely missing the point.
I never said I’m against std.Io. But it leaves me with no choice—do you understand what that means? I can provide interfaces that take an Io parameter, and those interfaces would integrate seamlessly, just as you want. Users could also use interfaces without an Io parameter—that would be up to them.
On the other hand, for a performance-oriented library, this design forces me to implement things at literally twice the performance cost. Do you think that’s acceptable?
The std.Thread synchronization primitives weren’t that different in spirit of std.posix. I already also told you that if you use OS native synchronization primitives and threads, they will not work in tandem with std.Io. It is possible to make them cooperate, but you basically will have to mix 2 different kind of synchronization primitives and know much about the implementation itself.
Avoiding this indeed is the idea of std.Io interface. Think how you have to normally write both blocking and non-blocking version of code. Or in rust, support every async runtime seperately.
If you don’t care about these aspects, then yeah you could start by copying the old 0.15.2 code and maintaining your own portable OS abstraction layer. That will have issues and caveats when mixed with std.Io that you and any consumer of the project would have to be aware of.
If you are “performance-oriented” library, that wants to break through every abstraction level, then I don’t see why this is such a huge issue for you.
Of course it’s gonna split the ecosystem. Because you are giving the way to split the ecosystem … If you don’t want to split the ecosystem use std.Io, that’s the point.
If you are implementing Io providing the lower level APIs is up to you indeed. If user is gonna use those lower-level APIs instead of the std.Io implementation you provide, they make the decision that it might not be compatible with other things out there. This is similar situation to rust async runtimes, or deciding on libuv, libxev, zig-aio, zio.
That has nothing to do with me. That is the user splitting the ecosystem.
According to your logic, providing an interface that is not based on std.Io is an act of splitting the ecosystem. So does the Zig world only run one programming paradigm? Is only std.Io allowed to exist?
I think it’s very unlikely that std will bring back these primitives. I’m also unhappy with the situation, because Io is a very lacking interface. I think splitting the ecosystem is unavoidable at some point, much like C++ has a standard library, but applications don’t use it. I don’t see how e.g. TigerBeatle would want to use std.Io instead of it’s own abstraction layer.
I also would like to see those primitives back, yes std.Io have those but there are still situations where users of the code does not need to choose an io implementation or provide one. I get that it might interfere but other things in code can do that as well. Whats stopping a developer to implement these on their own?
former std.Thread.mutex is zigabstraction - for example Windows mutex is reqursive but zig one is not (there is ReqursiveMutex iirc), windows supports named mutexes - zig does not
These os independed abstractions were already implemented , it was huge work - I don’t want repeat it
the same “IO” oriented os primitives exist in Windows
WSAEVENT WSAAPI WSACreateEvent();
This api creates WinSocket event, but IT’S THE SAME AS REGULAR WINDOWS EVENT:
application can call the CreateEvent function directly
I understand your question to be that “these were already created, why give them up, I need them?”
I also understood the Zig team’s motivations to be that “maintaining code takes resources, we want to spend those resources maintaining this new abstraction and retire the old abstraction”.
If the old Zig standard library 15.2 code really is what you want, then why can’t you copy-paste it into a new library? Seems like there are several of you here thinking about this, could you team up and support something like this?