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 @backingInt(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)) |_| @backingInt(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 @backingInt(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}