*WIP* use pthreads when linking libc

master
Andrew Kelley 2018-04-28 23:30:13 -04:00
parent a10351b439
commit a344cb03bc
5 changed files with 107 additions and 29 deletions

View File

@ -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,
};

View File

@ -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();

View File

@ -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,
};

View File

@ -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");
}
}

View File

@ -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;
}