@@ -265,6 +265,10 @@ const have_flock_open_flags = @hasField(posix.O, "EXLOCK");
265265const have_networking = builtin .os .tag != .wasi ;
266266const have_flock = @TypeOf (posix .system .flock ) != void ;
267267const 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+ };
268272
269273const openat_sym = if (posix .lfs64_abi ) posix .system .openat64 else posix .system .openat ;
270274const fstat_sym = if (posix .lfs64_abi ) posix .system .fstat64 else posix .system .fstat ;
@@ -731,6 +735,7 @@ fn checkCancel(t: *Threaded) error{Canceled}!void {
731735}
732736
733737fn 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.
734739 const t : * Threaded = @ptrCast (@alignCast (userdata ));
735740 if (prev_state == .contended ) {
736741 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
741746}
742747
743748fn mutexLockUncancelable (userdata : ? * anyopaque , prev_state : Io.Mutex.State , mutex : * Io.Mutex ) void {
749+ if (builtin .single_threaded ) unreachable ; // Interface should have prevented this.
744750 _ = userdata ;
745751 if (prev_state == .contended ) {
746752 futexWaitUncancelable (@ptrCast (& mutex .state ), @intFromEnum (Io .Mutex .State .contended ));
@@ -751,6 +757,7 @@ fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mute
751757}
752758
753759fn mutexUnlock (userdata : ? * anyopaque , prev_state : Io.Mutex.State , mutex : * Io.Mutex ) void {
760+ if (builtin .single_threaded ) unreachable ; // Interface should have prevented this.
754761 _ = userdata ;
755762 _ = prev_state ;
756763 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
759766}
760767
761768fn conditionWaitUncancelable (userdata : ? * anyopaque , cond : * Io.Condition , mutex : * Io.Mutex ) void {
769+ if (builtin .single_threaded ) unreachable ; // Deadlock.
762770 const t : * Threaded = @ptrCast (@alignCast (userdata ));
763771 const t_io = t .io ();
764772 comptime assert (@TypeOf (cond .state ) == u64 );
@@ -789,6 +797,7 @@ fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex:
789797}
790798
791799fn conditionWait (userdata : ? * anyopaque , cond : * Io.Condition , mutex : * Io.Mutex ) Io.Cancelable ! void {
800+ if (builtin .single_threaded ) unreachable ; // Deadlock.
792801 const t : * Threaded = @ptrCast (@alignCast (userdata ));
793802 comptime assert (@TypeOf (cond .state ) == u64 );
794803 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
833842}
834843
835844fn conditionWake (userdata : ? * anyopaque , cond : * Io.Condition , wake : Io.Condition.Wake ) void {
845+ if (builtin .single_threaded ) unreachable ; // Nothing to wake up.
836846 const t : * Threaded = @ptrCast (@alignCast (userdata ));
837847 _ = t ;
838848 comptime assert (@TypeOf (cond .state ) == u64 );
@@ -4007,6 +4017,32 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca
40074017 return ;
40084018 }
40094019
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+
40104046 @compileError ("TODO" );
40114047}
40124048
@@ -4054,6 +4090,31 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi
40544090 return ;
40554091 }
40564092
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+
40574118 @compileError ("TODO" );
40584119}
40594120
@@ -4119,6 +4180,22 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void {
41194180 }
41204181 }
41214182
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+
41224199 @compileError ("TODO" );
41234200}
41244201
0 commit comments