| ... | ... | @@ -701,7 +701,7 @@ pub const VTable = struct { |
| 701 | 701 | netClose: *const fn (?*anyopaque, handle: net.Socket.Handle) void, |
| 702 | 702 | netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface, |
| 703 | 703 | netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name, |
| 704 | | netLookup: *const fn (?*anyopaque, net.HostName, *Queue(net.HostName.LookupResult), net.HostName.LookupOptions) void, |
| 704 | netLookup: *const fn (?*anyopaque, net.HostName, *Queue(net.HostName.LookupResult), net.HostName.LookupOptions) net.HostName.LookupError!void, |
| 705 | 705 | }; |
| 706 | 706 | |
| 707 | 707 | pub const Cancelable = error{ |
| ... | ... | @@ -1208,7 +1208,9 @@ pub fn Select(comptime U: type) type { |
| 1208 | 1208 | const args_casted: *const Args = @ptrCast(@alignCast(context)); |
| 1209 | 1209 | const unerased_select: *S = @fieldParentPtr("group", group); |
| 1210 | 1210 | const elem = @unionInit(U, @tagName(field), @call(.auto, function, args_casted.*)); |
| 1211 | | unerased_select.queue.putOneUncancelable(unerased_select.io, elem); |
| 1211 | unerased_select.queue.putOneUncancelable(unerased_select.io, elem) catch |err| switch (err) { |
| 1212 | error.Closed => unreachable, |
| 1213 | }; |
| 1212 | 1214 | } |
| 1213 | 1215 | }; |
| 1214 | 1216 | _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic); |
| ... | ... | @@ -1222,7 +1224,10 @@ pub fn Select(comptime U: type) type { |
| 1222 | 1224 | /// Not threadsafe. |
| 1223 | 1225 | pub fn wait(s: *S) Cancelable!U { |
| 1224 | 1226 | s.outstanding -= 1; |
| 1225 | | return s.queue.getOne(s.io); |
| 1227 | return s.queue.getOne(s.io) catch |err| switch (err) { |
| 1228 | error.Canceled => |e| return e, |
| 1229 | error.Closed => unreachable, |
| 1230 | }; |
| 1226 | 1231 | } |
| 1227 | 1232 | |
| 1228 | 1233 | /// Equivalent to `wait` but requests cancellation on all remaining |
| ... | ... | @@ -1569,8 +1574,11 @@ pub const Event = enum(u32) { |
| 1569 | 1574 | } |
| 1570 | 1575 | }; |
| 1571 | 1576 | |
| 1577 | pub const QueueClosedError = error{Closed}; |
| 1578 | |
| 1572 | 1579 | pub const TypeErasedQueue = struct { |
| 1573 | 1580 | mutex: Mutex, |
| 1581 | closed: bool, |
| 1574 | 1582 | |
| 1575 | 1583 | /// Ring buffer. This data is logically *after* queued getters. |
| 1576 | 1584 | buffer: []u8, |
| ... | ... | @@ -1582,12 +1590,14 @@ pub const TypeErasedQueue = struct { |
| 1582 | 1590 | |
| 1583 | 1591 | const Put = struct { |
| 1584 | 1592 | remaining: []const u8, |
| 1593 | needed: usize, |
| 1585 | 1594 | condition: Condition, |
| 1586 | 1595 | node: std.DoublyLinkedList.Node, |
| 1587 | 1596 | }; |
| 1588 | 1597 | |
| 1589 | 1598 | const Get = struct { |
| 1590 | 1599 | remaining: []u8, |
| 1600 | needed: usize, |
| 1591 | 1601 | condition: Condition, |
| 1592 | 1602 | node: std.DoublyLinkedList.Node, |
| 1593 | 1603 | }; |
| ... | ... | @@ -1595,6 +1605,7 @@ pub const TypeErasedQueue = struct { |
| 1595 | 1605 | pub fn init(buffer: []u8) TypeErasedQueue { |
| 1596 | 1606 | return .{ |
| 1597 | 1607 | .mutex = .init, |
| 1608 | .closed = false, |
| 1598 | 1609 | .buffer = buffer, |
| 1599 | 1610 | .start = 0, |
| 1600 | 1611 | .len = 0, |
| ... | ... | @@ -1603,7 +1614,27 @@ pub const TypeErasedQueue = struct { |
| 1603 | 1614 | }; |
| 1604 | 1615 | } |
| 1605 | 1616 | |
| 1606 | | pub fn put(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize) Cancelable!usize { |
| 1617 | pub fn close(q: *TypeErasedQueue, io: Io) void { |
| 1618 | q.mutex.lockUncancelable(io); |
| 1619 | defer q.mutex.unlock(io); |
| 1620 | q.closed = true; |
| 1621 | { |
| 1622 | var it = q.getters.first; |
| 1623 | while (it) |node| : (it = node.next) { |
| 1624 | const getter: *Get = @alignCast(@fieldParentPtr("node", node)); |
| 1625 | getter.condition.signal(io); |
| 1626 | } |
| 1627 | } |
| 1628 | { |
| 1629 | var it = q.putters.first; |
| 1630 | while (it) |node| : (it = node.next) { |
| 1631 | const putter: *Put = @alignCast(@fieldParentPtr("node", node)); |
| 1632 | putter.condition.signal(io); |
| 1633 | } |
| 1634 | } |
| 1635 | } |
| 1636 | |
| 1637 | pub fn put(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize) (QueueClosedError || Cancelable)!usize { |
| 1607 | 1638 | assert(elements.len >= min); |
| 1608 | 1639 | if (elements.len == 0) return 0; |
| 1609 | 1640 | try q.mutex.lock(io); |
| ... | ... | @@ -1614,13 +1645,14 @@ pub const TypeErasedQueue = struct { |
| 1614 | 1645 | /// Same as `put`, except does not introduce a cancelation point. |
| 1615 | 1646 | /// |
| 1616 | 1647 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1617 | | pub fn putUncancelable(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize) usize { |
| 1648 | pub fn putUncancelable(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize) QueueClosedError!usize { |
| 1618 | 1649 | assert(elements.len >= min); |
| 1619 | 1650 | if (elements.len == 0) return 0; |
| 1620 | 1651 | q.mutex.lockUncancelable(io); |
| 1621 | 1652 | defer q.mutex.unlock(io); |
| 1622 | 1653 | return q.putLocked(io, elements, min, true) catch |err| switch (err) { |
| 1623 | 1654 | error.Canceled => unreachable, |
| 1655 | error.Closed => |e| return e, |
| 1624 | 1656 | }; |
| 1625 | 1657 | } |
| 1626 | 1658 | |
| ... | ... | @@ -1634,49 +1666,79 @@ pub const TypeErasedQueue = struct { |
| 1634 | 1666 | return if (slice.len > 0) slice else null; |
| 1635 | 1667 | } |
| 1636 | 1668 | |
| 1637 | | fn putLocked(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize, uncancelable: bool) Cancelable!usize { |
| 1669 | fn putLocked(q: *TypeErasedQueue, io: Io, elements: []const u8, target: usize, uncancelable: bool) (QueueClosedError || Cancelable)!usize { |
| 1670 | // A closed queue cannot be added to, even if there is space in the buffer. |
| 1671 | if (q.closed) return error.Closed; |
| 1672 | |
| 1638 | 1673 | // Getters have first priority on the data, and only when the getters |
| 1639 | 1674 | // queue is empty do we start populating the buffer. |
| 1640 | 1675 | |
| 1641 | | var remaining = elements; |
| 1676 | // The number of elements we add immediately, before possibly blocking. |
| 1677 | var n: usize = 0; |
| 1678 | |
| 1642 | 1679 | while (q.getters.popFirst()) |getter_node| { |
| 1643 | 1680 | const getter: *Get = @alignCast(@fieldParentPtr("node", getter_node)); |
| 1644 | | const copy_len = @min(getter.remaining.len, remaining.len); |
| 1681 | const copy_len = @min(getter.remaining.len, elements.len - n); |
| 1645 | 1682 | assert(copy_len > 0); |
| 1646 | | @memcpy(getter.remaining[0..copy_len], remaining[0..copy_len]); |
| 1647 | | remaining = remaining[copy_len..]; |
| 1683 | @memcpy(getter.remaining[0..copy_len], elements[n..][0..copy_len]); |
| 1648 | 1684 | getter.remaining = getter.remaining[copy_len..]; |
| 1649 | | if (getter.remaining.len == 0) { |
| 1685 | getter.needed -|= copy_len; |
| 1686 | n += copy_len; |
| 1687 | if (getter.needed == 0) { |
| 1650 | 1688 | getter.condition.signal(io); |
| 1651 | | if (remaining.len > 0) continue; |
| 1652 | | } else q.getters.prepend(getter_node); |
| 1653 | | assert(remaining.len == 0); |
| 1654 | | return elements.len; |
| 1689 | } else { |
| 1690 | assert(n == elements.len); // we didn't have enough elements for the getter |
| 1691 | q.getters.prepend(getter_node); |
| 1692 | } |
| 1693 | if (n == elements.len) return elements.len; |
| 1655 | 1694 | } |
| 1656 | 1695 | |
| 1657 | 1696 | while (q.puttableSlice()) |slice| { |
| 1658 | | const copy_len = @min(slice.len, remaining.len); |
| 1697 | const copy_len = @min(slice.len, elements.len - n); |
| 1659 | 1698 | assert(copy_len > 0); |
| 1660 | | @memcpy(slice[0..copy_len], remaining[0..copy_len]); |
| 1699 | @memcpy(slice[0..copy_len], elements[n..][0..copy_len]); |
| 1661 | 1700 | q.len += copy_len; |
| 1662 | | remaining = remaining[copy_len..]; |
| 1663 | | if (remaining.len == 0) return elements.len; |
| 1701 | n += copy_len; |
| 1702 | if (n == elements.len) return elements.len; |
| 1664 | 1703 | } |
| 1665 | 1704 | |
| 1666 | | const total_filled = elements.len - remaining.len; |
| 1667 | | if (total_filled >= min) return total_filled; |
| 1705 | // Don't block if we hit the target. |
| 1706 | if (n >= target) return n; |
| 1668 | 1707 | |
| 1669 | | var pending: Put = .{ .remaining = remaining, .condition = .{}, .node = .{} }; |
| 1708 | var pending: Put = .{ |
| 1709 | .remaining = elements[n..], |
| 1710 | .needed = target - n, |
| 1711 | .condition = .init, |
| 1712 | .node = .{}, |
| 1713 | }; |
| 1670 | 1714 | q.putters.append(&pending.node); |
| 1671 | | defer if (pending.remaining.len > 0) q.putters.remove(&pending.node); |
| 1672 | | while (pending.remaining.len > 0) if (uncancelable) |
| 1673 | | pending.condition.waitUncancelable(io, &q.mutex) |
| 1674 | | else |
| 1675 | | try pending.condition.wait(io, &q.mutex); |
| 1676 | | return elements.len; |
| 1715 | defer if (pending.needed > 0) q.putters.remove(&pending.node); |
| 1716 | |
| 1717 | while (pending.needed > 0 and !q.closed) { |
| 1718 | if (uncancelable) { |
| 1719 | pending.condition.waitUncancelable(io, &q.mutex); |
| 1720 | continue; |
| 1721 | } |
| 1722 | pending.condition.wait(io, &q.mutex) catch |err| switch (err) { |
| 1723 | error.Canceled => if (pending.remaining.len == elements.len) { |
| 1724 | // Canceled while waiting, and appended no elements. |
| 1725 | return error.Canceled; |
| 1726 | } else { |
| 1727 | // Canceled while waiting, but appended some elements, so report those first. |
| 1728 | io.recancel(); |
| 1729 | return elements.len - pending.remaining.len; |
| 1730 | }, |
| 1731 | }; |
| 1732 | } |
| 1733 | if (pending.remaining.len == elements.len) { |
| 1734 | // The queue was closed while we were waiting. We appended no elements. |
| 1735 | assert(q.closed); |
| 1736 | return error.Closed; |
| 1737 | } |
| 1738 | return elements.len - pending.remaining.len; |
| 1677 | 1739 | } |
| 1678 | 1740 | |
| 1679 | | pub fn get(q: *@This(), io: Io, buffer: []u8, min: usize) Cancelable!usize { |
| 1741 | pub fn get(q: *TypeErasedQueue, io: Io, buffer: []u8, min: usize) (QueueClosedError || Cancelable)!usize { |
| 1680 | 1742 | assert(buffer.len >= min); |
| 1681 | 1743 | if (buffer.len == 0) return 0; |
| 1682 | 1744 | try q.mutex.lock(io); |
| ... | ... | @@ -1687,13 +1749,14 @@ pub const TypeErasedQueue = struct { |
| 1687 | 1749 | /// Same as `get`, except does not introduce a cancelation point. |
| 1688 | 1750 | /// |
| 1689 | 1751 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1690 | | pub fn getUncancelable(q: *TypeErasedQueue, io: Io, buffer: []u8, min: usize) usize { |
| 1752 | pub fn getUncancelable(q: *TypeErasedQueue, io: Io, buffer: []u8, min: usize) QueueClosedError!usize { |
| 1691 | 1753 | assert(buffer.len >= min); |
| 1692 | 1754 | if (buffer.len == 0) return 0; |
| 1693 | 1755 | q.mutex.lockUncancelable(io); |
| 1694 | 1756 | defer q.mutex.unlock(io); |
| 1695 | 1757 | return q.getLocked(io, buffer, min, true) catch |err| switch (err) { |
| 1696 | 1758 | error.Canceled => unreachable, |
| 1759 | error.Closed => |e| return e, |
| 1697 | 1760 | }; |
| 1698 | 1761 | } |
| 1699 | 1762 | |
| ... | ... | @@ -1703,21 +1766,23 @@ pub const TypeErasedQueue = struct { |
| 1703 | 1766 | return if (slice.len > 0) slice else null; |
| 1704 | 1767 | } |
| 1705 | 1768 | |
| 1706 | | fn getLocked(q: *@This(), io: Io, buffer: []u8, min: usize, uncancelable: bool) Cancelable!usize { |
| 1769 | fn getLocked(q: *TypeErasedQueue, io: Io, buffer: []u8, target: usize, uncancelable: bool) (QueueClosedError || Cancelable)!usize { |
| 1707 | 1770 | // The ring buffer gets first priority, then data should come from any |
| 1708 | 1771 | // queued putters, then finally the ring buffer should be filled with |
| 1709 | 1772 | // data from putters so they can be resumed. |
| 1710 | 1773 | |
| 1711 | | var remaining = buffer; |
| 1774 | // The number of elements we received immediately, before possibly blocking. |
| 1775 | var n: usize = 0; |
| 1776 | |
| 1712 | 1777 | while (q.gettableSlice()) |slice| { |
| 1713 | | const copy_len = @min(slice.len, remaining.len); |
| 1778 | const copy_len = @min(slice.len, buffer.len - n); |
| 1714 | 1779 | assert(copy_len > 0); |
| 1715 | | @memcpy(remaining[0..copy_len], slice[0..copy_len]); |
| 1780 | @memcpy(buffer[n..][0..copy_len], slice[0..copy_len]); |
| 1716 | 1781 | q.start += copy_len; |
| 1717 | 1782 | if (q.buffer.len - q.start == 0) q.start = 0; |
| 1718 | 1783 | q.len -= copy_len; |
| 1719 | | remaining = remaining[copy_len..]; |
| 1720 | | if (remaining.len == 0) { |
| 1784 | n += copy_len; |
| 1785 | if (n == buffer.len) { |
| 1721 | 1786 | q.fillRingBufferFromPutters(io); |
| 1722 | 1787 | return buffer.len; |
| 1723 | 1788 | } |
| ... | ... | @@ -1726,33 +1791,64 @@ pub const TypeErasedQueue = struct { |
| 1726 | 1791 | // Copy directly from putters into buffer. |
| 1727 | 1792 | while (q.putters.popFirst()) |putter_node| { |
| 1728 | 1793 | const putter: *Put = @alignCast(@fieldParentPtr("node", putter_node)); |
| 1729 | | const copy_len = @min(putter.remaining.len, remaining.len); |
| 1794 | const copy_len = @min(putter.remaining.len, buffer.len - n); |
| 1730 | 1795 | assert(copy_len > 0); |
| 1731 | | @memcpy(remaining[0..copy_len], putter.remaining[0..copy_len]); |
| 1796 | @memcpy(buffer[n..][0..copy_len], putter.remaining[0..copy_len]); |
| 1732 | 1797 | putter.remaining = putter.remaining[copy_len..]; |
| 1733 | | remaining = remaining[copy_len..]; |
| 1734 | | if (putter.remaining.len == 0) { |
| 1798 | putter.needed -|= copy_len; |
| 1799 | n += copy_len; |
| 1800 | if (putter.needed == 0) { |
| 1735 | 1801 | putter.condition.signal(io); |
| 1736 | | if (remaining.len > 0) continue; |
| 1737 | | } else q.putters.prepend(putter_node); |
| 1738 | | assert(remaining.len == 0); |
| 1739 | | q.fillRingBufferFromPutters(io); |
| 1740 | | return buffer.len; |
| 1802 | } else { |
| 1803 | assert(n == buffer.len); // we didn't have enough space for the putter |
| 1804 | q.putters.prepend(putter_node); |
| 1805 | } |
| 1806 | if (n == buffer.len) { |
| 1807 | q.fillRingBufferFromPutters(io); |
| 1808 | return buffer.len; |
| 1809 | } |
| 1741 | 1810 | } |
| 1742 | 1811 | |
| 1743 | | // Both ring buffer and putters queue is empty. |
| 1744 | | const total_filled = buffer.len - remaining.len; |
| 1745 | | if (total_filled >= min) return total_filled; |
| 1812 | // No need to call `fillRingBufferFromPutters` from this point onwards, |
| 1813 | // because we emptied the ring buffer *and* the putter queue! |
| 1746 | 1814 | |
| 1747 | | var pending: Get = .{ .remaining = remaining, .condition = .{}, .node = .{} }; |
| 1815 | // Don't block if we hit the target or if the queue is closed. Return how |
| 1816 | // many elements we could get immediately, unless the queue was closed and |
| 1817 | // empty, in which case report `error.Closed`. |
| 1818 | if (n == 0 and q.closed) return error.Closed; |
| 1819 | if (n >= target or q.closed) return n; |
| 1820 | |
| 1821 | var pending: Get = .{ |
| 1822 | .remaining = buffer[n..], |
| 1823 | .needed = target - n, |
| 1824 | .condition = .init, |
| 1825 | .node = .{}, |
| 1826 | }; |
| 1748 | 1827 | q.getters.append(&pending.node); |
| 1749 | | defer if (pending.remaining.len > 0) q.getters.remove(&pending.node); |
| 1750 | | while (pending.remaining.len > 0) if (uncancelable) |
| 1751 | | pending.condition.waitUncancelable(io, &q.mutex) |
| 1752 | | else |
| 1753 | | try pending.condition.wait(io, &q.mutex); |
| 1754 | | q.fillRingBufferFromPutters(io); |
| 1755 | | return buffer.len; |
| 1828 | defer if (pending.needed > 0) q.getters.remove(&pending.node); |
| 1829 | |
| 1830 | while (pending.needed > 0 and !q.closed) { |
| 1831 | if (uncancelable) { |
| 1832 | pending.condition.waitUncancelable(io, &q.mutex); |
| 1833 | continue; |
| 1834 | } |
| 1835 | pending.condition.wait(io, &q.mutex) catch |err| switch (err) { |
| 1836 | error.Canceled => if (pending.remaining.len == buffer.len) { |
| 1837 | // Canceled while waiting, and received no elements. |
| 1838 | return error.Canceled; |
| 1839 | } else { |
| 1840 | // Canceled while waiting, but received some elements, so report those first. |
| 1841 | io.recancel(); |
| 1842 | return buffer.len - pending.remaining.len; |
| 1843 | }, |
| 1844 | }; |
| 1845 | } |
| 1846 | if (pending.remaining.len == buffer.len) { |
| 1847 | // The queue was closed while we were waiting. We received no elements. |
| 1848 | assert(q.closed); |
| 1849 | return error.Closed; |
| 1850 | } |
| 1851 | return buffer.len - pending.remaining.len; |
| 1756 | 1852 | } |
| 1757 | 1853 | |
| 1758 | 1854 | /// Called when there is nonzero space available in the ring buffer and |
| ... | ... | @@ -1768,7 +1864,8 @@ pub const TypeErasedQueue = struct { |
| 1768 | 1864 | @memcpy(slice[0..copy_len], putter.remaining[0..copy_len]); |
| 1769 | 1865 | q.len += copy_len; |
| 1770 | 1866 | putter.remaining = putter.remaining[copy_len..]; |
| 1771 | | if (putter.remaining.len == 0) { |
| 1867 | putter.needed -|= copy_len; |
| 1868 | if (putter.needed == 0) { |
| 1772 | 1869 | putter.condition.signal(io); |
| 1773 | 1870 | break; |
| 1774 | 1871 | } |
| ... | ... | @@ -1791,59 +1888,101 @@ pub fn Queue(Elem: type) type { |
| 1791 | 1888 | return .{ .type_erased = .init(@ptrCast(buffer)) }; |
| 1792 | 1889 | } |
| 1793 | 1890 | |
| 1794 | | /// Appends elements to the end of the queue. The function returns when |
| 1795 | | /// at least `min` elements have been added to the buffer or sent |
| 1796 | | /// directly to a consumer. |
| 1891 | pub fn close(q: *@This(), io: Io) void { |
| 1892 | q.type_erased.close(io); |
| 1893 | } |
| 1894 | |
| 1895 | /// Appends elements to the end of the queue, potentially blocking if |
| 1896 | /// there is insufficient capacity. Returns when any one of the |
| 1897 | /// following conditions is satisfied: |
| 1898 | /// |
| 1899 | /// * At least `target` elements have been added to the queue |
| 1900 | /// * The queue is closed |
| 1901 | /// * The current task is canceled |
| 1902 | /// |
| 1903 | /// Returns how many of `elements` have been added to the queue, if any. |
| 1904 | /// If an error is returned, no elements have been added. |
| 1797 | 1905 | /// |
| 1798 | | /// Returns how many elements have been added to the queue. |
| 1906 | /// If the queue is closed or the task is canceled, but some items were |
| 1907 | /// already added before the closure or cancelation, then `put` may |
| 1908 | /// return a number lower than `target`, in which case future calls are |
| 1909 | /// guaranteed to return `error.Canceled` or `error.Closed`. |
| 1799 | 1910 | /// |
| 1800 | | /// Asserts that `elements.len >= min`. |
| 1801 | | pub fn put(q: *@This(), io: Io, elements: []const Elem, min: usize) Cancelable!usize { |
| 1802 | | return @divExact(try q.type_erased.put(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1911 | /// A return value of 0 is only possible if `target` is 0, in which case |
| 1912 | /// the call is guaranteed to queue as many of `elements` as is possible |
| 1913 | /// *without* blocking. |
| 1914 | /// |
| 1915 | /// Asserts that `elements.len >= target`. |
| 1916 | pub fn put(q: *@This(), io: Io, elements: []const Elem, target: usize) (QueueClosedError || Cancelable)!usize { |
| 1917 | return @divExact(try q.type_erased.put(io, @ptrCast(elements), target * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1803 | 1918 | } |
| 1804 | 1919 | |
| 1805 | 1920 | /// Same as `put` but blocks until all elements have been added to the queue. |
| 1806 | | pub fn putAll(q: *@This(), io: Io, elements: []const Elem) Cancelable!void { |
| 1807 | | assert(try q.put(io, elements, elements.len) == elements.len); |
| 1921 | /// |
| 1922 | /// If the queue is closed or canceled, `error.Closed` or `error.Canceled` |
| 1923 | /// is returned, and it is unspecified how many, if any, of `elements` were |
| 1924 | /// added to the queue prior to cancelation or closure. |
| 1925 | pub fn putAll(q: *@This(), io: Io, elements: []const Elem) (QueueClosedError || Cancelable)!void { |
| 1926 | const n = try q.put(io, elements, elements.len); |
| 1927 | if (n != elements.len) { |
| 1928 | _ = try q.put(io, elements[n..], elements.len - n); |
| 1929 | unreachable; // partial `put` implies queue was closed or we were canceled |
| 1930 | } |
| 1808 | 1931 | } |
| 1809 | 1932 | |
| 1810 | 1933 | /// Same as `put`, except does not introduce a cancelation point. |
| 1811 | 1934 | /// |
| 1812 | 1935 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1813 | | pub fn putUncancelable(q: *@This(), io: Io, elements: []const Elem, min: usize) usize { |
| 1814 | | return @divExact(q.type_erased.putUncancelable(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1936 | pub fn putUncancelable(q: *@This(), io: Io, elements: []const Elem, min: usize) QueueClosedError!usize { |
| 1937 | return @divExact(try q.type_erased.putUncancelable(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1815 | 1938 | } |
| 1816 | 1939 | |
| 1817 | | pub fn putOne(q: *@This(), io: Io, item: Elem) Cancelable!void { |
| 1940 | /// Appends `item` to the end of the queue, blocking if the queue is full. |
| 1941 | pub fn putOne(q: *@This(), io: Io, item: Elem) (QueueClosedError || Cancelable)!void { |
| 1818 | 1942 | assert(try q.put(io, &.{item}, 1) == 1); |
| 1819 | 1943 | } |
| 1820 | 1944 | |
| 1821 | 1945 | /// Same as `putOne`, except does not introduce a cancelation point. |
| 1822 | 1946 | /// |
| 1823 | 1947 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1824 | | pub fn putOneUncancelable(q: *@This(), io: Io, item: Elem) void { |
| 1825 | | assert(q.putUncancelable(io, &.{item}, 1) == 1); |
| 1948 | pub fn putOneUncancelable(q: *@This(), io: Io, item: Elem) QueueClosedError!void { |
| 1949 | assert(try q.putUncancelable(io, &.{item}, 1) == 1); |
| 1826 | 1950 | } |
| 1827 | 1951 | |
| 1828 | | /// Receives elements from the beginning of the queue. The function |
| 1829 | | /// returns when at least `min` elements have been populated inside |
| 1830 | | /// `buffer`. |
| 1952 | /// Receives elements from the beginning of the queue, potentially blocking |
| 1953 | /// if there are insufficient elements currently in the queue. Returns when |
| 1954 | /// any one of the following conditions is satisfied: |
| 1955 | /// |
| 1956 | /// * At least `target` elements have been received from the queue |
| 1957 | /// * The queue is closed and contains no buffered elements |
| 1958 | /// * The current task is canceled |
| 1959 | /// |
| 1960 | /// Returns how many elements of `buffer` have been populated, if any. |
| 1961 | /// If an error is returned, no elements have been populated. |
| 1962 | /// |
| 1963 | /// If the queue is closed or the task is canceled, but some items were |
| 1964 | /// already received before the closure or cancelation, then `get` may |
| 1965 | /// return a number lower than `target`, in which case future calls are |
| 1966 | /// guaranteed to return `error.Canceled` or `error.Closed`. |
| 1831 | 1967 | /// |
| 1832 | | /// Returns how many elements of `buffer` have been populated. |
| 1968 | /// A return value of 0 is only possible if `target` is 0, in which case |
| 1969 | /// the call is guaranteed to fill as much of `buffer` as is possible |
| 1970 | /// *without* blocking. |
| 1833 | 1971 | /// |
| 1834 | | /// Asserts that `buffer.len >= min`. |
| 1835 | | pub fn get(q: *@This(), io: Io, buffer: []Elem, min: usize) Cancelable!usize { |
| 1836 | | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1972 | /// Asserts that `buffer.len >= target`. |
| 1973 | pub fn get(q: *@This(), io: Io, buffer: []Elem, target: usize) (QueueClosedError || Cancelable)!usize { |
| 1974 | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), target * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1837 | 1975 | } |
| 1838 | 1976 | |
| 1839 | 1977 | /// Same as `get`, except does not introduce a cancelation point. |
| 1840 | 1978 | /// |
| 1841 | 1979 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1842 | | pub fn getUncancelable(q: *@This(), io: Io, buffer: []Elem, min: usize) usize { |
| 1980 | pub fn getUncancelable(q: *@This(), io: Io, buffer: []Elem, min: usize) QueueClosedError!usize { |
| 1843 | 1981 | return @divExact(try q.type_erased.getUncancelable(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1844 | 1982 | } |
| 1845 | 1983 | |
| 1846 | | pub fn getOne(q: *@This(), io: Io) Cancelable!Elem { |
| 1984 | /// Receives one element from the beginning of the queue, blocking if the queue is empty. |
| 1985 | pub fn getOne(q: *@This(), io: Io) (QueueClosedError || Cancelable)!Elem { |
| 1847 | 1986 | var buf: [1]Elem = undefined; |
| 1848 | 1987 | assert(try q.get(io, &buf, 1) == 1); |
| 1849 | 1988 | return buf[0]; |
| ... | ... | @@ -1852,9 +1991,9 @@ pub fn Queue(Elem: type) type { |
| 1852 | 1991 | /// Same as `getOne`, except does not introduce a cancelation point. |
| 1853 | 1992 | /// |
| 1854 | 1993 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1855 | | pub fn getOneUncancelable(q: *@This(), io: Io) Elem { |
| 1994 | pub fn getOneUncancelable(q: *@This(), io: Io) QueueClosedError!Elem { |
| 1856 | 1995 | var buf: [1]Elem = undefined; |
| 1857 | | assert(q.getUncancelable(io, &buf, 1) == 1); |
| 1996 | assert(try q.getUncancelable(io, &buf, 1) == 1); |
| 1858 | 1997 | return buf[0]; |
| 1859 | 1998 | } |
| 1860 | 1999 | |