| ... | ... | @@ -265,6 +265,10 @@ const have_flock_open_flags = @hasField(posix.O, "EXLOCK"); |
| 265 | 265 | const have_networking = builtin.os.tag != .wasi; |
| 266 | 266 | const have_flock = @TypeOf(posix.system.flock) != void; |
| 267 | 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 | 273 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| 270 | 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 | 735 | } |
| 732 | 736 | |
| 733 | 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 | 739 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 735 | 740 | if (prev_state == .contended) { |
| 736 | 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 | 746 | } |
| 742 | 747 | |
| 743 | 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 | 750 | _ = userdata; |
| 745 | 751 | if (prev_state == .contended) { |
| 746 | 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 | 757 | } |
| 752 | 758 | |
| 753 | 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 | 761 | _ = userdata; |
| 755 | 762 | _ = prev_state; |
| 756 | 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 | 766 | } |
| 760 | 767 | |
| 761 | 768 | fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) void { |
| 769 | if (builtin.single_threaded) unreachable; // Deadlock. |
| 762 | 770 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 763 | 771 | const t_io = t.io(); |
| 764 | 772 | comptime assert(@TypeOf(cond.state) == u64); |
| ... | ... | @@ -789,6 +797,7 @@ fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: |
| 789 | 797 | } |
| 790 | 798 | |
| 791 | 799 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { |
| 800 | if (builtin.single_threaded) unreachable; // Deadlock. |
| 792 | 801 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 793 | 802 | comptime assert(@TypeOf(cond.state) == u64); |
| 794 | 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 | 842 | } |
| 834 | 843 | |
| 835 | 844 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { |
| 845 | if (builtin.single_threaded) unreachable; // Nothing to wake up. |
| 836 | 846 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 837 | 847 | _ = t; |
| 838 | 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 | 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 | 4046 | @compileError("TODO"); |
| 4011 | 4047 | } |
| 4012 | 4048 | |
| ... | ... | @@ -4054,6 +4090,31 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi |
| 4054 | 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 | 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 | 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 | 4199 | @compileError("TODO"); |
| 4123 | 4200 | } |
| 4124 | 4201 | |