diff --git a/lib/std/c.zig b/lib/std/c.zig index f70c7e30c4..3fd39b26f5 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -8,9 +8,16 @@ pub usingnamespace switch (builtin.os) { .linux => @import("c/linux.zig"), .windows => @import("c/windows.zig"), .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), - .freebsd => @import("c/freebsd.zig"), + .freebsd, .kfreebsd => @import("c/freebsd.zig"), .netbsd => @import("c/netbsd.zig"), .dragonfly => @import("c/dragonfly.zig"), + .openbsd => @import("c/openbsd.zig"), + .haiku => @import("c/haiku.zig"), + .hermit => @import("c/hermit.zig"), + .solaris => @import("c/solaris.zig"), + .fuchsia => @import("c/fuchsia.zig"), + .minix => @import("c/minix.zig"), + .emscripten => @import("c/emscripten.zig"), else => struct {}, }; @@ -205,3 +212,16 @@ pub extern "c" fn dn_expand( ) c_int; pub extern "c" fn sched_yield() c_int; + +pub const PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}; +pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) c_int; +pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) c_int; +pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int; + +pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{}; +pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int; +pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int; +pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int; + +pub const pthread_t = *@OpaqueType(); +pub const FILE = @OpaqueType(); diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index f3cd77ec6f..eaccb1dcfd 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -112,3 +112,19 @@ pub const EAI_PROTOCOL = 13; /// argument buffer overflow pub const EAI_OVERFLOW = 14; pub const EAI_MAX = 15; + +pub const pthread_mutex_t = extern struct { + __sig: c_long = 0x32AAABA7, + __opaque: [__PTHREAD_MUTEX_SIZE__]u8 = [_]u8{0} ** __PTHREAD_MUTEX_SIZE__, +}; +pub const pthread_cond_t = extern struct { + __sig: c_long = 0x3CB0B1BB, + __opaque: [__PTHREAD_COND_SIZE__]u8 = [_]u8{0} ** __PTHREAD_COND_SIZE__, +}; +const __PTHREAD_MUTEX_SIZE__ = if (@sizeOf(usize) == 8) 56 else 40; +const __PTHREAD_COND_SIZE__ = if (@sizeOf(usize) == 8) 40 else 24; + +pub const pthread_attr_t = extern struct { + __sig: c_long, + __opaque: [56]u8, +}; diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index bd44a25514..a271b2e869 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -1,6 +1,5 @@ const std = @import("../std.zig"); usingnamespace std.c; - extern "c" threadlocal var errno: c_int; pub fn _errno() *c_int { return &errno; @@ -12,3 +11,15 @@ pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize, data: ?*c_void) c_int; pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; + +pub const pthread_mutex_t = extern struct { + inner: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + inner: ?*c_void = null, +}; + +pub const pthread_attr_t = extern struct { // copied from freebsd + __size: [56]u8, + __align: c_long, +}; diff --git a/lib/std/c/emscripten.zig b/lib/std/c/emscripten.zig new file mode 100644 index 0000000000..e91e1421c7 --- /dev/null +++ b/lib/std/c/emscripten.zig @@ -0,0 +1,8 @@ +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(4) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = 28; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 550b5a59b1..4f18df15fd 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -10,3 +10,15 @@ pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize, data: ?*c_void) c_int; pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; + +pub const pthread_mutex_t = extern struct { + inner: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + inner: ?*c_void = null, +}; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; diff --git a/lib/std/c/fuchsia.zig b/lib/std/c/fuchsia.zig new file mode 100644 index 0000000000..4f52b8900d --- /dev/null +++ b/lib/std/c/fuchsia.zig @@ -0,0 +1,8 @@ +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = 40; diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig new file mode 100644 index 0000000000..eb1e52a618 --- /dev/null +++ b/lib/std/c/haiku.zig @@ -0,0 +1,14 @@ +pub const pthread_mutex_t = extern struct { + flags: u32 = 0, + lock: i32 = 0, + unused: i32 = -42, + owner: i32 = -1, + owner_count: i32 = 0, +}; +pub const pthread_cond_t = extern struct { + flags: u32 = 0, + unused: i32 = -42, + mutex: ?*c_void = null, + waiter_count: i32 = 0, + lock: i32 = 0, +}; diff --git a/lib/std/c/hermit.zig b/lib/std/c/hermit.zig new file mode 100644 index 0000000000..b9be76c3ba --- /dev/null +++ b/lib/std/c/hermit.zig @@ -0,0 +1,6 @@ +pub const pthread_mutex_t = extern struct { + inner: usize = ~usize(0), +}; +pub const pthread_cond_t = extern struct { + inner: usize = ~usize(0), +}; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index a1db162e20..07fdceec9a 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -75,3 +75,26 @@ pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; + +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (builtin.abi) { + .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { + .aarch64 => 48, + .x86_64 => if (builtin.abi == .gnux32) 40 else 32, + .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40, + else => if (@sizeOf(usize) == 8) 40 else 24, + }, + else => unreachable, +}; diff --git a/lib/std/c/minix.zig b/lib/std/c/minix.zig new file mode 100644 index 0000000000..98ec087a93 --- /dev/null +++ b/lib/std/c/minix.zig @@ -0,0 +1,18 @@ +const builtin = @import("builtin"); +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = switch (builtin.abi) { + .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { + .aarch64 => 48, + .x86_64 => if (builtin.abi == .gnux32) 40 else 32, + .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40, + else => if (@sizeOf(usize) == 8) 40 else 24, + }, + else => unreachable, +}; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 417c78db69..c253362ac1 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -6,3 +6,32 @@ pub const _errno = __errno; pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; + +pub const pthread_mutex_t = extern struct { + ptm_magic: c_uint = 0x33330003, + ptm_errorcheck: padded_spin_t = 0, + ptm_unused: padded_spin_t = 0, + ptm_owner: usize = 0, + ptm_waiters: ?*u8 = null, + ptm_recursed: c_uint = 0, + ptm_spare2: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + ptc_magic: c_uint = 0x55550005, + ptc_lock: pthread_spin_t = 0, + ptc_waiters_first: ?*u8 = null, + ptc_waiters_last: ?*u8 = null, + ptc_mutex: ?*pthread_mutex_t = null, + ptc_private: ?*c_void = null, +}; +const pthread_spin_t = if (builtin.arch == .arm or .arch == .powerpc) c_int else u8; +const padded_spin_t = switch (builtin.arch) { + .sparc, .sparcel, .sparcv9, .i386, .x86_64, .le64 => u32, + else => spin_t, +}; + +pub const pthread_attr_t = extern struct { + pta_magic: u32, + pta_flags: c_int, + pta_private: *c_void, +}; diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig new file mode 100644 index 0000000000..22963b08c3 --- /dev/null +++ b/lib/std/c/openbsd.zig @@ -0,0 +1,6 @@ +pub const pthread_mutex_t = extern struct { + inner: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + inner: ?*c_void = null, +}; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig new file mode 100644 index 0000000000..7c70a01fc4 --- /dev/null +++ b/lib/std/c/solaris.zig @@ -0,0 +1,15 @@ +pub const pthread_mutex_t = extern struct { + __pthread_mutex_flag1: u16 = 0, + __pthread_mutex_flag2: u8 = 0, + __pthread_mutex_ceiling: u8 = 0, + __pthread_mutex_type: u16 = 0, + __pthread_mutex_magic: u16 = 0x4d58, + __pthread_mutex_lock: u64 = 0, + __pthread_mutex_data: u64 = 0, +}; +pub const pthread_cond_t = extern struct { + __pthread_cond_flag: u32 = 0, + __pthread_cond_type: u16 = 0, + __pthread_cond_magic: u16 = 0x4356, + __pthread_cond_data: u64 = 0, +}; diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index 5387bd4b15..9b44e2edc3 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -37,81 +37,84 @@ pub const Mutex = if (builtin.single_threaded) return Held{ .mutex = self }; } } -else struct { - state: u32, // TODO: make this an enum - parker: ThreadParker, +else + struct { + state: u32, // TODO: make this an enum + parker: ThreadParker, - const Unlocked = 0; - const Sleeping = 1; - const Locked = 2; + const Unlocked = 0; + const Sleeping = 1; + const Locked = 2; - /// number of iterations to spin yielding the cpu - const SPIN_CPU = 4; - /// number of iterations to perform in the cpu yield loop - const SPIN_CPU_COUNT = 30; - /// number of iterations to spin yielding the thread - const SPIN_THREAD = 1; + /// number of iterations to spin yielding the cpu + const SPIN_CPU = 4; - pub fn init() Mutex { - return Mutex{ - .state = Unlocked, - .parker = ThreadParker.init(), + /// number of iterations to perform in the cpu yield loop + const SPIN_CPU_COUNT = 30; + + /// number of iterations to spin yielding the thread + const SPIN_THREAD = 1; + + pub fn init() Mutex { + return Mutex{ + .state = Unlocked, + .parker = ThreadParker.init(), + }; + } + + pub fn deinit(self: *Mutex) void { + self.parker.deinit(); + } + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + switch (@atomicRmw(u32, &self.mutex.state, .Xchg, Unlocked, .Release)) { + Locked => {}, + Sleeping => self.mutex.parker.unpark(&self.mutex.state), + Unlocked => unreachable, // unlocking an unlocked mutex + else => unreachable, // should never be anything else + } + } }; - } - pub fn deinit(self: *Mutex) void { - self.parker.deinit(); - } + pub fn acquire(self: *Mutex) Held { + // Try and speculatively grab the lock. + // If it fails, the state is either Locked or Sleeping + // depending on if theres a thread stuck sleeping below. + var state = @atomicRmw(u32, &self.state, .Xchg, Locked, .Acquire); + if (state == Unlocked) + return Held{ .mutex = self }; - pub const Held = struct { - mutex: *Mutex, + while (true) { + // try and acquire the lock using cpu spinning on failure + var spin: usize = 0; + while (spin < SPIN_CPU) : (spin += 1) { + var value = @atomicLoad(u32, &self.state, .Monotonic); + while (value == Unlocked) + value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; + SpinLock.yield(SPIN_CPU_COUNT); + } - pub fn release(self: Held) void { - switch (@atomicRmw(u32, &self.mutex.state, .Xchg, Unlocked, .Release)) { - Locked => {}, - Sleeping => self.mutex.parker.unpark(&self.mutex.state), - Unlocked => unreachable, // unlocking an unlocked mutex - else => unreachable, // should never be anything else + // try and acquire the lock using thread rescheduling on failure + spin = 0; + while (spin < SPIN_THREAD) : (spin += 1) { + var value = @atomicLoad(u32, &self.state, .Monotonic); + while (value == Unlocked) + value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; + std.os.sched_yield() catch std.time.sleep(1); + } + + // failed to acquire the lock, go to sleep until woken up by `Held.release()` + if (@atomicRmw(u32, &self.state, .Xchg, Sleeping, .Acquire) == Unlocked) + return Held{ .mutex = self }; + state = Sleeping; + self.parker.park(&self.state, Sleeping); } } }; - pub fn acquire(self: *Mutex) Held { - // Try and speculatively grab the lock. - // If it fails, the state is either Locked or Sleeping - // depending on if theres a thread stuck sleeping below. - var state = @atomicRmw(u32, &self.state, .Xchg, Locked, .Acquire); - if (state == Unlocked) - return Held{ .mutex = self }; - - while (true) { - // try and acquire the lock using cpu spinning on failure - var spin: usize = 0; - while (spin < SPIN_CPU) : (spin += 1) { - var value = @atomicLoad(u32, &self.state, .Monotonic); - while (value == Unlocked) - value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; - SpinLock.yield(SPIN_CPU_COUNT); - } - - // try and acquire the lock using thread rescheduling on failure - spin = 0; - while (spin < SPIN_THREAD) : (spin += 1) { - var value = @atomicLoad(u32, &self.state, .Monotonic); - while (value == Unlocked) - value = @cmpxchgWeak(u32, &self.state, Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; - std.os.sched_yield(); - } - - // failed to acquire the lock, go to sleep until woken up by `Held.release()` - if (@atomicRmw(u32, &self.state, .Xchg, Sleeping, .Acquire) == Unlocked) - return Held{ .mutex = self }; - state = Sleeping; - self.parker.park(&self.state, Sleeping); - } - } -}; - const TestContext = struct { mutex: *Mutex, data: i128, diff --git a/lib/std/os.zig b/lib/std/os.zig index cf97a5ee45..6803006bf1 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3172,9 +3172,21 @@ pub fn dn_expand( return error.InvalidDnsPacket; } -pub fn sched_yield() void { - switch (builtin.os) { - .windows => _ = windows.kernel32.SwitchToThread(), - else => assert(system.sched_yield() == 0), +pub const SchedYieldError = error{ + /// The system is not configured to allow yielding + SystemCannotYield, +}; + +pub fn sched_yield() SchedYieldError!void { + if (builtin.os == .windows) { + // The return value has to do with how many other threads there are; it is not + // an error condition on Windows. + _ = windows.kernel32.SwitchToThread(); + return; + } + switch (errno(system.sched_yield())) { + 0 => return, + ENOSYS => return error.SystemCannotYield, + else => return error.SystemCannotYield, } } diff --git a/lib/std/os/bits.zig b/lib/std/os/bits.zig index 6d6163b8fa..7dd968e473 100644 --- a/lib/std/os/bits.zig +++ b/lib/std/os/bits.zig @@ -14,9 +14,6 @@ pub usingnamespace switch (builtin.os) { else => struct {}, }; -pub const pthread_t = *@OpaqueType(); -pub const FILE = @OpaqueType(); - pub const iovec = extern struct { iov_base: [*]u8, iov_len: usize, diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig index ca7f663661..335f296dd6 100644 --- a/lib/std/os/bits/darwin.zig +++ b/lib/std/os/bits/darwin.zig @@ -133,11 +133,6 @@ pub const dirent = extern struct { } }; -pub const pthread_attr_t = extern struct { - __sig: c_long, - __opaque: [56]u8, -}; - /// Renamed from `kevent` to `Kevent` to avoid conflict with function name. pub const Kevent = extern struct { ident: usize, diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index d1b65707f4..87d89df7d8 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -360,11 +360,6 @@ pub const Kevent = extern struct { udata: usize, }; -pub const pthread_attr_t = extern struct { // copied from freebsd - __size: [56]u8, - __align: c_long, -}; - pub const EVFILT_FS = -10; pub const EVFILT_USER = -9; pub const EVFILT_EXCEPT = -8; @@ -515,13 +510,13 @@ pub const sigset_t = extern struct { pub const sig_atomic_t = c_int; pub const Sigaction = extern struct { __sigaction_u: extern union { - __sa_handler: ?extern fn(c_int) void, - __sa_sigaction: ?extern fn(c_int, [*c]siginfo_t, ?*c_void) void, + __sa_handler: ?extern fn (c_int) void, + __sa_sigaction: ?extern fn (c_int, [*c]siginfo_t, ?*c_void) void, }, sa_flags: c_int, sa_mask: sigset_t, }; -pub const sig_t = [*c]extern fn(c_int) void; +pub const sig_t = [*c]extern fn (c_int) void; pub const sigvec = extern struct { sv_handler: [*c]__sighandler_t, diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index b0506ad0e1..a7a2536133 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -15,11 +15,6 @@ pub const Kevent = extern struct { // TODO ext }; -pub const pthread_attr_t = extern struct { - __size: [56]u8, - __align: c_long, -}; - pub const dl_phdr_info = extern struct { dlpi_addr: usize, dlpi_name: ?[*]const u8, diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 0cf30920e4..0fd528b0c0 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1000,11 +1000,6 @@ pub const dl_phdr_info = extern struct { dlpi_phnum: u16, }; -pub const pthread_attr_t = extern struct { - __size: [56]u8, - __align: c_long, -}; - pub const CPU_SETSIZE = 128; pub const cpu_set_t = [CPU_SETSIZE / @sizeOf(usize)]usize; pub const cpu_count_t = @IntType(false, std.math.log2(CPU_SETSIZE * 8)); diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index f93bbbf106..497c4a0344 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -14,12 +14,6 @@ pub const Kevent = extern struct { udata: usize, }; -pub const pthread_attr_t = extern struct { - pta_magic: u32, - pta_flags: c_int, - pta_private: *c_void, -}; - pub const dl_phdr_info = extern struct { dlpi_addr: usize, dlpi_name: ?[*]const u8, diff --git a/lib/std/parker.zig b/lib/std/parker.zig index 036fe160ce..4ba0100b9e 100644 --- a/lib/std/parker.zig +++ b/lib/std/parker.zig @@ -8,24 +8,9 @@ const linux = std.os.linux; const windows = std.os.windows; pub const ThreadParker = switch (builtin.os) { - .macosx, - .tvos, - .ios, - .watchos, - .netbsd, - .openbsd, - .freebsd, - .kfreebsd, - .dragonfly, - .haiku, - .hermit, - .solaris, - .minix, - .fuchsia, - .emscripten => if (builtin.link_libc) PosixParker else SpinParker, .linux => if (builtin.link_libc) PosixParker else LinuxParker, .windows => WindowsParker, - else => SpinParker, + else => if (builtin.link_libc) PosixParker else SpinParker, }; const SpinParker = struct { @@ -62,7 +47,7 @@ const LinuxParker = struct { 0, linux.EAGAIN => return, linux.EINTR => continue, linux.EINVAL => unreachable, - else => unreachable, + else => continue, } } } @@ -94,7 +79,7 @@ const WindowsParker = struct { var spin = SpinLock.Backoff.init(); const ev_handle = getEventHandle(); const key = @ptrCast(*const c_void, ptr); - + while (@atomicLoad(u32, ptr, .Monotonic) == expected) { if (ev_handle) |handle| { _ = @atomicRmw(u32, &self.waiters, .Add, 1, .Release); @@ -121,191 +106,64 @@ const WindowsParker = struct { }; const PosixParker = struct { - cond: pthread_cond_t, - mutex: pthread_mutex_t, + cond: c.pthread_cond_t, + mutex: c.pthread_mutex_t, + + const c = std.c; pub fn init() PosixParker { return PosixParker{ - .cond = PTHREAD_COND_INITIALIZER, - .mutex = PTHREAD_MUTEX_INITIALIZER, + .cond = c.PTHREAD_COND_INITIALIZER, + .mutex = c.PTHREAD_MUTEX_INITIALIZER, }; } pub fn deinit(self: *PosixParker) void { // On dragonfly, the destroy functions return EINVAL if they were initialized statically. - const retm = pthread_mutex_destroy(&self.mutex); + const retm = c.pthread_mutex_destroy(&self.mutex); assert(retm == 0 or retm == (if (builtin.os == .dragonfly) os.EINVAL else 0)); - const retc = pthread_cond_destroy(&self.cond); + const retc = c.pthread_cond_destroy(&self.cond); assert(retc == 0 or retc == (if (builtin.os == .dragonfly) os.EINVAL else 0)); } pub fn unpark(self: *PosixParker, ptr: *const u32) void { - assert(pthread_mutex_lock(&self.mutex) == 0); - defer assert(pthread_mutex_unlock(&self.mutex) == 0); - assert(pthread_cond_signal(&self.cond) == 0); + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + assert(c.pthread_cond_signal(&self.cond) == 0); } pub fn park(self: *PosixParker, ptr: *const u32, expected: u32) void { - assert(pthread_mutex_lock(&self.mutex) == 0); - defer assert(pthread_mutex_unlock(&self.mutex) == 0); + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); while (@atomicLoad(u32, ptr, .Acquire) == expected) - assert(pthread_cond_wait(&self.cond, &self.mutex) == 0); + assert(c.pthread_cond_wait(&self.cond, &self.mutex) == 0); } - - const PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}; - extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) c_int; - extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) c_int; - extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int; - - const PTHREAD_COND_INITIALIZER = pthread_cond_t{}; - extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int; - extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int; - extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int; - - // https://github.com/rust-lang/libc - usingnamespace switch (builtin.os) { - .macosx, .tvos, .ios, .watchos => struct { - pub const pthread_mutex_t = extern struct { - __sig: c_long = 0x32AAABA7, - __opaque: [__PTHREAD_MUTEX_SIZE__]u8 = [_]u8{0} ** __PTHREAD_MUTEX_SIZE__, - }; - pub const pthread_cond_t = extern struct { - __sig: c_long = 0x3CB0B1BB, - __opaque: [__PTHREAD_COND_SIZE__]u8 = [_]u8{0} ** __PTHREAD_COND_SIZE__, - }; - const __PTHREAD_MUTEX_SIZE__ = if (@sizeOf(usize) == 8) 56 else 40; - const __PTHREAD_COND_SIZE__ = if (@sizeOf(usize) == 8) 40 else 24; - }, - .netbsd => struct { - pub const pthread_mutex_t = extern struct { - ptm_magic: c_uint = 0x33330003, - ptm_errorcheck: padded_spin_t = 0, - ptm_unused: padded_spin_t = 0, - ptm_owner: usize = 0, - ptm_waiters: ?*u8 = null, - ptm_recursed: c_uint = 0, - ptm_spare2: ?*c_void = null, - }; - pub const pthread_cond_t = extern struct { - ptc_magic: c_uint = 0x55550005, - ptc_lock: pthread_spin_t = 0, - ptc_waiters_first: ?*u8 = null, - ptc_waiters_last: ?*u8 = null, - ptc_mutex: ?*pthread_mutex_t = null, - ptc_private: ?*c_void = null, - }; - const pthread_spin_t = if (builtin.arch == .arm or .arch == .powerpc) c_int else u8; - const padded_spin_t = switch (builtin.arch) { - .sparc, .sparcel, .sparcv9, .i386, .x86_64, .le64 => u32, - else => spin_t, - }; - }, - .openbsd, .freebsd, .kfreebsd, .dragonfly => struct { - pub const pthread_mutex_t = extern struct { - inner: ?*c_void = null, - }; - pub const pthread_cond_t = extern struct { - inner: ?*c_void = null, - }; - }, - .haiku => struct { - pub const pthread_mutex_t = extern struct { - flags: u32 = 0, - lock: i32 = 0, - unused: i32 = -42, - owner: i32 = -1, - owner_count: i32 = 0, - }; - pub const pthread_cond_t = extern struct { - flags: u32 = 0, - unused: i32 = -42, - mutex: ?*c_void = null, - waiter_count: i32 = 0, - lock: i32 = 0, - }; - }, - .hermit => struct { - pub const pthread_mutex_t = extern struct { - inner: usize = ~usize(0), - }; - pub const pthread_cond_t = extern struct { - inner: usize = ~usize(0), - }; - }, - .solaris => struct { - pub const pthread_mutex_t = extern struct { - __pthread_mutex_flag1: u16 = 0, - __pthread_mutex_flag2: u8 = 0, - __pthread_mutex_ceiling: u8 = 0, - __pthread_mutex_type: u16 = 0, - __pthread_mutex_magic: u16 = 0x4d58, - __pthread_mutex_lock: u64 = 0, - __pthread_mutex_data: u64 = 0, - }; - pub const pthread_cond_t = extern struct { - __pthread_cond_flag: u32 = 0, - __pthread_cond_type: u16 = 0, - __pthread_cond_magic: u16 = 0x4356, - __pthread_cond_data: u64 = 0, - }; - }, - .fuchsia, .minix, .linux => struct { - pub const pthread_mutex_t = extern struct { - size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, - }; - pub const pthread_cond_t = extern struct { - size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, - }; - const __SIZEOF_PTHREAD_COND_T = 48; - const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (builtin.abi) { - .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { - .aarch64 => 48, - .x86_64 => if (builtin.abi == .gnux32) 40 else 32, - .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40, - else => if (@sizeOf(usize) == 8) 40 else 24, - }, - else => unreachable, - }; - }, - .emscripten => struct { - pub const pthread_mutex_t = extern struct { - size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(4) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, - }; - pub const pthread_cond_t = extern struct { - size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, - }; - const __SIZEOF_PTHREAD_COND_T = 48; - const __SIZEOF_PTHREAD_MUTEX_T = 28; - }, - else => unreachable, - }; }; test "std.ThreadParker" { if (builtin.single_threaded) return error.SkipZigTest; - + const Context = struct { parker: ThreadParker, data: u32, fn receiver(self: *@This()) void { - self.parker.park(&self.data, 0); // receives 1 - assert(@atomicRmw(u32, &self.data, .Xchg, 2, .SeqCst) == 1); // sends 2 - self.parker.unpark(&self.data); // wakes up waiters on 2 - self.parker.park(&self.data, 2); // receives 3 - assert(@atomicRmw(u32, &self.data, .Xchg, 4, .SeqCst) == 3); // sends 4 - self.parker.unpark(&self.data); // wakes up waiters on 4 + self.parker.park(&self.data, 0); // receives 1 + assert(@atomicRmw(u32, &self.data, .Xchg, 2, .SeqCst) == 1); // sends 2 + self.parker.unpark(&self.data); // wakes up waiters on 2 + self.parker.park(&self.data, 2); // receives 3 + assert(@atomicRmw(u32, &self.data, .Xchg, 4, .SeqCst) == 3); // sends 4 + self.parker.unpark(&self.data); // wakes up waiters on 4 } fn sender(self: *@This()) void { - assert(@atomicRmw(u32, &self.data, .Xchg, 1, .SeqCst) == 0); // sends 1 - self.parker.unpark(&self.data); // wakes up waiters on 1 - self.parker.park(&self.data, 1); // receives 2 - assert(@atomicRmw(u32, &self.data, .Xchg, 3, .SeqCst) == 2); // sends 3 - self.parker.unpark(&self.data); // wakes up waiters on 3 - self.parker.park(&self.data, 3); // receives 4 + assert(@atomicRmw(u32, &self.data, .Xchg, 1, .SeqCst) == 0); // sends 1 + self.parker.unpark(&self.data); // wakes up waiters on 1 + self.parker.park(&self.data, 1); // receives 2 + assert(@atomicRmw(u32, &self.data, .Xchg, 3, .SeqCst) == 2); // sends 3 + self.parker.unpark(&self.data); // wakes up waiters on 3 + self.parker.park(&self.data, 3); // receives 4 } }; @@ -314,9 +172,9 @@ test "std.ThreadParker" { .data = 0, }; defer context.parker.deinit(); - + var receiver = try std.Thread.spawn(&context, Context.receiver); defer receiver.wait(); context.sender(); -} \ No newline at end of file +} diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig index b374eba3eb..3bed3d3891 100644 --- a/lib/std/spinlock.zig +++ b/lib/std/spinlock.zig @@ -31,8 +31,8 @@ pub const SpinLock = struct { var i = iterations; while (i != 0) : (i -= 1) { switch (builtin.arch) { - .i386, .x86_64 => asm volatile("pause"), - .arm, .aarch64 => asm volatile("yield"), + .i386, .x86_64 => asm volatile ("pause"), + .arm, .aarch64 => asm volatile ("yield"), else => time.sleep(0), } } @@ -53,7 +53,7 @@ pub const SpinLock = struct { if (self.iteration < 20) { SpinLock.yield(self.iteration); } else if (self.iteration < 24) { - os.sched_yield(); + os.sched_yield() catch time.sleep(1); } else if (self.iteration < 26) { time.sleep(1 * time.millisecond); } else {