From 19459840fe0a1dcc39a4552a430a70fbf39b52ea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Dec 2020 16:42:53 -0700 Subject: [PATCH] std.ResetEvent: pthreads sem_t cannot be statically initialized because it is allowed for the implementation to use a file descriptor, which would require making a syscall at runtime. --- lib/std/c/linux.zig | 37 ++++-------------------------- lib/std/reset_event.zig | 51 +++++++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 76d0ddb9e0..8cc8a7cd72 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -123,6 +123,10 @@ pub const pthread_mutex_t = extern struct { pub const pthread_cond_t = extern struct { size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, }; +pub const sem_t = extern struct { + __size: [__SIZEOF_SEM_T]u8 align(@alignOf(usize)), +}; + const __SIZEOF_PTHREAD_COND_T = 48; const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch (builtin.abi) { .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, @@ -134,38 +138,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch }, else => unreachable, }; - -pub const sem_t = switch (builtin.abi) { - .musl, .musleabi, .musleabihf => extern struct { - __val: [4 * @sizeOf(c_long) / @sizeOf(c_int)]c_int, - - pub fn init(pshared: c_int, value: c_uint) @This() { - var result: @This() = undefined; - result.__val[0] = @bitCast(c_int, value); - result.__val[1] = 0; - result.__val[2] = if (pshared != 0) 0 else 128; - return result; - } - }, - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => extern struct { - __lock: c_int, - __queue: ?*pthread_t, - __pshared: c_int, - __value: c_int, - __data: ?*c_void, - - pub fn init(pshared: c_int, value: c_uint) @This() { - return .{ - .__lock = 0, - .__queue = null, - .__pshared = pshared, - .__value = @bitCast(c_int, value), - .__data = null, - }; - } - }, - else => unreachable, -}; +const __SIZEOF_SEM_T = 4 * @sizeOf(usize); pub const RTLD_LAZY = 1; pub const RTLD_NOW = 2; diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig index f0655e4e6a..cdbad71c75 100644 --- a/lib/std/reset_event.zig +++ b/lib/std/reset_event.zig @@ -101,30 +101,48 @@ const DebugEvent = struct { }; const PosixEvent = struct { - sem: c.sem_t, + sem: c.sem_t = undefined, + /// Sadly this is needed because pthreads semaphore API does not + /// support static initialization. + init_mutex: std.mutex.PthreadMutex = .{}, + state: enum { uninit, init } = .uninit, fn init() PosixEvent { - return PosixEvent{ - .sem = c.sem_t.init(0, 0), - }; + return .{}; } + /// Not thread-safe. fn deinit(self: *PosixEvent) void { - assert(c.sem_destroy(&self.sem) == 0); + switch (self.state) { + .uninit => {}, + .init => { + assert(c.sem_destroy(&self.sem) == 0); + }, + } + self.* = undefined; } fn reset(self: *PosixEvent) void { - self.deinit(); - assert(c.sem_init(&self.sem, 0, 0) == 0); + const sem = self.getInitializedSem(); + while (true) { + switch (c.getErrno(c.sem_trywait(sem))) { + 0 => continue, // Need to make it go to zero. + c.EINTR => continue, + c.EINVAL => unreachable, + c.EAGAIN => return, // The semaphore currently has the value zero. + else => unreachable, + } + } } fn set(self: *PosixEvent) void { - assert(c.sem_post(&self.sem) == 0); + assert(c.sem_post(self.getInitializedSem()) == 0); } fn wait(self: *PosixEvent) void { + const sem = self.getInitializedSem(); while (true) { - switch (c.getErrno(c.sem_wait(&self.sem))) { + switch (c.getErrno(c.sem_wait(sem))) { 0 => return, c.EINTR => continue, c.EINVAL => unreachable, @@ -148,6 +166,7 @@ const PosixEvent = struct { } ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s)); ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s)); + const sem = self.getInitializedSem(); while (true) { switch (c.getErrno(c.sem_timedwait(&self.sem, &ts))) { 0 => return, @@ -158,6 +177,20 @@ const PosixEvent = struct { } } } + + fn getInitializedSem(self: *PosixEvent) *c.sem_t { + const held = self.init_mutex.acquire(); + defer held.release(); + + switch (self.state) { + .init => return &self.sem, + .uninit => { + self.state = .init; + assert(c.sem_init(&self.sem, 0, 0) == 0); + return &self.sem; + }, + } + } }; const AtomicEvent = struct {