| ... | ... | @@ -1778,9 +1778,10 @@ pub const Condition = struct { |
| 1778 | 1778 | |
| 1779 | 1779 | epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod |
| 1780 | 1780 | |
| 1781 | | // Even on error, try to consume a pending signal first. Otherwise a race might |
| 1782 | | // cause a signal to get stuck in the state with no corresponding waiter. |
| 1783 | | { |
| 1781 | // We were woken normally, so try to consume a pending signal. A signal takes |
| 1782 | // priority over an expired deadline, so this is checked before the deadline |
| 1783 | // below. On error we safely remove ourselves as a waiter and propagate the error. |
| 1784 | if (result) |_| { |
| 1784 | 1785 | var prev_state = cond.state.load(.monotonic); |
| 1785 | 1786 | while (prev_state.signals > 0) { |
| 1786 | 1787 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| ... | ... | @@ -1791,22 +1792,19 @@ pub const Condition = struct { |
| 1791 | 1792 | return; |
| 1792 | 1793 | }; |
| 1793 | 1794 | } |
| 1795 | } else |err| { |
| 1796 | cond.deregister(io); |
| 1797 | return err; |
| 1794 | 1798 | } |
| 1795 | 1799 | |
| 1796 | | // There are no more signals available; this was a spurious wakeup or an error. If it |
| 1797 | | // was an error, we will remove ourselves as a waiter and return that error. If a |
| 1798 | | // timeout was specified and the deadline has passed, we remove ourselves as a waiter |
| 1799 | | // and return `error.Timeout`. Otherwise, we'll loop back to the futex wait. |
| 1800 | | result catch |err| { |
| 1801 | | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| 1802 | | assert(prev_state.waiters > 0); // underflow caused by illegal state |
| 1803 | | return err; |
| 1804 | | }; |
| 1800 | // There are no signals available and no error; if a timeout was specified and |
| 1801 | // the deadline has passed, remove ourselves as a waiter and return |
| 1802 | // `error.Timeout`. Otherwise, this was a spurious wakeup: loop back to the |
| 1803 | // futex wait. |
| 1805 | 1804 | switch (deadline) { |
| 1806 | 1805 | .none => {}, |
| 1807 | 1806 | .deadline => |d| if (d.untilNow(io).raw.nanoseconds >= 0) { |
| 1808 | | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| 1809 | | assert(prev_state.waiters > 0); // underflow caused by illegal state |
| 1807 | cond.deregister(io); |
| 1810 | 1808 | return error.Timeout; |
| 1811 | 1809 | }, |
| 1812 | 1810 | .duration => unreachable, |
| ... | ... | @@ -1853,6 +1851,24 @@ pub const Condition = struct { |
| 1853 | 1851 | } |
| 1854 | 1852 | } |
| 1855 | 1853 | |
| 1854 | fn deregister(cond: *Condition, io: Io) void { |
| 1855 | var prev_state = cond.state.load(.monotonic); |
| 1856 | while (true) { |
| 1857 | const new_signals = @min(prev_state.signals, prev_state.waiters - 1); |
| 1858 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| 1859 | .waiters = prev_state.waiters - 1, |
| 1860 | .signals = new_signals, |
| 1861 | }, .monotonic, .monotonic) orelse { |
| 1862 | if (prev_state.signals > 0 and prev_state.signals < prev_state.waiters) { |
| 1863 | // We kept a signal we are not consuming; wake a remaining waiter for it. |
| 1864 | _ = cond.epoch.fetchAdd(1, .release); |
| 1865 | io.futexWake(u32, &cond.epoch.raw, 1); |
| 1866 | } |
| 1867 | return; |
| 1868 | }; |
| 1869 | } |
| 1870 | } |
| 1871 | |
| 1856 | 1872 | pub fn signal(cond: *Condition, io: Io) void { |
| 1857 | 1873 | var prev_state = cond.state.load(.monotonic); |
| 1858 | 1874 | while (prev_state.waiters > prev_state.signals) { |