| ... | @@ -5787,7 +5787,13 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { | ... | @@ -5787,7 +5787,13 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { |
| 5787 | /// | 5787 | /// |
| 5788 | /// It can also block threads until the value is set with cancelation via timed | 5788 | /// It can also block threads until the value is set with cancelation via timed |
| 5789 | /// waits. Statically initializable; four bytes on all targets. | 5789 | /// waits. Statically initializable; four bytes on all targets. |
| 5790 | pub const ResetEvent = enum(u32) { | 5790 | pub const ResetEvent = switch (native_os) { |
| | 5791 | .netbsd => ResetEventPosix, |
| | 5792 | else => ResetEventFutex, |
| | 5793 | }; |
| | 5794 | |
| | 5795 | /// A `ResetEvent` implementation based on futexes. |
| | 5796 | const ResetEventFutex = enum(u32) { |
| 5791 | unset = 0, | 5797 | unset = 0, |
| 5792 | waiting = 1, | 5798 | waiting = 1, |
| 5793 | is_set = 2, | 5799 | is_set = 2, |
| ... | @@ -5798,15 +5804,15 @@ pub const ResetEvent = enum(u32) { | ... | @@ -5798,15 +5804,15 @@ pub const ResetEvent = enum(u32) { |
| 5798 | /// | 5804 | /// |
| 5799 | /// The memory accesses before the `set` can be said to happen before | 5805 | /// The memory accesses before the `set` can be said to happen before |
| 5800 | /// `isSet` returns true. | 5806 | /// `isSet` returns true. |
| 5801 | pub fn isSet(re: *const ResetEvent) bool { | 5807 | pub fn isSet(ref: *const ResetEventFutex) bool { |
| 5802 | if (builtin.single_threaded) return switch (re.*) { | 5808 | if (builtin.single_threaded) return switch (ref.*) { |
| 5803 | .unset => false, | 5809 | .unset => false, |
| 5804 | .waiting => unreachable, | 5810 | .waiting => unreachable, |
| 5805 | .is_set => true, | 5811 | .is_set => true, |
| 5806 | }; | 5812 | }; |
| 5807 | // Acquire barrier ensures memory accesses before `set` happen before | 5813 | // Acquire barrier ensures memory accesses before `set` happen before |
| 5808 | // returning true. | 5814 | // returning true. |
| 5809 | return @atomicLoad(ResetEvent, re, .acquire) == .is_set; | 5815 | return @atomicLoad(ResetEventFutex, ref, .acquire) == .is_set; |
| 5810 | } | 5816 | } |
| 5811 | | 5817 | |
| 5812 | /// Blocks the calling thread until `set` is called. | 5818 | /// Blocks the calling thread until `set` is called. |
| ... | @@ -5814,51 +5820,51 @@ pub const ResetEvent = enum(u32) { | ... | @@ -5814,51 +5820,51 @@ pub const ResetEvent = enum(u32) { |
| 5814 | /// This is effectively a more efficient version of `while (!isSet()) {}`. | 5820 | /// This is effectively a more efficient version of `while (!isSet()) {}`. |
| 5815 | /// | 5821 | /// |
| 5816 | /// The memory accesses before the `set` can be said to happen before `wait` returns. | 5822 | /// The memory accesses before the `set` can be said to happen before `wait` returns. |
| 5817 | pub fn wait(re: *ResetEvent, t: *Threaded) Io.Cancelable!void { | 5823 | pub fn wait(ref: *ResetEventFutex, t: *Threaded) Io.Cancelable!void { |
| 5818 | if (builtin.single_threaded) switch (re.*) { | 5824 | if (builtin.single_threaded) switch (ref.*) { |
| 5819 | .unset => unreachable, // Deadlock, no other threads to wake us up. | 5825 | .unset => unreachable, // Deadlock, no other threads to wake us up. |
| 5820 | .waiting => unreachable, // Invalid state. | 5826 | .waiting => unreachable, // Invalid state. |
| 5821 | .is_set => return, | 5827 | .is_set => return, |
| 5822 | }; | 5828 | }; |
| 5823 | if (re.isSet()) { | 5829 | // Try to set the state from `unset` to `waiting` to indicate to the |
| | 5830 | // `set` thread that others are blocked on the ResetEventFutex. Avoid using |
| | 5831 | // any strict barriers until we know the ResetEventFutex is set. |
| | 5832 | var state = @atomicLoad(ResetEventFutex, ref, .acquire); |
| | 5833 | if (state == .is_set) { |
| 5824 | @branchHint(.likely); | 5834 | @branchHint(.likely); |
| 5825 | return; | 5835 | return; |
| 5826 | } | 5836 | } |
| 5827 | // Try to set the state from `unset` to `waiting` to indicate to the | | |
| 5828 | // `set` thread that others are blocked on the ResetEvent. Avoid using | | |
| 5829 | // any strict barriers until we know the ResetEvent is set. | | |
| 5830 | var state = @atomicLoad(ResetEvent, re, .acquire); | | |
| 5831 | if (state == .unset) { | 5837 | if (state == .unset) { |
| 5832 | state = @cmpxchgStrong(ResetEvent, re, state, .waiting, .acquire, .acquire) orelse .waiting; | 5838 | state = @cmpxchgStrong(ResetEventFutex, ref, state, .waiting, .acquire, .acquire) orelse .waiting; |
| 5833 | } | 5839 | } |
| 5834 | while (state == .waiting) { | 5840 | while (state == .waiting) { |
| 5835 | try futexWait(t, @ptrCast(re), @intFromEnum(ResetEvent.waiting)); | 5841 | try futexWait(t, @ptrCast(ref), @intFromEnum(ResetEventFutex.waiting)); |
| 5836 | state = @atomicLoad(ResetEvent, re, .acquire); | 5842 | state = @atomicLoad(ResetEventFutex, ref, .acquire); |
| 5837 | } | 5843 | } |
| 5838 | assert(state == .is_set); | 5844 | assert(state == .is_set); |
| 5839 | } | 5845 | } |
| 5840 | | 5846 | |
| 5841 | /// Same as `wait` except uninterruptible. | 5847 | /// Same as `wait` except uninterruptible. |
| 5842 | pub fn waitUncancelable(re: *ResetEvent) void { | 5848 | pub fn waitUncancelable(ref: *ResetEventFutex) void { |
| 5843 | if (builtin.single_threaded) switch (re.*) { | 5849 | if (builtin.single_threaded) switch (ref.*) { |
| 5844 | .unset => unreachable, // Deadlock, no other threads to wake us up. | 5850 | .unset => unreachable, // Deadlock, no other threads to wake us up. |
| 5845 | .waiting => unreachable, // Invalid state. | 5851 | .waiting => unreachable, // Invalid state. |
| 5846 | .is_set => return, | 5852 | .is_set => return, |
| 5847 | }; | 5853 | }; |
| 5848 | if (re.isSet()) { | 5854 | // Try to set the state from `unset` to `waiting` to indicate to the |
| | 5855 | // `set` thread that others are blocked on the ResetEventFutex. Avoid using |
| | 5856 | // any strict barriers until we know the ResetEventFutex is set. |
| | 5857 | var state = @atomicLoad(ResetEventFutex, ref, .acquire); |
| | 5858 | if (state == .is_set) { |
| 5849 | @branchHint(.likely); | 5859 | @branchHint(.likely); |
| 5850 | return; | 5860 | return; |
| 5851 | } | 5861 | } |
| 5852 | // Try to set the state from `unset` to `waiting` to indicate to the | | |
| 5853 | // `set` thread that others are blocked on the ResetEvent. Avoid using | | |
| 5854 | // any strict barriers until we know the ResetEvent is set. | | |
| 5855 | var state = @atomicLoad(ResetEvent, re, .acquire); | | |
| 5856 | if (state == .unset) { | 5862 | if (state == .unset) { |
| 5857 | state = @cmpxchgStrong(ResetEvent, re, state, .waiting, .acquire, .acquire) orelse .waiting; | 5863 | state = @cmpxchgStrong(ResetEventFutex, ref, state, .waiting, .acquire, .acquire) orelse .waiting; |
| 5858 | } | 5864 | } |
| 5859 | while (state == .waiting) { | 5865 | while (state == .waiting) { |
| 5860 | futexWaitUncancelable(@ptrCast(re), @intFromEnum(ResetEvent.waiting)); | 5866 | futexWaitUncancelable(@ptrCast(ref), @intFromEnum(ResetEventFutex.waiting)); |
| 5861 | state = @atomicLoad(ResetEvent, re, .acquire); | 5867 | state = @atomicLoad(ResetEventFutex, ref, .acquire); |
| 5862 | } | 5868 | } |
| 5863 | assert(state == .is_set); | 5869 | assert(state == .is_set); |
| 5864 | } | 5870 | } |
| ... | @@ -5871,26 +5877,109 @@ pub const ResetEvent = enum(u32) { | ... | @@ -5871,26 +5877,109 @@ pub const ResetEvent = enum(u32) { |
| 5871 | /// | 5877 | /// |
| 5872 | /// The memory accesses before `set` can be said to happen before `isSet` | 5878 | /// The memory accesses before `set` can be said to happen before `isSet` |
| 5873 | /// returns true or `wait`/`timedWait` return successfully. | 5879 | /// returns true or `wait`/`timedWait` return successfully. |
| 5874 | pub fn set(re: *ResetEvent) void { | 5880 | pub fn set(ref: *ResetEventFutex) void { |
| 5875 | if (builtin.single_threaded) { | 5881 | if (builtin.single_threaded) { |
| 5876 | re.* = .is_set; | 5882 | ref.* = .is_set; |
| 5877 | return; | 5883 | return; |
| 5878 | } | 5884 | } |
| 5879 | if (@atomicRmw(ResetEvent, re, .Xchg, .is_set, .release) == .waiting) { | 5885 | if (@atomicRmw(ResetEventFutex, ref, .Xchg, .is_set, .release) == .waiting) { |
| 5880 | futexWake(@ptrCast(re), std.math.maxInt(u32)); | 5886 | futexWake(@ptrCast(ref), std.math.maxInt(u32)); |
| 5881 | } | 5887 | } |
| 5882 | } | 5888 | } |
| 5883 | | 5889 | |
| 5884 | /// Unmarks the ResetEvent as if `set` was never called. | 5890 | /// Unmarks the ResetEventFutex as if `set` was never called. |
| 5885 | /// | 5891 | /// |
| 5886 | /// Assumes no threads are blocked in `wait` or `timedWait`. Concurrent | 5892 | /// Assumes no threads are blocked in `wait` or `timedWait`. Concurrent |
| 5887 | /// calls to `set`, `isSet` and `reset` are allowed. | 5893 | /// calls to `set`, `isSet` and `reset` are allowed. |
| 5888 | pub fn reset(re: *ResetEvent) void { | 5894 | pub fn reset(ref: *ResetEventFutex) void { |
| | 5895 | if (builtin.single_threaded) { |
| | 5896 | ref.* = .unset; |
| | 5897 | return; |
| | 5898 | } |
| | 5899 | @atomicStore(ResetEventFutex, ref, .unset, .monotonic); |
| | 5900 | } |
| | 5901 | }; |
| | 5902 | |
| | 5903 | /// A `ResetEvent` implementation based on pthreads API. |
| | 5904 | const ResetEventPosix = struct { |
| | 5905 | cond: std.c.pthread_cond_t, |
| | 5906 | mutex: std.c.pthread_mutex_t, |
| | 5907 | state: ResetEventFutex, |
| | 5908 | |
| | 5909 | pub const unset: ResetEventPosix = .{ |
| | 5910 | .cond = std.c.PTHREAD_COND_INITIALIZER, |
| | 5911 | .mutex = std.c.PTHREAD_MUTEX_INITIALIZER, |
| | 5912 | .state = .unset, |
| | 5913 | }; |
| | 5914 | |
| | 5915 | pub fn isSet(rep: *const ResetEventPosix) bool { |
| | 5916 | if (builtin.single_threaded) return switch (rep.state) { |
| | 5917 | .unset => false, |
| | 5918 | .waiting => unreachable, |
| | 5919 | .is_set => true, |
| | 5920 | }; |
| | 5921 | return @atomicLoad(ResetEventFutex, &rep.state, .acquire) == .is_set; |
| | 5922 | } |
| | 5923 | |
| | 5924 | pub fn wait(rep: *ResetEventPosix, t: *Threaded) Io.Cancelable!void { |
| | 5925 | if (builtin.single_threaded) switch (rep.*) { |
| | 5926 | .unset => unreachable, // Deadlock, no other threads to wake us up. |
| | 5927 | .waiting => unreachable, // Invalid state. |
| | 5928 | .is_set => return, |
| | 5929 | }; |
| | 5930 | assert(std.c.pthread_mutex_lock(&rep.mutex) == .SUCCESS); |
| | 5931 | defer assert(std.c.pthread_mutex_unlock(&rep.mutex) == .SUCCESS); |
| | 5932 | sw: switch (rep.state) { |
| | 5933 | .unset => { |
| | 5934 | rep.state = .waiting; |
| | 5935 | continue :sw .waiting; |
| | 5936 | }, |
| | 5937 | .waiting => { |
| | 5938 | try t.checkCancel(); |
| | 5939 | assert(std.c.pthread_cond_wait(&rep.cond, &rep.mutex) == .SUCCESS); |
| | 5940 | continue :sw rep.state; |
| | 5941 | }, |
| | 5942 | .is_set => return, |
| | 5943 | } |
| | 5944 | } |
| | 5945 | |
| | 5946 | pub fn waitUncancelable(rep: *ResetEventPosix) void { |
| | 5947 | if (builtin.single_threaded) switch (rep.*) { |
| | 5948 | .unset => unreachable, // Deadlock, no other threads to wake us up. |
| | 5949 | .waiting => unreachable, // Invalid state. |
| | 5950 | .is_set => return, |
| | 5951 | }; |
| | 5952 | assert(std.c.pthread_mutex_lock(&rep.mutex) == .SUCCESS); |
| | 5953 | defer assert(std.c.pthread_mutex_unlock(&rep.mutex) == .SUCCESS); |
| | 5954 | sw: switch (rep.state) { |
| | 5955 | .unset => { |
| | 5956 | rep.state = .waiting; |
| | 5957 | continue :sw .waiting; |
| | 5958 | }, |
| | 5959 | .waiting => { |
| | 5960 | assert(std.c.pthread_cond_wait(&rep.cond, &rep.mutex) == .SUCCESS); |
| | 5961 | continue :sw rep.state; |
| | 5962 | }, |
| | 5963 | .is_set => return, |
| | 5964 | } |
| | 5965 | } |
| | 5966 | |
| | 5967 | pub fn set(rep: *ResetEventPosix) void { |
| | 5968 | if (builtin.single_threaded) { |
| | 5969 | rep.* = .is_set; |
| | 5970 | return; |
| | 5971 | } |
| | 5972 | if (@atomicRmw(ResetEventFutex, &rep.state, .Xchg, .is_set, .release) == .waiting) { |
| | 5973 | assert(std.c.pthread_cond_broadcast(&rep.cond) == .SUCCESS); |
| | 5974 | } |
| | 5975 | } |
| | 5976 | |
| | 5977 | pub fn reset(rep: *ResetEventPosix) void { |
| 5889 | if (builtin.single_threaded) { | 5978 | if (builtin.single_threaded) { |
| 5890 | re.* = .unset; | 5979 | rep.* = .unset; |
| 5891 | return; | 5980 | return; |
| 5892 | } | 5981 | } |
| 5893 | @atomicStore(ResetEvent, re, .unset, .monotonic); | 5982 | @atomicStore(ResetEventFutex, &rep.state, .unset, .monotonic); |
| 5894 | } | 5983 | } |
| 5895 | }; | 5984 | }; |
| 5896 | | 5985 | |