| ... | @@ -1681,28 +1681,29 @@ pub const Condition = struct { | ... | @@ -1681,28 +1681,29 @@ pub const Condition = struct { |
| 1681 | .epoch = .init(0), | 1681 | .epoch = .init(0), |
| 1682 | }; | 1682 | }; |
| 1683 | | 1683 | |
| | 1684 | /// Blocks until the condition is signaled or canceled. |
| | 1685 | /// |
| | 1686 | /// See also: |
| | 1687 | /// * `waitUncancelable` |
| | 1688 | /// * `waitTimeout` |
| 1684 | pub fn wait(cond: *Condition, io: Io, mutex: *Mutex) Cancelable!void { | 1689 | pub fn wait(cond: *Condition, io: Io, mutex: *Mutex) Cancelable!void { |
| 1685 | waitInner(cond, io, mutex, .{ .timeout = .none }) catch |err| switch (err) { | 1690 | waitTimeout(cond, io, mutex, .none) catch |err| switch (err) { |
| 1686 | error.Timeout => unreachable, | 1691 | error.Timeout => unreachable, |
| 1687 | error.Canceled => return error.Canceled, | 1692 | error.Canceled => |e| return e, |
| 1688 | }; | 1693 | }; |
| 1689 | } | 1694 | } |
| 1690 | | 1695 | |
| 1691 | pub fn waitTimeout(cond: *Condition, io: Io, mutex: *Mutex, timeout: Timeout) (Cancelable || Timeout.Error)!void { | 1696 | pub const WaitTimeoutError = Cancelable || Timeout.Error; |
| 1692 | return waitInner(cond, io, mutex, .{ .timeout = timeout.toDeadline(io) }); | | |
| 1693 | } | | |
| 1694 | | 1697 | |
| 1695 | /// Same as `wait`, except does not introduce a cancelation point. | 1698 | /// Blocks until the condition is signaled, canceled, or the provided |
| | 1699 | /// timeout expires. |
| 1696 | /// | 1700 | /// |
| 1697 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 1701 | /// See also: |
| 1698 | pub fn waitUncancelable(cond: *Condition, io: Io, mutex: *Mutex) void { | 1702 | /// * `wait` |
| 1699 | waitInner(cond, io, mutex, .uncancelable) catch |err| switch (err) { | 1703 | /// * `waitUncancelable` |
| 1700 | error.Timeout => unreachable, | 1704 | pub fn waitTimeout(cond: *Condition, io: Io, mutex: *Mutex, timeout: Timeout) WaitTimeoutError!void { |
| 1701 | error.Canceled => unreachable, | 1705 | const deadline = timeout.toDeadline(io); |
| 1702 | }; | | |
| 1703 | } | | |
| 1704 | | 1706 | |
| 1705 | fn waitInner(cond: *Condition, io: Io, mutex: *Mutex, mode: union(enum) { uncancelable, timeout: Timeout }) (Cancelable || Timeout.Error)!void { | | |
| 1706 | var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load | 1707 | var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load |
| 1707 | | 1708 | |
| 1708 | { | 1709 | { |
| ... | @@ -1714,10 +1715,7 @@ pub const Condition = struct { | ... | @@ -1714,10 +1715,7 @@ pub const Condition = struct { |
| 1714 | defer mutex.lockUncancelable(io); | 1715 | defer mutex.lockUncancelable(io); |
| 1715 | | 1716 | |
| 1716 | while (true) { | 1717 | while (true) { |
| 1717 | const result = switch (mode) { | 1718 | const result = io.futexWaitTimeout(u32, &cond.epoch.raw, epoch, deadline); |
| 1718 | .uncancelable => io.futexWaitUncancelable(u32, &cond.epoch.raw, epoch), | | |
| 1719 | .timeout => |t| io.futexWaitTimeout(u32, &cond.epoch.raw, epoch, t), | | |
| 1720 | }; | | |
| 1721 | | 1719 | |
| 1722 | epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod | 1720 | epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod |
| 1723 | | 1721 | |
| ... | @@ -1745,13 +1743,54 @@ pub const Condition = struct { | ... | @@ -1745,13 +1743,54 @@ pub const Condition = struct { |
| 1745 | assert(prev_state.waiters > 0); // underflow caused by illegal state | 1743 | assert(prev_state.waiters > 0); // underflow caused by illegal state |
| 1746 | return err; | 1744 | return err; |
| 1747 | }; | 1745 | }; |
| 1748 | if (mode == .timeout and mode.timeout != .none) { | 1746 | switch (deadline) { |
| 1749 | if (mode.timeout.deadline.untilNow(io).raw.nanoseconds >= 0) { | 1747 | .none => {}, |
| | 1748 | .deadline => |d| if (d.untilNow(io).raw.nanoseconds >= 0) { |
| 1750 | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); | 1749 | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| 1751 | assert(prev_state.waiters > 0); // underflow caused by illegal state | 1750 | assert(prev_state.waiters > 0); // underflow caused by illegal state |
| 1752 | return error.Timeout; | 1751 | return error.Timeout; |
| | 1752 | }, |
| | 1753 | .duration => unreachable, |
| | 1754 | } |
| | 1755 | } |
| | 1756 | } |
| | 1757 | |
| | 1758 | /// Same as `wait`, except does not introduce a cancelation point. |
| | 1759 | /// |
| | 1760 | /// See `Future.cancel` for a description of cancelation points. |
| | 1761 | pub fn waitUncancelable(cond: *Condition, io: Io, mutex: *Mutex) void { |
| | 1762 | var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load |
| | 1763 | |
| | 1764 | { |
| | 1765 | const prev_state = cond.state.fetchAdd(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| | 1766 | assert(prev_state.waiters < math.maxInt(u16)); // overflow caused by too many waiters |
| | 1767 | } |
| | 1768 | |
| | 1769 | mutex.unlock(io); |
| | 1770 | defer mutex.lockUncancelable(io); |
| | 1771 | |
| | 1772 | while (true) { |
| | 1773 | io.futexWaitUncancelable(u32, &cond.epoch.raw, epoch); |
| | 1774 | |
| | 1775 | epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod |
| | 1776 | |
| | 1777 | // Even on error, try to consume a pending signal first. Otherwise a race might |
| | 1778 | // cause a signal to get stuck in the state with no corresponding waiter. |
| | 1779 | { |
| | 1780 | var prev_state = cond.state.load(.monotonic); |
| | 1781 | while (prev_state.signals > 0) { |
| | 1782 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| | 1783 | .waiters = prev_state.waiters - 1, |
| | 1784 | .signals = prev_state.signals - 1, |
| | 1785 | }, .acquire, .monotonic) orelse { |
| | 1786 | // We successfully consumed a signal. |
| | 1787 | return; |
| | 1788 | }; |
| 1753 | } | 1789 | } |
| 1754 | } | 1790 | } |
| | 1791 | |
| | 1792 | // There are no more signals available; this was a spurious wakeup, |
| | 1793 | // so we'll loop back to the futex wait. |
| 1755 | } | 1794 | } |
| 1756 | } | 1795 | } |
| 1757 | | 1796 | |