| ... | ... | @@ -5787,7 +5787,13 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { |
| 5787 | 5787 | /// |
| 5788 | 5788 | /// It can also block threads until the value is set with cancelation via timed |
| 5789 | 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 | 5797 | unset = 0, |
| 5792 | 5798 | waiting = 1, |
| 5793 | 5799 | is_set = 2, |
| ... | ... | @@ -5798,15 +5804,15 @@ pub const ResetEvent = enum(u32) { |
| 5798 | 5804 | /// |
| 5799 | 5805 | /// The memory accesses before the `set` can be said to happen before |
| 5800 | 5806 | /// `isSet` returns true. |
| 5801 | | pub fn isSet(re: *const ResetEvent) bool { |
| 5802 | | if (builtin.single_threaded) return switch (re.*) { |
| 5807 | pub fn isSet(ref: *const ResetEventFutex) bool { |
| 5808 | if (builtin.single_threaded) return switch (ref.*) { |
| 5803 | 5809 | .unset => false, |
| 5804 | 5810 | .waiting => unreachable, |
| 5805 | 5811 | .is_set => true, |
| 5806 | 5812 | }; |
| 5807 | 5813 | // Acquire barrier ensures memory accesses before `set` happen before |
| 5808 | 5814 | // returning true. |
| 5809 | | return @atomicLoad(ResetEvent, re, .acquire) == .is_set; |
| 5815 | return @atomicLoad(ResetEventFutex, ref, .acquire) == .is_set; |
| 5810 | 5816 | } |
| 5811 | 5817 | |
| 5812 | 5818 | /// Blocks the calling thread until `set` is called. |
| ... | ... | @@ -5814,51 +5820,51 @@ pub const ResetEvent = enum(u32) { |
| 5814 | 5820 | /// This is effectively a more efficient version of `while (!isSet()) {}`. |
| 5815 | 5821 | /// |
| 5816 | 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 { |
| 5818 | | if (builtin.single_threaded) switch (re.*) { |
| 5823 | pub fn wait(ref: *ResetEventFutex, t: *Threaded) Io.Cancelable!void { |
| 5824 | if (builtin.single_threaded) switch (ref.*) { |
| 5819 | 5825 | .unset => unreachable, // Deadlock, no other threads to wake us up. |
| 5820 | 5826 | .waiting => unreachable, // Invalid state. |
| 5821 | 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 | 5834 | @branchHint(.likely); |
| 5825 | 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 | 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 | 5840 | while (state == .waiting) { |
| 5835 | | try futexWait(t, @ptrCast(re), @intFromEnum(ResetEvent.waiting)); |
| 5836 | | state = @atomicLoad(ResetEvent, re, .acquire); |
| 5841 | try futexWait(t, @ptrCast(ref), @intFromEnum(ResetEventFutex.waiting)); |
| 5842 | state = @atomicLoad(ResetEventFutex, ref, .acquire); |
| 5837 | 5843 | } |
| 5838 | 5844 | assert(state == .is_set); |
| 5839 | 5845 | } |
| 5840 | 5846 | |
| 5841 | 5847 | /// Same as `wait` except uninterruptible. |
| 5842 | | pub fn waitUncancelable(re: *ResetEvent) void { |
| 5843 | | if (builtin.single_threaded) switch (re.*) { |
| 5848 | pub fn waitUncancelable(ref: *ResetEventFutex) void { |
| 5849 | if (builtin.single_threaded) switch (ref.*) { |
| 5844 | 5850 | .unset => unreachable, // Deadlock, no other threads to wake us up. |
| 5845 | 5851 | .waiting => unreachable, // Invalid state. |
| 5846 | 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 | 5859 | @branchHint(.likely); |
| 5850 | 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 | 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 | 5865 | while (state == .waiting) { |
| 5860 | | futexWaitUncancelable(@ptrCast(re), @intFromEnum(ResetEvent.waiting)); |
| 5861 | | state = @atomicLoad(ResetEvent, re, .acquire); |
| 5866 | futexWaitUncancelable(@ptrCast(ref), @intFromEnum(ResetEventFutex.waiting)); |
| 5867 | state = @atomicLoad(ResetEventFutex, ref, .acquire); |
| 5862 | 5868 | } |
| 5863 | 5869 | assert(state == .is_set); |
| 5864 | 5870 | } |
| ... | ... | @@ -5871,26 +5877,109 @@ pub const ResetEvent = enum(u32) { |
| 5871 | 5877 | /// |
| 5872 | 5878 | /// The memory accesses before `set` can be said to happen before `isSet` |
| 5873 | 5879 | /// returns true or `wait`/`timedWait` return successfully. |
| 5874 | | pub fn set(re: *ResetEvent) void { |
| 5880 | pub fn set(ref: *ResetEventFutex) void { |
| 5875 | 5881 | if (builtin.single_threaded) { |
| 5876 | | re.* = .is_set; |
| 5882 | ref.* = .is_set; |
| 5877 | 5883 | return; |
| 5878 | 5884 | } |
| 5879 | | if (@atomicRmw(ResetEvent, re, .Xchg, .is_set, .release) == .waiting) { |
| 5880 | | futexWake(@ptrCast(re), std.math.maxInt(u32)); |
| 5885 | if (@atomicRmw(ResetEventFutex, ref, .Xchg, .is_set, .release) == .waiting) { |
| 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 | 5892 | /// Assumes no threads are blocked in `wait` or `timedWait`. Concurrent |
| 5887 | 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 | 5978 | if (builtin.single_threaded) { |
| 5890 | | re.* = .unset; |
| 5979 | rep.* = .unset; |
| 5891 | 5980 | return; |
| 5892 | 5981 | } |
| 5893 | | @atomicStore(ResetEvent, re, .unset, .monotonic); |
| 5982 | @atomicStore(ResetEventFutex, &rep.state, .unset, .monotonic); |
| 5894 | 5983 | } |
| 5895 | 5984 | }; |
| 5896 | 5985 | |