zig icon indicating copy to clipboard operation
zig copied to clipboard

Prefer pthread's getCurrentId when using posix API

Open tw4452852 opened this issue 8 months ago • 2 comments

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.

tw4452852 avatar Apr 29 '25 10:04 tw4452852

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.

rootbeer avatar Apr 29 '25 17:04 rootbeer

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).

tw4452852 avatar May 06 '25 01:05 tw4452852