Commit 19459840fe

Andrew Kelley <andrew@ziglang.org>
2020-12-22 00:42:53
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.
1 parent 028af97
Changed files (2)
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;
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 {