| ... | @@ -1681,20 +1681,29 @@ pub const Condition = struct { | ... | @@ -1681,20 +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 | try waitInner(cond, io, mutex, false); | 1690 | waitTimeout(cond, io, mutex, .none) catch |err| switch (err) { |
| | 1691 | error.Timeout => unreachable, |
| | 1692 | error.Canceled => |e| return e, |
| | 1693 | }; |
| 1686 | } | 1694 | } |
| 1687 | | 1695 | |
| 1688 | /// Same as `wait`, except does not introduce a cancelation point. | 1696 | pub const WaitTimeoutError = Cancelable || Timeout.Error; |
| | 1697 | |
| | 1698 | /// Blocks until the condition is signaled, canceled, or the provided |
| | 1699 | /// timeout expires. |
| 1689 | /// | 1700 | /// |
| 1690 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 1701 | /// See also: |
| 1691 | pub fn waitUncancelable(cond: *Condition, io: Io, mutex: *Mutex) void { | 1702 | /// * `wait` |
| 1692 | waitInner(cond, io, mutex, true) catch |err| switch (err) { | 1703 | /// * `waitUncancelable` |
| 1693 | error.Canceled => unreachable, | 1704 | pub fn waitTimeout(cond: *Condition, io: Io, mutex: *Mutex, timeout: Timeout) WaitTimeoutError!void { |
| 1694 | }; | 1705 | const deadline = timeout.toDeadline(io); |
| 1695 | } | | |
| 1696 | | 1706 | |
| 1697 | fn waitInner(cond: *Condition, io: Io, mutex: *Mutex, uncancelable: bool) Cancelable!void { | | |
| 1698 | 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 |
| 1699 | | 1708 | |
| 1700 | { | 1709 | { |
| ... | @@ -1706,10 +1715,7 @@ pub const Condition = struct { | ... | @@ -1706,10 +1715,7 @@ pub const Condition = struct { |
| 1706 | defer mutex.lockUncancelable(io); | 1715 | defer mutex.lockUncancelable(io); |
| 1707 | | 1716 | |
| 1708 | while (true) { | 1717 | while (true) { |
| 1709 | const result = if (uncancelable) | 1718 | const result = io.futexWaitTimeout(u32, &cond.epoch.raw, epoch, deadline); |
| 1710 | io.futexWaitUncancelable(u32, &cond.epoch.raw, epoch) | | |
| 1711 | else | | |
| 1712 | io.futexWait(u32, &cond.epoch.raw, epoch); | | |
| 1713 | | 1719 | |
| 1714 | 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 |
| 1715 | | 1721 | |
| ... | @@ -1729,13 +1735,62 @@ pub const Condition = struct { | ... | @@ -1729,13 +1735,62 @@ pub const Condition = struct { |
| 1729 | } | 1735 | } |
| 1730 | | 1736 | |
| 1731 | // There are no more signals available; this was a spurious wakeup or an error. If it | 1737 | // There are no more signals available; this was a spurious wakeup or an error. If it |
| 1732 | // was an error, we will remove ourselves as a waiter and return that error. Otherwise, | 1738 | // was an error, we will remove ourselves as a waiter and return that error. If a |
| 1733 | // we'll loop back to the futex wait. | 1739 | // timeout was specified and the deadline has passed, we remove ourselves as a waiter |
| | 1740 | // and return `error.Timeout`. Otherwise, we'll loop back to the futex wait. |
| 1734 | result catch |err| { | 1741 | result catch |err| { |
| 1735 | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); | 1742 | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| 1736 | assert(prev_state.waiters > 0); // underflow caused by illegal state | 1743 | assert(prev_state.waiters > 0); // underflow caused by illegal state |
| 1737 | return err; | 1744 | return err; |
| 1738 | }; | 1745 | }; |
| | 1746 | switch (deadline) { |
| | 1747 | .none => {}, |
| | 1748 | .deadline => |d| if (d.untilNow(io).raw.nanoseconds >= 0) { |
| | 1749 | const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| | 1750 | assert(prev_state.waiters > 0); // underflow caused by illegal state |
| | 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 | }; |
| | 1789 | } |
| | 1790 | } |
| | 1791 | |
| | 1792 | // There are no more signals available; this was a spurious wakeup, |
| | 1793 | // so we'll loop back to the futex wait. |
| 1739 | } | 1794 | } |
| 1740 | } | 1795 | } |
| 1741 | | 1796 | |