From 86d1cc8e2fffed9c93e41b28605d764e6b1749dc Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Thu, 2 Aug 2018 17:44:20 -0400 Subject: [PATCH 1/6] Add thread ID support to std.os.Thread (fixes #1316) --- std/c/index.zig | 1 + std/os/index.zig | 33 +++++++++++++++++++++++++++++++++ std/os/test.zig | 12 ++++++++++++ std/os/windows/kernel32.zig | 2 ++ 4 files changed, 48 insertions(+) diff --git a/std/c/index.zig b/std/c/index.zig index 7de8634d07..738b2f9c05 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -58,6 +58,7 @@ pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias at pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; +pub extern "pthread" fn pthread_self() pthread_t; pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; pub const pthread_t = *@OpaqueType(); diff --git a/std/os/index.zig b/std/os/index.zig index 425a900a71..c946e13046 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2516,6 +2516,10 @@ pub const Thread = struct { data: Data, pub const use_pthreads = is_posix and builtin.link_libc; + + /// An opaque type representing a kernel thread ID. + pub const Id = *@OpaqueType(); + pub const Data = if (use_pthreads) struct { handle: c.pthread_t, @@ -2536,6 +2540,35 @@ pub const Thread = struct { else => @compileError("Unsupported OS"), }; + /// Returns the ID of the calling thread. + pub fn currentId() Thread.Id { + // TODO: As-is, this function is potentially expensive (making a + // syscall on every call). Once we have support for thread-local + // storage (https://github.com/ziglang/zig/issues/924), we could + // memoize it. + if (use_pthreads) { + return @ptrCast(Thread.Id, c.pthread_self()); + } else return switch (builtin.os) { + builtin.Os.linux => + @intToPtr(Thread.Id, @bitCast(u32, linux.getpid())), + builtin.Os.windows => + @ptrCast(Thread.Id, windows.GetCurrentThread()), + else => @compileError("Unsupported OS"), + }; + } + + /// Returns the ID of this thread object. + pub fn id(self: *const Thread) Thread.Id { + if (use_pthreads) { + return @ptrCast(Thread.Id, self.data.handle); + } else return switch (builtin.os) { + builtin.Os.linux => + @intToPtr(Thread.Id, @bitCast(u32, self.data.pid)), + builtin.Os.windows => @ptrCast(Thread.Id, self.data.handle), + else => @compileError("Unsupported OS"), + }; + } + pub fn wait(self: *const Thread) void { if (use_pthreads) { const err = c.pthread_join(self.data.handle, null); diff --git a/std/os/test.zig b/std/os/test.zig index 9e795e8ad2..6ef38c6764 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -34,6 +34,18 @@ test "access file" { try os.deleteTree(a, "os_test_tmp"); } +fn testThreadIdFn(threadId: *?os.Thread.Id) void { + threadId.* = os.Thread.currentId(); +} + +test "std.os.Thread.currentId" { + var threadCurrentId: ?os.Thread.Id = null; + const thread = try os.spawnThread(&threadCurrentId, testThreadIdFn); + const threadId = thread.id(); + thread.wait(); + assert(threadCurrentId == threadId); +} + test "spawn threads" { var shared_ctx: i32 = 1; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index fa3473ad05..a28179f4eb 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -63,6 +63,8 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; +pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; + pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; From a25824e033d11a8ada0d444efab9ac08a93185af Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 5 Aug 2018 03:38:51 +0900 Subject: [PATCH 2/6] zig/std/os/index.zig: clean-up thread id; (#1) Ref #1316 #1330 --- std/os/index.zig | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index c946e13046..2eb9d98da0 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2518,22 +2518,28 @@ pub const Thread = struct { pub const use_pthreads = is_posix and builtin.link_libc; /// An opaque type representing a kernel thread ID. - pub const Id = *@OpaqueType(); + pub const Id = if (use_pthreads) + c.pthread_t + else switch (builtin.os) { + builtin.Os.linux => i32, + builtin.Os.windows => windows.HANDLE, + else => @compileError("Unsupported OS"), + }; pub const Data = if (use_pthreads) struct { - handle: c.pthread_t, + handle: Thread.Id, stack_addr: usize, stack_len: usize, } else switch (builtin.os) { builtin.Os.linux => struct { - pid: i32, + handle: Thread.Id, stack_addr: usize, stack_len: usize, }, builtin.Os.windows => struct { - handle: windows.HANDLE, + handle: Thread.Id, alloc_start: *c_void, heap_handle: windows.HANDLE, }, @@ -2547,26 +2553,17 @@ pub const Thread = struct { // storage (https://github.com/ziglang/zig/issues/924), we could // memoize it. if (use_pthreads) { - return @ptrCast(Thread.Id, c.pthread_self()); + return c.pthread_self(); } else return switch (builtin.os) { - builtin.Os.linux => - @intToPtr(Thread.Id, @bitCast(u32, linux.getpid())), - builtin.Os.windows => - @ptrCast(Thread.Id, windows.GetCurrentThread()), + builtin.Os.linux => linux.getpid(), + builtin.Os.windows => windows.GetCurrentThread(), else => @compileError("Unsupported OS"), }; } - /// Returns the ID of this thread object. + /// Returns the ID of this thread. pub fn id(self: *const Thread) Thread.Id { - if (use_pthreads) { - return @ptrCast(Thread.Id, self.data.handle); - } else return switch (builtin.os) { - builtin.Os.linux => - @intToPtr(Thread.Id, @bitCast(u32, self.data.pid)), - builtin.Os.windows => @ptrCast(Thread.Id, self.data.handle), - else => @compileError("Unsupported OS"), - }; + return self.data.handle; } pub fn wait(self: *const Thread) void { @@ -2583,9 +2580,9 @@ pub const Thread = struct { } else switch (builtin.os) { builtin.Os.linux => { while (true) { - const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst); + const pid_value = @atomicLoad(i32, &self.data.handle, builtin.AtomicOrder.SeqCst); if (pid_value == 0) break; - const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null); + const rc = linux.futex_wait(@ptrToInt(&self.data.handle), linux.FUTEX_WAIT, pid_value, null); switch (linux.getErrno(rc)) { 0 => continue, posix.EINTR => continue, @@ -2767,7 +2764,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; const newtls: usize = 0; - const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid); + const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); const err = posix.getErrno(rc); switch (err) { 0 => return thread_ptr, From 7a2401ef1eca3446279469b092e6f9c9653e38f8 Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Sat, 4 Aug 2018 21:47:13 -0400 Subject: [PATCH 3/6] Don't compare ?Thread.Id == Thread.Id in the test It doesn't work, because of issue #1332. --- std/os/index.zig | 2 +- std/os/test.zig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 2eb9d98da0..a35f244e38 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2517,7 +2517,7 @@ pub const Thread = struct { pub const use_pthreads = is_posix and builtin.link_libc; - /// An opaque type representing a kernel thread ID. + /// An type representing a kernel thread ID. pub const Id = if (use_pthreads) c.pthread_t else switch (builtin.os) { diff --git a/std/os/test.zig b/std/os/test.zig index 6ef38c6764..e983935b73 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -34,12 +34,12 @@ test "access file" { try os.deleteTree(a, "os_test_tmp"); } -fn testThreadIdFn(threadId: *?os.Thread.Id) void { +fn testThreadIdFn(threadId: *os.Thread.Id) void { threadId.* = os.Thread.currentId(); } test "std.os.Thread.currentId" { - var threadCurrentId: ?os.Thread.Id = null; + var threadCurrentId: os.Thread.Id = undefined; const thread = try os.spawnThread(&threadCurrentId, testThreadIdFn); const threadId = thread.id(); thread.wait(); From 0a3ae9dc6e79e595bc7a78da564f46f6b466abd0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Aug 2018 16:48:49 -0400 Subject: [PATCH 4/6] fix std.os.Thread.getCurrentId for linux --- std/os/index.zig | 16 +++++++--------- std/os/linux/index.zig | 4 ++++ std/os/test.zig | 14 +++++++------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index a35f244e38..c36769c455 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -160,7 +160,7 @@ test "os.getRandomBytes" { try getRandomBytes(buf_b[0..]); // Check if random (not 100% conclusive) - assert( !mem.eql(u8, buf_a, buf_b) ); + assert(!mem.eql(u8, buf_a, buf_b)); } /// Raises a signal in the current kernel thread, ending its execution. @@ -2547,22 +2547,20 @@ pub const Thread = struct { }; /// Returns the ID of the calling thread. - pub fn currentId() Thread.Id { - // TODO: As-is, this function is potentially expensive (making a - // syscall on every call). Once we have support for thread-local - // storage (https://github.com/ziglang/zig/issues/924), we could - // memoize it. + /// Makes a syscall every time the function is called. + pub fn getCurrentId() Thread.Id { if (use_pthreads) { return c.pthread_self(); - } else return switch (builtin.os) { - builtin.Os.linux => linux.getpid(), + } else + return switch (builtin.os) { + builtin.Os.linux => linux.gettid(), builtin.Os.windows => windows.GetCurrentThread(), else => @compileError("Unsupported OS"), }; } /// Returns the ID of this thread. - pub fn id(self: *const Thread) Thread.Id { + pub fn id(self: Thread) Thread.Id { return self.data.handle; } diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 15607ea6c0..15ca649f06 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -947,6 +947,10 @@ pub fn getpid() i32 { return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); } +pub fn gettid() i32 { + return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); +} + pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); } diff --git a/std/os/test.zig b/std/os/test.zig index e983935b73..440491380b 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -34,16 +34,16 @@ test "access file" { try os.deleteTree(a, "os_test_tmp"); } -fn testThreadIdFn(threadId: *os.Thread.Id) void { - threadId.* = os.Thread.currentId(); +fn testThreadIdFn(thread_id: *os.Thread.Id) void { + thread_id.* = os.Thread.getCurrentId(); } -test "std.os.Thread.currentId" { - var threadCurrentId: os.Thread.Id = undefined; - const thread = try os.spawnThread(&threadCurrentId, testThreadIdFn); - const threadId = thread.id(); +test "std.os.Thread.getCurrentId" { + var thread_current_id: os.Thread.Id = undefined; + const thread = try os.spawnThread(&thread_current_id, testThreadIdFn); + const thread_id = thread.id(); thread.wait(); - assert(threadCurrentId == threadId); + assert(thread_current_id == thread_id); } test "spawn threads" { From d2dd29e80c89d8cc530a185e32e9025d0e453bb5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Aug 2018 17:25:24 -0400 Subject: [PATCH 5/6] separate os.Thread.Id and os.Thread.Handle because of windows --- std/os/index.zig | 27 ++++++++++++++++++--------- std/os/test.zig | 9 +++++++-- std/os/windows/kernel32.zig | 1 + 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index c36769c455..6f124df4ed 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2517,8 +2517,9 @@ pub const Thread = struct { pub const use_pthreads = is_posix and builtin.link_libc; - /// An type representing a kernel thread ID. - pub const Id = if (use_pthreads) + /// Represents a kernel thread handle. + /// May be an integer or a pointer depending on the platform. + pub const Handle = if (use_pthreads) c.pthread_t else switch (builtin.os) { builtin.Os.linux => i32, @@ -2526,20 +2527,28 @@ pub const Thread = struct { else => @compileError("Unsupported OS"), }; + /// Represents a unique ID per thread. + /// May be an integer or pointer depending on the platform. + /// On Linux and POSIX, this is the same as Handle. + pub const Id = switch (builtin.os) { + builtin.Os.windows => windows.DWORD, + else => Handle, + }; + pub const Data = if (use_pthreads) struct { - handle: Thread.Id, + handle: Thread.Handle, stack_addr: usize, stack_len: usize, } else switch (builtin.os) { builtin.Os.linux => struct { - handle: Thread.Id, + handle: Thread.Handle, stack_addr: usize, stack_len: usize, }, builtin.Os.windows => struct { - handle: Thread.Id, + handle: Thread.Handle, alloc_start: *c_void, heap_handle: windows.HANDLE, }, @@ -2548,19 +2557,19 @@ pub const Thread = struct { /// Returns the ID of the calling thread. /// Makes a syscall every time the function is called. - pub fn getCurrentId() Thread.Id { + pub fn getCurrentId() Id { if (use_pthreads) { return c.pthread_self(); } else return switch (builtin.os) { builtin.Os.linux => linux.gettid(), - builtin.Os.windows => windows.GetCurrentThread(), + builtin.Os.windows => windows.GetCurrentThreadId(), else => @compileError("Unsupported OS"), }; } - /// Returns the ID of this thread. - pub fn id(self: Thread) Thread.Id { + /// Returns the handle of this thread. + pub fn handle(self: Thread) Thread.Handle { return self.data.handle; } diff --git a/std/os/test.zig b/std/os/test.zig index 440491380b..ee5f253f7b 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -41,9 +41,14 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void { test "std.os.Thread.getCurrentId" { var thread_current_id: os.Thread.Id = undefined; const thread = try os.spawnThread(&thread_current_id, testThreadIdFn); - const thread_id = thread.id(); thread.wait(); - assert(thread_current_id == thread_id); + switch (builtin.os) { + builtin.Os.windows => assert(os.Thread.getCurrentId() != thread_current_id), + else => { + const thread_id = thread.handle(); + assert(thread_current_id == thread_id); + }, + } } test "spawn threads" { diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index a28179f4eb..daeebf1021 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -64,6 +64,7 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; +pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD; pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; From 24d74cbf44b34b180b2df5557927d9d2e4c9e443 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Aug 2018 17:30:55 -0400 Subject: [PATCH 6/6] fix Thread impl on Linux and add docs --- std/os/index.zig | 5 ++++- std/os/test.zig | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 6f124df4ed..d47444d67d 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2519,6 +2519,7 @@ pub const Thread = struct { /// Represents a kernel thread handle. /// May be an integer or a pointer depending on the platform. + /// On Linux and POSIX, this is the same as Id. pub const Handle = if (use_pthreads) c.pthread_t else switch (builtin.os) { @@ -2557,6 +2558,7 @@ pub const Thread = struct { /// Returns the ID of the calling thread. /// Makes a syscall every time the function is called. + /// On Linux and POSIX, this Id is the same as a Handle. pub fn getCurrentId() Id { if (use_pthreads) { return c.pthread_self(); @@ -2569,7 +2571,8 @@ pub const Thread = struct { } /// Returns the handle of this thread. - pub fn handle(self: Thread) Thread.Handle { + /// On Linux and POSIX, this is the same as Id. + pub fn handle(self: Thread) Handle { return self.data.handle; } diff --git a/std/os/test.zig b/std/os/test.zig index ee5f253f7b..82054d3f32 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -41,11 +41,11 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void { test "std.os.Thread.getCurrentId" { var thread_current_id: os.Thread.Id = undefined; const thread = try os.spawnThread(&thread_current_id, testThreadIdFn); + const thread_id = thread.handle(); thread.wait(); switch (builtin.os) { builtin.Os.windows => assert(os.Thread.getCurrentId() != thread_current_id), else => { - const thread_id = thread.handle(); assert(thread_current_id == thread_id); }, }