authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-21 22:29:33+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-21 22:29:33+02:00
logd4cac43d308ffa930cd7174567be7c1fe18e29fb
tree816130dae7af5c407f17e384eb3f7bd96b9a123b
parentcfde9303ff75322525746aa325026f0e12fb402c
parentf8e9593851d4305a24836bdd3636255fb8c4243a

Merge pull request 'libc: add common implementations of `pthread_spin_*`' (#31990) from GasInfinity/zig:pthread-spin into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31990 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

17 files changed, 106 insertions(+), 151 deletions(-)

lib/c.zig+1
......@@ -68,6 +68,7 @@ comptime {
6868 _ = @import("c/malloc.zig");
6969 }
7070 _ = @import("c/math.zig");
71 _ = @import("c/pthread.zig");
7172 _ = @import("c/search.zig");
7273 _ = @import("c/stdlib.zig");
7374 _ = @import("c/string.zig");
lib/c/pthread.zig created+57
......@@ -0,0 +1,57 @@
1const builtin = @import("builtin");
2
3const std = @import("std");
4const c = std.c;
5
6const symbol = @import("../c.zig").symbol;
7
8comptime {
9 if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC() or builtin.target.isMinGW()) {
10 symbol(&pthread_spin_init, "pthread_spin_init");
11 symbol(&pthread_spin_destroy, "pthread_spin_destroy");
12 symbol(&pthread_spin_trylock, "pthread_spin_trylock");
13 symbol(&pthread_spin_lock, "pthread_spin_lock");
14 symbol(&pthread_spin_unlock, "pthread_spin_unlock");
15 }
16}
17
18const SpinLock = enum(c.pthread_spinlock_t) {
19 unlocked = if (builtin.target.isMinGW()) -1 else 0,
20 locked = if (builtin.target.isMinGW()) 0 else @intFromEnum(c.E.BUSY),
21};
22
23fn pthread_spin_init(s: *c.pthread_spinlock_t, pshared: c_int) callconv(.c) c_int {
24 _ = pshared;
25 const spin: *SpinLock = @ptrCast(s);
26 spin.* = .unlocked;
27 return 0;
28}
29
30fn pthread_spin_destroy(s: *c.pthread_spinlock_t) callconv(.c) c_int {
31 const spin: *SpinLock = @ptrCast(s);
32 spin.* = undefined;
33 return 0;
34}
35
36fn pthread_spin_trylock(s: *c.pthread_spinlock_t) callconv(.c) c_int {
37 const spin: *SpinLock = @ptrCast(s);
38 return if (@cmpxchgStrong(SpinLock, spin, .unlocked, .locked, .acquire, .monotonic)) |_| @intFromEnum(c.E.BUSY) else 0;
39}
40
41fn pthread_spin_lock(s: *c.pthread_spinlock_t) callconv(.c) c_int {
42 const spin: *SpinLock = @ptrCast(s);
43 if (builtin.single_threaded and @atomicLoad(SpinLock, spin, .monotonic) == .locked) return @intFromEnum(c.E.DEADLK);
44
45 while (@cmpxchgWeak(SpinLock, spin, .unlocked, .locked, .acquire, .monotonic)) |_| {
46 std.atomic.spinLoopHint();
47 }
48 return 0;
49}
50
51fn pthread_spin_unlock(s: *c.pthread_spinlock_t) callconv(.c) c_int {
52 const spin: *SpinLock = @ptrCast(s);
53
54 // "The results are undefined if the lock is not held by the calling thread"
55 std.debug.assert(@atomicRmw(SpinLock, spin, .Xchg, .unlocked, .release) == .locked);
56 return 0;
57}
lib/libc/mingw/winpthreads/spinlock.c deleted-82
......@@ -1,82 +0,0 @@
1/*
2 Copyright (c) 2013 mingw-w64 project
3 Copyright (c) 2015 Intel Corporation
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22*/
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#define WIN32_LEAN_AND_MEAN
29#include <windows.h>
30
31/* public header files */
32#include "pthread.h"
33/* internal header files */
34#include "misc.h"
35
36/* We use the pthread_spinlock_t itself as a lock:
37 -1 is free, 0 is locked.
38 (This is dictated by PTHREAD_SPINLOCK_INITIALIZER, which we can't change
39 without breaking binary compatibility.) */
40typedef intptr_t spinlock_word_t;
41
42int
43pthread_spin_init (pthread_spinlock_t *lock, int pshared)
44{
45 spinlock_word_t *lk = (spinlock_word_t *)lock;
46 *lk = -1;
47 return 0;
48}
49
50
51int
52pthread_spin_destroy (pthread_spinlock_t *lock)
53{
54 return 0;
55}
56
57int
58pthread_spin_lock (pthread_spinlock_t *lock)
59{
60 volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock;
61 while (unlikely(InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0))
62 do {
63 YieldProcessor();
64 } while (*lk == 0);
65 return 0;
66}
67
68int
69pthread_spin_trylock (pthread_spinlock_t *lock)
70{
71 spinlock_word_t *lk = (spinlock_word_t *)lock;
72 return InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0 ? EBUSY : 0;
73}
74
75
76int
77pthread_spin_unlock (pthread_spinlock_t *lock)
78{
79 volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock;
80 *lk = -1;
81 return 0;
82}
lib/libc/musl/src/thread/pthread_spin_destroy.c deleted-6
......@@ -1,6 +0,0 @@
1#include "pthread_impl.h"
2
3int pthread_spin_destroy(pthread_spinlock_t *s)
4{
5 return 0;
6}
lib/libc/musl/src/thread/pthread_spin_init.c deleted-6
......@@ -1,6 +0,0 @@
1#include "pthread_impl.h"
2
3int pthread_spin_init(pthread_spinlock_t *s, int shared)
4{
5 return *s = 0;
6}
lib/libc/musl/src/thread/pthread_spin_lock.c deleted-8
......@@ -1,8 +0,0 @@
1#include "pthread_impl.h"
2#include <errno.h>
3
4int pthread_spin_lock(pthread_spinlock_t *s)
5{
6 while (*(volatile int *)s || a_cas(s, 0, EBUSY)) a_spin();
7 return 0;
8}
lib/libc/musl/src/thread/pthread_spin_trylock.c deleted-7
......@@ -1,7 +0,0 @@
1#include "pthread_impl.h"
2#include <errno.h>
3
4int pthread_spin_trylock(pthread_spinlock_t *s)
5{
6 return a_cas(s, 0, EBUSY);
7}
lib/libc/musl/src/thread/pthread_spin_unlock.c deleted-7
......@@ -1,7 +0,0 @@
1#include "pthread_impl.h"
2
3int pthread_spin_unlock(pthread_spinlock_t *s)
4{
5 a_store(s, 0);
6 return 0;
7}
lib/libc/wasi/thread-stub/pthread_spin_lock.c deleted-8
......@@ -1,8 +0,0 @@
1#include "pthread_impl.h"
2
3int pthread_spin_lock(pthread_spinlock_t *s)
4{
5 if (*s) return EDEADLK;
6 *s = 1;
7 return 0;
8}
lib/libc/wasi/thread-stub/pthread_spin_trylock.c deleted-8
......@@ -1,8 +0,0 @@
1#include "pthread_impl.h"
2
3int pthread_spin_trylock(pthread_spinlock_t *s)
4{
5 if (*s) return EBUSY;
6 *s = 1;
7 return 0;
8}
lib/libc/wasi/thread-stub/pthread_spin_unlock.c deleted-7
......@@ -1,7 +0,0 @@
1#include "pthread_impl.h"
2
3int pthread_spin_unlock(pthread_spinlock_t *s)
4{
5 *s = 0;
6 return 0;
7}
lib/std/c.zig+27-1
......@@ -7898,6 +7898,20 @@ pub const Stat = switch (native_os) {
78987898 else => void,
78997899};
79007900
7901pub const pthread_spinlock_t = switch (native_os) {
7902 .openbsd => openbsd.pthread_spinlock_t,
7903 .freebsd => extern struct {
7904 inner: ?*anyopaque = null,
7905 },
7906 .netbsd => extern struct {
7907 pts_magic: c_uint,
7908 spin: pthread_spin_t,
7909 pts_flags: c_int,
7910 },
7911 .windows => isize,
7912 else => c_int,
7913};
7914
79017915pub const pthread_mutex_t = switch (native_os) {
79027916 .linux => extern struct {
79037917 data: [data_len]u8 align(@alignOf(usize)) = [_]u8{0} ** data_len,
......@@ -10956,6 +10970,19 @@ pub extern "c" fn dn_expand(
1095610970 length: c_int,
1095710971) c_int;
1095810972
10973pub const PTHREAD_PROCESS_PRIVATE: c_int = if (native_os.isDarwin())
10974 2
10975else
10976 0;
10977
10978pub const PTHREAD_PROCESS_SHARED: c_int = 1;
10979
10980pub extern "c" fn pthread_spin_init(spin: *pthread_spinlock_t, pshared: c_int) c_int;
10981pub extern "c" fn pthread_spin_lock(spin: *pthread_spinlock_t) c_int;
10982pub extern "c" fn pthread_spin_unlock(spin: *pthread_spinlock_t) c_int;
10983pub extern "c" fn pthread_spin_trylock(spin: *pthread_spinlock_t) c_int;
10984pub extern "c" fn pthread_spin_destroy(spin: *pthread_spinlock_t) c_int;
10985
1095910986pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = .{};
1096010987pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) E;
1096110988pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) E;
......@@ -11268,7 +11295,6 @@ pub const login_getcaptime = openbsd.login_getcaptime;
1126811295pub const login_getclass = openbsd.login_getclass;
1126911296pub const login_getstyle = openbsd.login_getstyle;
1127011297pub const pledge = openbsd.pledge;
11271pub const pthread_spinlock_t = openbsd.pthread_spinlock_t;
1127211298pub const pw_dup = openbsd.pw_dup;
1127311299pub const setclasscontext = openbsd.setclasscontext;
1127411300pub const setpassent = openbsd.setpassent;
src/libs/mingw.zig-1
......@@ -913,7 +913,6 @@ const mingw32_winpthreads_src = [_][]const u8{
913913 "winpthreads" ++ path.sep_str ++ "rwlock.c",
914914 "winpthreads" ++ path.sep_str ++ "sched.c",
915915 "winpthreads" ++ path.sep_str ++ "sem.c",
916 "winpthreads" ++ path.sep_str ++ "spinlock.c",
917916 "winpthreads" ++ path.sep_str ++ "thread.c",
918917};
919918
src/libs/musl.zig-5
......@@ -1663,11 +1663,6 @@ const src_files = [_][]const u8{
16631663 "musl/src/thread/pthread_setschedprio.c",
16641664 "musl/src/thread/pthread_setspecific.c",
16651665 "musl/src/thread/pthread_sigmask.c",
1666 "musl/src/thread/pthread_spin_destroy.c",
1667 "musl/src/thread/pthread_spin_init.c",
1668 "musl/src/thread/pthread_spin_lock.c",
1669 "musl/src/thread/pthread_spin_trylock.c",
1670 "musl/src/thread/pthread_spin_unlock.c",
16711666 "musl/src/thread/pthread_testcancel.c",
16721667 "musl/src/thread/riscv32/clone.s",
16731668 "musl/src/thread/riscv32/__set_thread_area.s",
src/libs/wasi_libc.zig-5
......@@ -939,8 +939,6 @@ const libc_top_half_src_files = [_][]const u8{
939939 "musl/src/thread/pthread_setcancelstate.c",
940940 "musl/src/thread/pthread_setcanceltype.c",
941941 "musl/src/thread/pthread_setspecific.c",
942 "musl/src/thread/pthread_spin_destroy.c",
943 "musl/src/thread/pthread_spin_init.c",
944942 "musl/src/thread/pthread_testcancel.c",
945943 "musl/src/thread/thrd_sleep.c",
946944 "musl/src/time/asctime.c",
......@@ -1086,9 +1084,6 @@ const libc_top_half_src_files = [_][]const u8{
10861084 "wasi/thread-stub/pthread_rwlock_trywrlock.c",
10871085 "wasi/thread-stub/pthread_rwlock_unlock.c",
10881086 "wasi/thread-stub/pthread_rwlock_wrlock.c",
1089 "wasi/thread-stub/pthread_spin_lock.c",
1090 "wasi/thread-stub/pthread_spin_trylock.c",
1091 "wasi/thread-stub/pthread_spin_unlock.c",
10921087};
10931088
10941089const crt1_command_src_file = "wasi/libc-bottom-half/crt/crt1-command.c";
test/c.zig+1
......@@ -4,6 +4,7 @@ const std = @import("std");
44test {
55 _ = @import("c/inttypes.zig");
66 _ = @import("c/math.zig");
7 _ = @import("c/pthread.zig");
78 _ = @import("c/search.zig");
89 _ = @import("c/stdlib.zig");
910 _ = @import("c/string.zig");
test/c/pthread.zig created+20
......@@ -0,0 +1,20 @@
1const builtin = @import("builtin");
2const std = @import("std");
3
4const c = std.c;
5const math = std.math;
6const testing = std.testing;
7
8test "pthread_spinlock_t" {
9 if (builtin.target.os.tag.isDarwin()) return; // Darwin doesn't have `pthread_spin_*`
10
11 var spin: c.pthread_spinlock_t = undefined;
12 _ = c.pthread_spin_init(&spin, c.PTHREAD_PROCESS_PRIVATE);
13 defer _ = c.pthread_spin_destroy(&spin);
14
15 try std.testing.expectEqual(@intFromEnum(c.E.SUCCESS), c.pthread_spin_trylock(&spin));
16 try std.testing.expectEqual(@intFromEnum(c.E.SUCCESS), c.pthread_spin_unlock(&spin));
17
18 try std.testing.expectEqual(@intFromEnum(c.E.SUCCESS), c.pthread_spin_lock(&spin));
19 try std.testing.expectEqual(@intFromEnum(c.E.SUCCESS), c.pthread_spin_unlock(&spin));
20}