commit 61cd38e8ace66de64b2d3e53b2ebc327a979c4ef (tree)
parent 9b177a7d21250b82cd18677c5c71ab04e431120d
Author: GasInfinity <me@gasinfinity.dev>
Date: Tue, 21 Apr 2026 08:54:54 +0200
feat(libzigc): add common implementations of `pthread_spin_*`
* and remove their mingw, musl and wasi implementations
Diffstat:
17 files changed, 98 insertions(+), 151 deletions(-)
diff --git a/lib/c.zig b/lib/c.zig
@@ -68,6 +68,7 @@ comptime {
_ = @import("c/malloc.zig");
}
_ = @import("c/math.zig");
+ _ = @import("c/pthread.zig");
_ = @import("c/search.zig");
_ = @import("c/stdlib.zig");
_ = @import("c/string.zig");
diff --git a/lib/c/pthread.zig b/lib/c/pthread.zig
@@ -0,0 +1,57 @@
+const builtin = @import("builtin");
+
+const std = @import("std");
+const c = std.c;
+
+const symbol = @import("../c.zig").symbol;
+
+comptime {
+ if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC() or builtin.target.isMinGW()) {
+ symbol(&pthread_spin_init, "pthread_spin_init");
+ symbol(&pthread_spin_destroy, "pthread_spin_destroy");
+ symbol(&pthread_spin_trylock, "pthread_spin_trylock");
+ symbol(&pthread_spin_lock, "pthread_spin_lock");
+ symbol(&pthread_spin_unlock, "pthread_spin_unlock");
+ }
+}
+
+const SpinLock = enum(c.pthread_spinlock_t) {
+ unlocked = if (builtin.target.isMinGW()) -1 else 0,
+ locked = if (builtin.target.isMinGW()) 0 else @intFromEnum(c.E.BUSY),
+};
+
+fn pthread_spin_init(s: *c.pthread_spinlock_t, pshared: c_int) callconv(.c) c_int {
+ _ = pshared;
+ const spin: *SpinLock = @ptrCast(s);
+ spin.* = .unlocked;
+ return 0;
+}
+
+fn pthread_spin_destroy(s: *c.pthread_spinlock_t) callconv(.c) c_int {
+ const spin: *SpinLock = @ptrCast(s);
+ spin.* = undefined;
+ return 0;
+}
+
+fn pthread_spin_trylock(s: *c.pthread_spinlock_t) callconv(.c) c_int {
+ const spin: *SpinLock = @ptrCast(s);
+ return if (@cmpxchgStrong(SpinLock, spin, .unlocked, .locked, .acquire, .monotonic)) |_| @intFromEnum(c.E.BUSY) else 0;
+}
+
+fn pthread_spin_lock(s: *c.pthread_spinlock_t) callconv(.c) c_int {
+ const spin: *SpinLock = @ptrCast(s);
+ if (builtin.single_threaded and @atomicLoad(SpinLock, spin, .monotonic) == .locked) return @intFromEnum(c.E.DEADLK);
+
+ while (@cmpxchgWeak(SpinLock, spin, .unlocked, .locked, .acquire, .monotonic)) |_| {
+ std.atomic.spinLoopHint();
+ }
+ return 0;
+}
+
+fn pthread_spin_unlock(s: *c.pthread_spinlock_t) callconv(.c) c_int {
+ const spin: *SpinLock = @ptrCast(s);
+
+ // "The results are undefined if the lock is not held by the calling thread"
+ std.debug.assert(@atomicRmw(SpinLock, spin, .Xchg, .unlocked, .release) == .locked);
+ return 0;
+}
diff --git a/lib/libc/mingw/winpthreads/spinlock.c b/lib/libc/mingw/winpthreads/spinlock.c
@@ -1,82 +0,0 @@
-/*
- Copyright (c) 2013 mingw-w64 project
- Copyright (c) 2015 Intel Corporation
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-/* public header files */
-#include "pthread.h"
-/* internal header files */
-#include "misc.h"
-
-/* We use the pthread_spinlock_t itself as a lock:
- -1 is free, 0 is locked.
- (This is dictated by PTHREAD_SPINLOCK_INITIALIZER, which we can't change
- without breaking binary compatibility.) */
-typedef intptr_t spinlock_word_t;
-
-int
-pthread_spin_init (pthread_spinlock_t *lock, int pshared)
-{
- spinlock_word_t *lk = (spinlock_word_t *)lock;
- *lk = -1;
- return 0;
-}
-
-
-int
-pthread_spin_destroy (pthread_spinlock_t *lock)
-{
- return 0;
-}
-
-int
-pthread_spin_lock (pthread_spinlock_t *lock)
-{
- volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock;
- while (unlikely(InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0))
- do {
- YieldProcessor();
- } while (*lk == 0);
- return 0;
-}
-
-int
-pthread_spin_trylock (pthread_spinlock_t *lock)
-{
- spinlock_word_t *lk = (spinlock_word_t *)lock;
- return InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0 ? EBUSY : 0;
-}
-
-
-int
-pthread_spin_unlock (pthread_spinlock_t *lock)
-{
- volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock;
- *lk = -1;
- return 0;
-}
diff --git a/lib/libc/musl/src/thread/pthread_spin_destroy.c b/lib/libc/musl/src/thread/pthread_spin_destroy.c
@@ -1,6 +0,0 @@
-#include "pthread_impl.h"
-
-int pthread_spin_destroy(pthread_spinlock_t *s)
-{
- return 0;
-}
diff --git a/lib/libc/musl/src/thread/pthread_spin_init.c b/lib/libc/musl/src/thread/pthread_spin_init.c
@@ -1,6 +0,0 @@
-#include "pthread_impl.h"
-
-int pthread_spin_init(pthread_spinlock_t *s, int shared)
-{
- return *s = 0;
-}
diff --git a/lib/libc/musl/src/thread/pthread_spin_lock.c b/lib/libc/musl/src/thread/pthread_spin_lock.c
@@ -1,8 +0,0 @@
-#include "pthread_impl.h"
-#include <errno.h>
-
-int pthread_spin_lock(pthread_spinlock_t *s)
-{
- while (*(volatile int *)s || a_cas(s, 0, EBUSY)) a_spin();
- return 0;
-}
diff --git a/lib/libc/musl/src/thread/pthread_spin_trylock.c b/lib/libc/musl/src/thread/pthread_spin_trylock.c
@@ -1,7 +0,0 @@
-#include "pthread_impl.h"
-#include <errno.h>
-
-int pthread_spin_trylock(pthread_spinlock_t *s)
-{
- return a_cas(s, 0, EBUSY);
-}
diff --git a/lib/libc/musl/src/thread/pthread_spin_unlock.c b/lib/libc/musl/src/thread/pthread_spin_unlock.c
@@ -1,7 +0,0 @@
-#include "pthread_impl.h"
-
-int pthread_spin_unlock(pthread_spinlock_t *s)
-{
- a_store(s, 0);
- return 0;
-}
diff --git a/lib/libc/wasi/thread-stub/pthread_spin_lock.c b/lib/libc/wasi/thread-stub/pthread_spin_lock.c
@@ -1,8 +0,0 @@
-#include "pthread_impl.h"
-
-int pthread_spin_lock(pthread_spinlock_t *s)
-{
- if (*s) return EDEADLK;
- *s = 1;
- return 0;
-}
diff --git a/lib/libc/wasi/thread-stub/pthread_spin_trylock.c b/lib/libc/wasi/thread-stub/pthread_spin_trylock.c
@@ -1,8 +0,0 @@
-#include "pthread_impl.h"
-
-int pthread_spin_trylock(pthread_spinlock_t *s)
-{
- if (*s) return EBUSY;
- *s = 1;
- return 0;
-}
diff --git a/lib/libc/wasi/thread-stub/pthread_spin_unlock.c b/lib/libc/wasi/thread-stub/pthread_spin_unlock.c
@@ -1,7 +0,0 @@
-#include "pthread_impl.h"
-
-int pthread_spin_unlock(pthread_spinlock_t *s)
-{
- *s = 0;
- return 0;
-}
diff --git a/lib/std/c.zig b/lib/std/c.zig
@@ -7898,6 +7898,12 @@ pub const Stat = switch (native_os) {
else => void,
};
+pub const pthread_spinlock_t = switch (native_os) {
+ .openbsd => openbsd.pthread_spinlock_t,
+ .windows => isize,
+ else => c_int,
+};
+
pub const pthread_mutex_t = switch (native_os) {
.linux => extern struct {
data: [data_len]u8 align(@alignOf(usize)) = [_]u8{0} ** data_len,
@@ -10956,6 +10962,19 @@ pub extern "c" fn dn_expand(
length: c_int,
) c_int;
+pub const PTHREAD_PROCESS_PRIVATE: c_int = if (native_os.isDarwin())
+ 2
+else
+ 0;
+
+pub const PTHREAD_PROCESS_SHARED: c_int = 1;
+
+pub extern "c" fn pthread_spin_init(spin: *pthread_spinlock_t, pshared: c_int) E;
+pub extern "c" fn pthread_spin_lock(spin: *pthread_spinlock_t) E;
+pub extern "c" fn pthread_spin_unlock(spin: *pthread_spinlock_t) E;
+pub extern "c" fn pthread_spin_trylock(spin: *pthread_spinlock_t) E;
+pub extern "c" fn pthread_spin_destroy(spin: *pthread_spinlock_t) E;
+
pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = .{};
pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) E;
pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) E;
@@ -11268,7 +11287,6 @@ pub const login_getcaptime = openbsd.login_getcaptime;
pub const login_getclass = openbsd.login_getclass;
pub const login_getstyle = openbsd.login_getstyle;
pub const pledge = openbsd.pledge;
-pub const pthread_spinlock_t = openbsd.pthread_spinlock_t;
pub const pw_dup = openbsd.pw_dup;
pub const setclasscontext = openbsd.setclasscontext;
pub const setpassent = openbsd.setpassent;
diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig
@@ -913,7 +913,6 @@ const mingw32_winpthreads_src = [_][]const u8{
"winpthreads" ++ path.sep_str ++ "rwlock.c",
"winpthreads" ++ path.sep_str ++ "sched.c",
"winpthreads" ++ path.sep_str ++ "sem.c",
- "winpthreads" ++ path.sep_str ++ "spinlock.c",
"winpthreads" ++ path.sep_str ++ "thread.c",
};
diff --git a/src/libs/musl.zig b/src/libs/musl.zig
@@ -1663,11 +1663,6 @@ const src_files = [_][]const u8{
"musl/src/thread/pthread_setschedprio.c",
"musl/src/thread/pthread_setspecific.c",
"musl/src/thread/pthread_sigmask.c",
- "musl/src/thread/pthread_spin_destroy.c",
- "musl/src/thread/pthread_spin_init.c",
- "musl/src/thread/pthread_spin_lock.c",
- "musl/src/thread/pthread_spin_trylock.c",
- "musl/src/thread/pthread_spin_unlock.c",
"musl/src/thread/pthread_testcancel.c",
"musl/src/thread/riscv32/clone.s",
"musl/src/thread/riscv32/__set_thread_area.s",
diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig
@@ -939,8 +939,6 @@ const libc_top_half_src_files = [_][]const u8{
"musl/src/thread/pthread_setcancelstate.c",
"musl/src/thread/pthread_setcanceltype.c",
"musl/src/thread/pthread_setspecific.c",
- "musl/src/thread/pthread_spin_destroy.c",
- "musl/src/thread/pthread_spin_init.c",
"musl/src/thread/pthread_testcancel.c",
"musl/src/thread/thrd_sleep.c",
"musl/src/time/asctime.c",
@@ -1086,9 +1084,6 @@ const libc_top_half_src_files = [_][]const u8{
"wasi/thread-stub/pthread_rwlock_trywrlock.c",
"wasi/thread-stub/pthread_rwlock_unlock.c",
"wasi/thread-stub/pthread_rwlock_wrlock.c",
- "wasi/thread-stub/pthread_spin_lock.c",
- "wasi/thread-stub/pthread_spin_trylock.c",
- "wasi/thread-stub/pthread_spin_unlock.c",
};
const crt1_command_src_file = "wasi/libc-bottom-half/crt/crt1-command.c";
diff --git a/test/c.zig b/test/c.zig
@@ -4,6 +4,7 @@ const std = @import("std");
test {
_ = @import("c/inttypes.zig");
_ = @import("c/math.zig");
+ _ = @import("c/pthread.zig");
_ = @import("c/search.zig");
_ = @import("c/stdlib.zig");
_ = @import("c/string.zig");
diff --git a/test/c/pthread.zig b/test/c/pthread.zig
@@ -0,0 +1,20 @@
+const builtin = @import("builtin");
+const std = @import("std");
+
+const c = std.c;
+const math = std.math;
+const testing = std.testing;
+
+test "pthread_spinlock_t" {
+ if (builtin.target.os.tag.isDarwin()) return; // Darwin doesn't have `pthread_spin_*`
+
+ var spin: c.pthread_spinlock_t = undefined;
+ _ = c.pthread_spin_init(&spin, c.PTHREAD_PROCESS_PRIVATE);
+ defer _ = c.pthread_spin_destroy(&spin);
+
+ try std.testing.expectEqual(.SUCCESS, c.pthread_spin_trylock(&spin));
+ try std.testing.expectEqual(.SUCCESS, c.pthread_spin_unlock(&spin));
+
+ try std.testing.expectEqual(.SUCCESS, c.pthread_spin_lock(&spin));
+ try std.testing.expectEqual(.SUCCESS, c.pthread_spin_unlock(&spin));
+}