| ... | @@ -1778,9 +1778,10 @@ pub const Condition = struct { | ... | @@ -1778,9 +1778,10 @@ pub const Condition = struct { |
| 1778 | | 1778 | |
| 1779 | epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod | 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 | 1781 | // We were woken normally, so try to consume a pending signal. A signal takes |
| 1782 | // cause a signal to get stuck in the state with no corresponding waiter. | 1782 | // priority over an expired deadline, so this is checked before the deadline |
| 1783 | { | 1783 | // below. On error we safely remove ourselves as a waiter and propagate the error. |
| | 1784 | if (result) |_| { |
| 1784 | var prev_state = cond.state.load(.monotonic); | 1785 | var prev_state = cond.state.load(.monotonic); |
| 1785 | while (prev_state.signals > 0) { | 1786 | while (prev_state.signals > 0) { |
| 1786 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ | 1787 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| ... | @@ -1791,22 +1792,19 @@ pub const Condition = struct { | ... | @@ -1791,22 +1792,19 @@ pub const Condition = struct { |
| 1791 | return; | 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 | 1800 | // There are no signals available and no error; if a timeout was specified and |
| 1797 | // was an error, we will remove ourselves as a waiter and return that error. If a | 1801 | // the deadline has passed, remove ourselves as a waiter and return |
| 1798 | // timeout was specified and the deadline has passed, we remove ourselves as a waiter | 1802 | // `error.Timeout`. Otherwise, this was a spurious wakeup: loop back to the |
| 1799 | // and return `error.Timeout`. Otherwise, we'll loop back to the futex wait. | 1803 | // 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 | }; | | |
| 1805 | switch (deadline) { | 1804 | switch (deadline) { |
| 1806 | .none => {}, | 1805 | .none => {}, |
| 1807 | .deadline => |d| if (d.untilNow(io).raw.nanoseconds >= 0) { | 1806 | .deadline => |d| if (d.untilNow(io).raw.nanoseconds >= 0) { |
| 1808 | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); | 1807 | cond.deregister(io); |
| 1809 | assert(prev_state.waiters > 0); // underflow caused by illegal state | | |
| 1810 | return error.Timeout; | 1808 | return error.Timeout; |
| 1811 | }, | 1809 | }, |
| 1812 | .duration => unreachable, | 1810 | .duration => unreachable, |
| ... | @@ -1853,6 +1851,24 @@ pub const Condition = struct { | ... | @@ -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 | pub fn signal(cond: *Condition, io: Io) void { | 1872 | pub fn signal(cond: *Condition, io: Io) void { |
| 1857 | var prev_state = cond.state.load(.monotonic); | 1873 | var prev_state = cond.state.load(.monotonic); |
| 1858 | while (prev_state.waiters > prev_state.signals) { | 1874 | while (prev_state.waiters > prev_state.signals) { |