From a344cb03bc3c48f3c7fec32dc19c1bcad0910941 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 23:30:13 -0400 Subject: [PATCH] *WIP* use pthreads when linking libc --- std/c/darwin.zig | 5 +++ std/c/index.zig | 10 +++++ std/c/linux.zig | 5 +++ std/os/index.zig | 112 +++++++++++++++++++++++++++++++++++------------ std/os/test.zig | 4 +- 5 files changed, 107 insertions(+), 29 deletions(-) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index b958055ae8..7ac57514c9 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -81,3 +81,8 @@ pub const sockaddr = extern struct { }; pub const sa_family_t = u8; + +pub const pthread_attr_t = extern struct { + __sig: c_long, + __opaque: [56]u8, +}; diff --git a/std/c/index.zig b/std/c/index.zig index cff86f4041..5ea7145cd3 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -53,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void; pub extern "c" fn realloc(&c_void, usize) ?&c_void; pub extern "c" fn free(&c_void) void; pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; + +pub extern "c" fn pthread_create(noalias newthread: &pthread_t, + noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, + noalias arg: ?&c_void) c_int; +pub extern "c" fn pthread_attr_init(attr: &pthread_attr_t) c_int; +pub extern "c" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; +pub extern "c" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; +pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; + +pub const pthread_t = &@OpaqueType(); diff --git a/std/c/linux.zig b/std/c/linux.zig index b2ac05eba5..7810fec130 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig"); pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int; extern "c" fn __errno_location() &c_int; pub const _errno = __errno_location; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; diff --git a/std/os/index.zig b/std/os/index.zig index 0639490725..3669dca198 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2,6 +2,10 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const is_windows = builtin.os == Os.windows; +const is_posix = switch (builtin.os) { + builtin.Os.linux, builtin.Os.macosx => true, + else => false, +}; const os = this; test "std.os" { @@ -2343,21 +2347,39 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { } pub const Thread = struct { - pid: i32, + pid: pid_t, allocator: ?&mem.Allocator, stack: []u8, + pthread_handle: pthread_t, + + pub const use_pthreads = is_posix and builtin.link_libc; + const pthread_t = if (use_pthreads) c.pthread_t else void; + const pid_t = if (!use_pthreads) i32 else void; pub fn wait(self: &const Thread) void { - while (true) { - const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); - if (pid_value == 0) break; - const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); - switch (linux.getErrno(rc)) { - 0 => continue, - posix.EINTR => continue, - posix.EAGAIN => continue, + if (use_pthreads) { + const err = c.pthread_join(self.pthread_handle, null); + switch (err) { + 0 => {}, + posix.EINVAL => unreachable, + posix.ESRCH => unreachable, + posix.EDEADLK => unreachable, else => unreachable, } + } else if (builtin.os == builtin.Os.linux) { + while (true) { + const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); + if (pid_value == 0) break; + const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); + switch (linux.getErrno(rc)) { + 0 => continue, + posix.EINTR => continue, + posix.EAGAIN => continue, + else => unreachable, + } + } + } else { + @compileError("Unsupported OS"); } if (self.allocator) |a| { a.free(self.stack); @@ -2429,31 +2451,67 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread thread_ptr.stack = stack; thread_ptr.allocator = null; - const threadMain = struct { - extern fn threadMain(ctx_addr: usize) u8 { + const MainFuncs = struct { + extern fn linuxThreadMain(ctx_addr: usize) u8 { if (@sizeOf(Context) == 0) { return startFn({}); } else { return startFn(*@intToPtr(&const Context, ctx_addr)); } } - }.threadMain; + extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { + if (@sizeOf(Context) == 0) { + _ = startFn({}); + return null; + } else { + _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx))); + return null; + } + } + }; - const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND - | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS - | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; - const newtls: usize = 0; - const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); - const err = posix.getErrno(rc); - switch (err) { - 0 => return thread_ptr, - posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, - posix.EINVAL => unreachable, - posix.ENOMEM => return SpawnThreadError.SystemResources, - posix.ENOSPC => unreachable, - posix.EPERM => unreachable, - posix.EUSERS => unreachable, - else => return unexpectedErrorPosix(err), + if (builtin.os == builtin.Os.windows) { + // use windows API directly + @compileError("TODO support spawnThread for Windows"); + } else if (Thread.use_pthreads) { + // use pthreads + var attr: c.pthread_attr_t = undefined; + if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; + defer assert(c.pthread_attr_destroy(&attr) == 0); + + const stack_size = stack_end - @ptrToInt(stack.ptr); + if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) { + return SpawnThreadError.SystemResources; + } + + const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.SystemResources, + posix.EPERM => unreachable, + posix.EINVAL => unreachable, + else => return unexpectedErrorPosix(usize(err)), + } + } else if (builtin.os == builtin.Os.linux) { + // use linux API directly + const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND + | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS + | 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.pid, newtls, &thread_ptr.pid); + const err = posix.getErrno(rc); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, + posix.EINVAL => unreachable, + posix.ENOMEM => return SpawnThreadError.SystemResources, + posix.ENOSPC => unreachable, + posix.EPERM => unreachable, + posix.EUSERS => unreachable, + else => return unexpectedErrorPosix(err), + } + } else { + @compileError("Unsupported OS"); } } diff --git a/std/os/test.zig b/std/os/test.zig index 41afee004a..9a155c027a 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -44,8 +44,8 @@ test "access file" { } test "spawn threads" { - if (builtin.os != builtin.Os.linux) { - // TODO implement threads on macos and windows + if (builtin.os == builtin.Os.windows) { + // TODO implement threads on windows return; }