| ... | @@ -265,6 +265,10 @@ const have_flock_open_flags = @hasField(posix.O, "EXLOCK"); | ... | @@ -265,6 +265,10 @@ const have_flock_open_flags = @hasField(posix.O, "EXLOCK"); |
| 265 | const have_networking = builtin.os.tag != .wasi; | 265 | const have_networking = builtin.os.tag != .wasi; |
| 266 | const have_flock = @TypeOf(posix.system.flock) != void; | 266 | const have_flock = @TypeOf(posix.system.flock) != void; |
| 267 | const have_sendmmsg = builtin.os.tag == .linux; | 267 | const have_sendmmsg = builtin.os.tag == .linux; |
| | 268 | const have_futex = switch (builtin.cpu.arch) { |
| | 269 | .wasm32, .wasm64 => builtin.cpu.has(.wasm, .atomics), |
| | 270 | else => true, |
| | 271 | }; |
| 268 | | 272 | |
| 269 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; | 273 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| 270 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; | 274 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; |
| ... | @@ -731,6 +735,7 @@ fn checkCancel(t: *Threaded) error{Canceled}!void { | ... | @@ -731,6 +735,7 @@ fn checkCancel(t: *Threaded) error{Canceled}!void { |
| 731 | } | 735 | } |
| 732 | | 736 | |
| 733 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) Io.Cancelable!void { | 737 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) Io.Cancelable!void { |
| | 738 | if (builtin.single_threaded) unreachable; // Interface should have prevented this. |
| 734 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 739 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 735 | if (prev_state == .contended) { | 740 | if (prev_state == .contended) { |
| 736 | try futexWait(t, @ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); | 741 | try futexWait(t, @ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); |
| ... | @@ -741,6 +746,7 @@ fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex | ... | @@ -741,6 +746,7 @@ fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex |
| 741 | } | 746 | } |
| 742 | | 747 | |
| 743 | fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { | 748 | fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { |
| | 749 | if (builtin.single_threaded) unreachable; // Interface should have prevented this. |
| 744 | _ = userdata; | 750 | _ = userdata; |
| 745 | if (prev_state == .contended) { | 751 | if (prev_state == .contended) { |
| 746 | futexWaitUncancelable(@ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); | 752 | futexWaitUncancelable(@ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); |
| ... | @@ -751,6 +757,7 @@ fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mute | ... | @@ -751,6 +757,7 @@ fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mute |
| 751 | } | 757 | } |
| 752 | | 758 | |
| 753 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { | 759 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { |
| | 760 | if (builtin.single_threaded) unreachable; // Interface should have prevented this. |
| 754 | _ = userdata; | 761 | _ = userdata; |
| 755 | _ = prev_state; | 762 | _ = prev_state; |
| 756 | if (@atomicRmw(Io.Mutex.State, &mutex.state, .Xchg, .unlocked, .release) == .contended) { | 763 | if (@atomicRmw(Io.Mutex.State, &mutex.state, .Xchg, .unlocked, .release) == .contended) { |
| ... | @@ -759,6 +766,7 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut | ... | @@ -759,6 +766,7 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut |
| 759 | } | 766 | } |
| 760 | | 767 | |
| 761 | fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) void { | 768 | fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) void { |
| | 769 | if (builtin.single_threaded) unreachable; // Deadlock. |
| 762 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 770 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 763 | const t_io = t.io(); | 771 | const t_io = t.io(); |
| 764 | comptime assert(@TypeOf(cond.state) == u64); | 772 | comptime assert(@TypeOf(cond.state) == u64); |
| ... | @@ -789,6 +797,7 @@ fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: | ... | @@ -789,6 +797,7 @@ fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: |
| 789 | } | 797 | } |
| 790 | | 798 | |
| 791 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { | 799 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { |
| | 800 | if (builtin.single_threaded) unreachable; // Deadlock. |
| 792 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 801 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 793 | comptime assert(@TypeOf(cond.state) == u64); | 802 | comptime assert(@TypeOf(cond.state) == u64); |
| 794 | const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state); | 803 | const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state); |
| ... | @@ -833,6 +842,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I | ... | @@ -833,6 +842,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I |
| 833 | } | 842 | } |
| 834 | | 843 | |
| 835 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { | 844 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { |
| | 845 | if (builtin.single_threaded) unreachable; // Nothing to wake up. |
| 836 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 846 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 837 | _ = t; | 847 | _ = t; |
| 838 | comptime assert(@TypeOf(cond.state) == u64); | 848 | comptime assert(@TypeOf(cond.state) == u64); |
| ... | @@ -4007,6 +4017,32 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca | ... | @@ -4007,6 +4017,32 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca |
| 4007 | return; | 4017 | return; |
| 4008 | } | 4018 | } |
| 4009 | | 4019 | |
| | 4020 | if (builtin.cpu.arch.isWasm()) { |
| | 4021 | comptime assert(builtin.cpu.has(.wasm, .atomics)); |
| | 4022 | try t.checkCancel(); |
| | 4023 | const timeout: i64 = -1; |
| | 4024 | const signed_expect: i32 = @bitCast(expect); |
| | 4025 | const result = asm volatile ( |
| | 4026 | \\local.get %[ptr] |
| | 4027 | \\local.get %[expected] |
| | 4028 | \\local.get %[timeout] |
| | 4029 | \\memory.atomic.wait32 0 |
| | 4030 | \\local.set %[ret] |
| | 4031 | : [ret] "=r" (-> u32), |
| | 4032 | : [ptr] "r" (&ptr.raw), |
| | 4033 | [expected] "r" (signed_expect), |
| | 4034 | [timeout] "r" (timeout), |
| | 4035 | ); |
| | 4036 | const is_debug = builtin.mode == .Debug; |
| | 4037 | switch (result) { |
| | 4038 | 0 => {}, // ok |
| | 4039 | 1 => {}, // expected != loaded |
| | 4040 | 2 => assert(!is_debug), // timeout |
| | 4041 | else => assert(!is_debug), |
| | 4042 | } |
| | 4043 | return; |
| | 4044 | } |
| | 4045 | |
| 4010 | @compileError("TODO"); | 4046 | @compileError("TODO"); |
| 4011 | } | 4047 | } |
| 4012 | | 4048 | |
| ... | @@ -4054,6 +4090,31 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi | ... | @@ -4054,6 +4090,31 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi |
| 4054 | return; | 4090 | return; |
| 4055 | } | 4091 | } |
| 4056 | | 4092 | |
| | 4093 | if (builtin.cpu.arch.isWasm()) { |
| | 4094 | comptime assert(builtin.cpu.has(.wasm, .atomics)); |
| | 4095 | const timeout: i64 = -1; |
| | 4096 | const signed_expect: i32 = @bitCast(expect); |
| | 4097 | const result = asm volatile ( |
| | 4098 | \\local.get %[ptr] |
| | 4099 | \\local.get %[expected] |
| | 4100 | \\local.get %[timeout] |
| | 4101 | \\memory.atomic.wait32 0 |
| | 4102 | \\local.set %[ret] |
| | 4103 | : [ret] "=r" (-> u32), |
| | 4104 | : [ptr] "r" (&ptr.raw), |
| | 4105 | [expected] "r" (signed_expect), |
| | 4106 | [timeout] "r" (timeout), |
| | 4107 | ); |
| | 4108 | const is_debug = builtin.mode == .Debug; |
| | 4109 | switch (result) { |
| | 4110 | 0 => {}, // ok |
| | 4111 | 1 => {}, // expected != loaded |
| | 4112 | 2 => assert(!is_debug), // timeout |
| | 4113 | else => assert(!is_debug), |
| | 4114 | } |
| | 4115 | return; |
| | 4116 | } |
| | 4117 | |
| 4057 | @compileError("TODO"); | 4118 | @compileError("TODO"); |
| 4058 | } | 4119 | } |
| 4059 | | 4120 | |
| ... | @@ -4119,6 +4180,22 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { | ... | @@ -4119,6 +4180,22 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { |
| 4119 | } | 4180 | } |
| 4120 | } | 4181 | } |
| 4121 | | 4182 | |
| | 4183 | if (builtin.cpu.arch.isWasm()) { |
| | 4184 | comptime assert(builtin.cpu.has(.wasm, .atomics)); |
| | 4185 | assert(max_waiters != 0); |
| | 4186 | const woken_count = asm volatile ( |
| | 4187 | \\local.get %[ptr] |
| | 4188 | \\local.get %[waiters] |
| | 4189 | \\memory.atomic.notify 0 |
| | 4190 | \\local.set %[ret] |
| | 4191 | : [ret] "=r" (-> u32), |
| | 4192 | : [ptr] "r" (&ptr.raw), |
| | 4193 | [waiters] "r" (max_waiters), |
| | 4194 | ); |
| | 4195 | _ = woken_count; // can be 0 when linker flag 'shared-memory' is not enabled |
| | 4196 | return; |
| | 4197 | } |
| | 4198 | |
| 4122 | @compileError("TODO"); | 4199 | @compileError("TODO"); |
| 4123 | } | 4200 | } |
| 4124 | | 4201 | |