Prefer pthread's getCurrentId when using posix API
Zig Version
zig-linux-x86_64-0.15.0-dev.386+2e35fdd03
Steps to Reproduce and Observed Behavior
Actually, this issue was found during writing unikernel with zig. In unikernel, all syscalls are replaced by function calls which the lower layer implements. But current std.Thread.PosixThreadImpl.getCurrentId is an exception and here's the backtrace:
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/os/linux.zig:1720
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/Thread.zig:1141
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/Thread.zig:672
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/Thread.zig:354
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/Thread/Mutex/Recursive.zig:50
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/Progress.zig:545
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/debug.zig:202
/home/tw/d/zig-linux-x86_64-0.14.0/lib/std/debug.zig:212
Now, I have to use a patch to workaround this issue locally:
diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig
index fe3bf0fcea..ed1dad5a9c 100644
--- a/lib/std/Thread.zig
+++ b/lib/std/Thread.zig
@@ -690,7 +690,9 @@ const PosixThreadImpl = struct {
fn getCurrentId() Id {
switch (native_os) {
- .linux => {
+ .linux => if (use_pthreads) {
+ return @truncate(@intFromPtr(c.pthread_self()));
+ } else {
return LinuxThreadImpl.getCurrentId();
},
.macos, .ios, .watchos, .tvos, .visionos => {
Hope some core team members could evaluate if this change makes more sense.
Expected Behavior
No panic on unikernel.
Yeah, it seems a little funky for the PosixThreadImpl to leverage the LinuxThreadImpl to get the current thread ID. It seems to be that they want to get a local 32-bit thread identity (and this seems different from the "handle"). I think the posix one is a pointer-sized (so sometimes 64-bit) ID. That said, I'm not sure the proposed patch is the right fix.
The use_pthreads check is always going to be true inside PosixThreadImpl, as this impl gets picked if use_pthreads is true, so that check doesn't do much in your diff. (See the definition of Impl up on L103.)
Also the final else clause in this native_os switch statement is to invoke c.pthread_self(), so you could also just delete the linux-specific handling here and let it fall through to that.
The @truncate seems aggressive too. Perhaps the type of Id needs to be wider?
Overall the proposed patch seems unlikely to be accepted as is. (But I'm not a Zig core member, so YMMV.)
Alternatively, the LinuxThreadImpl getCurrentId() boils down to invoking the gettid syscall. Shouldn't that be a straightforward syscall for the unikernel layer to implement? From what I gather, you're telling Zig to compile for Linux (that's what native_os is mapping to). You're going to run into a lot of direct syscalls to the Linux kernel API from Zig libraries, even if you're linking a C library. So if you're not prepared for direct linux syscalls, you probably need a different approach to build unikernels.
The @truncate seems aggressive too. Perhaps the type of Id needs to be wider?
Actually, I don't know the reason why Id was defined as u32 at the first place either.
Alternatively, the LinuxThreadImpl getCurrentId() boils down to invoking the gettid syscall. Shouldn't that be a straightforward syscall for the unikernel layer to implement? From what I gather, you're telling Zig to compile for Linux (that's what native_os is mapping to). You're going to run into a lot of direct syscalls to the Linux kernel API from Zig libraries, even if you're linking a C library.
The reason is that unikraft implements its own musl layer and I want to leverage this.
To achieve this, I cheat the zig that target is linux-musl and want it to file syscall through musl interface. Actually, what I want in std.c is a global flag indicating calling underlying libc external functions anyway instead of syscall on its own.
So if you're not prepared for direct linux syscalls, you probably need a different approach to build unikernels.
Yeah, this is my backup plan which adds a new os tag (unikraft maybe).