| ... | @@ -701,7 +701,7 @@ pub const VTable = struct { | ... | @@ -701,7 +701,7 @@ pub const VTable = struct { |
| 701 | netClose: *const fn (?*anyopaque, handle: net.Socket.Handle) void, | 701 | netClose: *const fn (?*anyopaque, handle: net.Socket.Handle) void, |
| 702 | netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface, | 702 | netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface, |
| 703 | netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name, | 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 | pub const Cancelable = error{ | 707 | pub const Cancelable = error{ |
| ... | @@ -1208,7 +1208,9 @@ pub fn Select(comptime U: type) type { | ... | @@ -1208,7 +1208,9 @@ pub fn Select(comptime U: type) type { |
| 1208 | const args_casted: *const Args = @ptrCast(@alignCast(context)); | 1208 | const args_casted: *const Args = @ptrCast(@alignCast(context)); |
| 1209 | const unerased_select: *S = @fieldParentPtr("group", group); | 1209 | const unerased_select: *S = @fieldParentPtr("group", group); |
| 1210 | const elem = @unionInit(U, @tagName(field), @call(.auto, function, args_casted.*)); | 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 | _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic); | 1216 | _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic); |
| ... | @@ -1222,7 +1224,10 @@ pub fn Select(comptime U: type) type { | ... | @@ -1222,7 +1224,10 @@ pub fn Select(comptime U: type) type { |
| 1222 | /// Not threadsafe. | 1224 | /// Not threadsafe. |
| 1223 | pub fn wait(s: *S) Cancelable!U { | 1225 | pub fn wait(s: *S) Cancelable!U { |
| 1224 | s.outstanding -= 1; | 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 | /// Equivalent to `wait` but requests cancellation on all remaining | 1233 | /// Equivalent to `wait` but requests cancellation on all remaining |
| ... | @@ -1569,8 +1574,11 @@ pub const Event = enum(u32) { | ... | @@ -1569,8 +1574,11 @@ pub const Event = enum(u32) { |
| 1569 | } | 1574 | } |
| 1570 | }; | 1575 | }; |
| 1571 | | 1576 | |
| | 1577 | pub const QueueClosedError = error{Closed}; |
| | 1578 | |
| 1572 | pub const TypeErasedQueue = struct { | 1579 | pub const TypeErasedQueue = struct { |
| 1573 | mutex: Mutex, | 1580 | mutex: Mutex, |
| | 1581 | closed: bool, |
| 1574 | | 1582 | |
| 1575 | /// Ring buffer. This data is logically *after* queued getters. | 1583 | /// Ring buffer. This data is logically *after* queued getters. |
| 1576 | buffer: []u8, | 1584 | buffer: []u8, |
| ... | @@ -1582,12 +1590,14 @@ pub const TypeErasedQueue = struct { | ... | @@ -1582,12 +1590,14 @@ pub const TypeErasedQueue = struct { |
| 1582 | | 1590 | |
| 1583 | const Put = struct { | 1591 | const Put = struct { |
| 1584 | remaining: []const u8, | 1592 | remaining: []const u8, |
| | 1593 | needed: usize, |
| 1585 | condition: Condition, | 1594 | condition: Condition, |
| 1586 | node: std.DoublyLinkedList.Node, | 1595 | node: std.DoublyLinkedList.Node, |
| 1587 | }; | 1596 | }; |
| 1588 | | 1597 | |
| 1589 | const Get = struct { | 1598 | const Get = struct { |
| 1590 | remaining: []u8, | 1599 | remaining: []u8, |
| | 1600 | needed: usize, |
| 1591 | condition: Condition, | 1601 | condition: Condition, |
| 1592 | node: std.DoublyLinkedList.Node, | 1602 | node: std.DoublyLinkedList.Node, |
| 1593 | }; | 1603 | }; |
| ... | @@ -1595,6 +1605,7 @@ pub const TypeErasedQueue = struct { | ... | @@ -1595,6 +1605,7 @@ pub const TypeErasedQueue = struct { |
| 1595 | pub fn init(buffer: []u8) TypeErasedQueue { | 1605 | pub fn init(buffer: []u8) TypeErasedQueue { |
| 1596 | return .{ | 1606 | return .{ |
| 1597 | .mutex = .init, | 1607 | .mutex = .init, |
| | 1608 | .closed = false, |
| 1598 | .buffer = buffer, | 1609 | .buffer = buffer, |
| 1599 | .start = 0, | 1610 | .start = 0, |
| 1600 | .len = 0, | 1611 | .len = 0, |
| ... | @@ -1603,7 +1614,27 @@ pub const TypeErasedQueue = struct { | ... | @@ -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 | assert(elements.len >= min); | 1638 | assert(elements.len >= min); |
| 1608 | if (elements.len == 0) return 0; | 1639 | if (elements.len == 0) return 0; |
| 1609 | try q.mutex.lock(io); | 1640 | try q.mutex.lock(io); |
| ... | @@ -1614,13 +1645,14 @@ pub const TypeErasedQueue = struct { | ... | @@ -1614,13 +1645,14 @@ pub const TypeErasedQueue = struct { |
| 1614 | /// Same as `put`, except does not introduce a cancelation point. | 1645 | /// Same as `put`, except does not introduce a cancelation point. |
| 1615 | /// | 1646 | /// |
| 1616 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 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 | assert(elements.len >= min); | 1649 | assert(elements.len >= min); |
| 1619 | if (elements.len == 0) return 0; | 1650 | if (elements.len == 0) return 0; |
| 1620 | q.mutex.lockUncancelable(io); | 1651 | q.mutex.lockUncancelable(io); |
| 1621 | defer q.mutex.unlock(io); | 1652 | defer q.mutex.unlock(io); |
| 1622 | return q.putLocked(io, elements, min, true) catch |err| switch (err) { | 1653 | return q.putLocked(io, elements, min, true) catch |err| switch (err) { |
| 1623 | error.Canceled => unreachable, | 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,49 +1666,79 @@ pub const TypeErasedQueue = struct { |
| 1634 | return if (slice.len > 0) slice else null; | 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 | // Getters have first priority on the data, and only when the getters | 1673 | // Getters have first priority on the data, and only when the getters |
| 1639 | // queue is empty do we start populating the buffer. | 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 | while (q.getters.popFirst()) |getter_node| { | 1679 | while (q.getters.popFirst()) |getter_node| { |
| 1643 | const getter: *Get = @alignCast(@fieldParentPtr("node", getter_node)); | 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 | assert(copy_len > 0); | 1682 | assert(copy_len > 0); |
| 1646 | @memcpy(getter.remaining[0..copy_len], remaining[0..copy_len]); | 1683 | @memcpy(getter.remaining[0..copy_len], elements[n..][0..copy_len]); |
| 1647 | remaining = remaining[copy_len..]; | | |
| 1648 | getter.remaining = getter.remaining[copy_len..]; | 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 | getter.condition.signal(io); | 1688 | getter.condition.signal(io); |
| 1651 | if (remaining.len > 0) continue; | 1689 | } else { |
| 1652 | } else q.getters.prepend(getter_node); | 1690 | assert(n == elements.len); // we didn't have enough elements for the getter |
| 1653 | assert(remaining.len == 0); | 1691 | q.getters.prepend(getter_node); |
| 1654 | return elements.len; | 1692 | } |
| | 1693 | if (n == elements.len) return elements.len; |
| 1655 | } | 1694 | } |
| 1656 | | 1695 | |
| 1657 | while (q.puttableSlice()) |slice| { | 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 | assert(copy_len > 0); | 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 | q.len += copy_len; | 1700 | q.len += copy_len; |
| 1662 | remaining = remaining[copy_len..]; | 1701 | n += copy_len; |
| 1663 | if (remaining.len == 0) return elements.len; | 1702 | if (n == elements.len) return elements.len; |
| 1664 | } | 1703 | } |
| 1665 | | 1704 | |
| 1666 | const total_filled = elements.len - remaining.len; | 1705 | // Don't block if we hit the target. |
| 1667 | if (total_filled >= min) return total_filled; | 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 | q.putters.append(&pending.node); | 1714 | q.putters.append(&pending.node); |
| 1671 | defer if (pending.remaining.len > 0) q.putters.remove(&pending.node); | 1715 | defer if (pending.needed > 0) q.putters.remove(&pending.node); |
| 1672 | while (pending.remaining.len > 0) if (uncancelable) | 1716 | |
| 1673 | pending.condition.waitUncancelable(io, &q.mutex) | 1717 | while (pending.needed > 0 and !q.closed) { |
| 1674 | else | 1718 | if (uncancelable) { |
| 1675 | try pending.condition.wait(io, &q.mutex); | 1719 | pending.condition.waitUncancelable(io, &q.mutex); |
| 1676 | return elements.len; | 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 | assert(buffer.len >= min); | 1742 | assert(buffer.len >= min); |
| 1681 | if (buffer.len == 0) return 0; | 1743 | if (buffer.len == 0) return 0; |
| 1682 | try q.mutex.lock(io); | 1744 | try q.mutex.lock(io); |
| ... | @@ -1687,13 +1749,14 @@ pub const TypeErasedQueue = struct { | ... | @@ -1687,13 +1749,14 @@ pub const TypeErasedQueue = struct { |
| 1687 | /// Same as `get`, except does not introduce a cancelation point. | 1749 | /// Same as `get`, except does not introduce a cancelation point. |
| 1688 | /// | 1750 | /// |
| 1689 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 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 | assert(buffer.len >= min); | 1753 | assert(buffer.len >= min); |
| 1692 | if (buffer.len == 0) return 0; | 1754 | if (buffer.len == 0) return 0; |
| 1693 | q.mutex.lockUncancelable(io); | 1755 | q.mutex.lockUncancelable(io); |
| 1694 | defer q.mutex.unlock(io); | 1756 | defer q.mutex.unlock(io); |
| 1695 | return q.getLocked(io, buffer, min, true) catch |err| switch (err) { | 1757 | return q.getLocked(io, buffer, min, true) catch |err| switch (err) { |
| 1696 | error.Canceled => unreachable, | 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,21 +1766,23 @@ pub const TypeErasedQueue = struct { |
| 1703 | return if (slice.len > 0) slice else null; | 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 | // The ring buffer gets first priority, then data should come from any | 1770 | // The ring buffer gets first priority, then data should come from any |
| 1708 | // queued putters, then finally the ring buffer should be filled with | 1771 | // queued putters, then finally the ring buffer should be filled with |
| 1709 | // data from putters so they can be resumed. | 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 | while (q.gettableSlice()) |slice| { | 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 | assert(copy_len > 0); | 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 | q.start += copy_len; | 1781 | q.start += copy_len; |
| 1717 | if (q.buffer.len - q.start == 0) q.start = 0; | 1782 | if (q.buffer.len - q.start == 0) q.start = 0; |
| 1718 | q.len -= copy_len; | 1783 | q.len -= copy_len; |
| 1719 | remaining = remaining[copy_len..]; | 1784 | n += copy_len; |
| 1720 | if (remaining.len == 0) { | 1785 | if (n == buffer.len) { |
| 1721 | q.fillRingBufferFromPutters(io); | 1786 | q.fillRingBufferFromPutters(io); |
| 1722 | return buffer.len; | 1787 | return buffer.len; |
| 1723 | } | 1788 | } |
| ... | @@ -1726,33 +1791,64 @@ pub const TypeErasedQueue = struct { | ... | @@ -1726,33 +1791,64 @@ pub const TypeErasedQueue = struct { |
| 1726 | // Copy directly from putters into buffer. | 1791 | // Copy directly from putters into buffer. |
| 1727 | while (q.putters.popFirst()) |putter_node| { | 1792 | while (q.putters.popFirst()) |putter_node| { |
| 1728 | const putter: *Put = @alignCast(@fieldParentPtr("node", putter_node)); | 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 | assert(copy_len > 0); | 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 | putter.remaining = putter.remaining[copy_len..]; | 1797 | putter.remaining = putter.remaining[copy_len..]; |
| 1733 | remaining = remaining[copy_len..]; | 1798 | putter.needed -|= copy_len; |
| 1734 | if (putter.remaining.len == 0) { | 1799 | n += copy_len; |
| | 1800 | if (putter.needed == 0) { |
| 1735 | putter.condition.signal(io); | 1801 | putter.condition.signal(io); |
| 1736 | if (remaining.len > 0) continue; | 1802 | } else { |
| 1737 | } else q.putters.prepend(putter_node); | 1803 | assert(n == buffer.len); // we didn't have enough space for the putter |
| 1738 | assert(remaining.len == 0); | 1804 | q.putters.prepend(putter_node); |
| 1739 | q.fillRingBufferFromPutters(io); | 1805 | } |
| 1740 | return buffer.len; | 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. | 1812 | // No need to call `fillRingBufferFromPutters` from this point onwards, |
| 1744 | const total_filled = buffer.len - remaining.len; | 1813 | // because we emptied the ring buffer *and* the putter queue! |
| 1745 | if (total_filled >= min) return total_filled; | | |
| 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 | q.getters.append(&pending.node); | 1827 | q.getters.append(&pending.node); |
| 1749 | defer if (pending.remaining.len > 0) q.getters.remove(&pending.node); | 1828 | defer if (pending.needed > 0) q.getters.remove(&pending.node); |
| 1750 | while (pending.remaining.len > 0) if (uncancelable) | 1829 | |
| 1751 | pending.condition.waitUncancelable(io, &q.mutex) | 1830 | while (pending.needed > 0 and !q.closed) { |
| 1752 | else | 1831 | if (uncancelable) { |
| 1753 | try pending.condition.wait(io, &q.mutex); | 1832 | pending.condition.waitUncancelable(io, &q.mutex); |
| 1754 | q.fillRingBufferFromPutters(io); | 1833 | continue; |
| 1755 | return buffer.len; | 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 | /// Called when there is nonzero space available in the ring buffer and | 1854 | /// Called when there is nonzero space available in the ring buffer and |
| ... | @@ -1768,7 +1864,8 @@ pub const TypeErasedQueue = struct { | ... | @@ -1768,7 +1864,8 @@ pub const TypeErasedQueue = struct { |
| 1768 | @memcpy(slice[0..copy_len], putter.remaining[0..copy_len]); | 1864 | @memcpy(slice[0..copy_len], putter.remaining[0..copy_len]); |
| 1769 | q.len += copy_len; | 1865 | q.len += copy_len; |
| 1770 | putter.remaining = putter.remaining[copy_len..]; | 1866 | putter.remaining = putter.remaining[copy_len..]; |
| 1771 | if (putter.remaining.len == 0) { | 1867 | putter.needed -|= copy_len; |
| | 1868 | if (putter.needed == 0) { |
| 1772 | putter.condition.signal(io); | 1869 | putter.condition.signal(io); |
| 1773 | break; | 1870 | break; |
| 1774 | } | 1871 | } |
| ... | @@ -1791,59 +1888,101 @@ pub fn Queue(Elem: type) type { | ... | @@ -1791,59 +1888,101 @@ pub fn Queue(Elem: type) type { |
| 1791 | return .{ .type_erased = .init(@ptrCast(buffer)) }; | 1888 | return .{ .type_erased = .init(@ptrCast(buffer)) }; |
| 1792 | } | 1889 | } |
| 1793 | | 1890 | |
| 1794 | /// Appends elements to the end of the queue. The function returns when | 1891 | pub fn close(q: *@This(), io: Io) void { |
| 1795 | /// at least `min` elements have been added to the buffer or sent | 1892 | q.type_erased.close(io); |
| 1796 | /// directly to a consumer. | 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`. | 1911 | /// A return value of 0 is only possible if `target` is 0, in which case |
| 1801 | pub fn put(q: *@This(), io: Io, elements: []const Elem, min: usize) Cancelable!usize { | 1912 | /// the call is guaranteed to queue as many of `elements` as is possible |
| 1802 | return @divExact(try q.type_erased.put(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); | 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 | /// Same as `put` but blocks until all elements have been added to the queue. | 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 { | 1921 | /// |
| 1807 | assert(try q.put(io, elements, elements.len) == elements.len); | 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 | /// Same as `put`, except does not introduce a cancelation point. | 1933 | /// Same as `put`, except does not introduce a cancelation point. |
| 1811 | /// | 1934 | /// |
| 1812 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 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 { | 1936 | pub fn putUncancelable(q: *@This(), io: Io, elements: []const Elem, min: usize) QueueClosedError!usize { |
| 1814 | return @divExact(q.type_erased.putUncancelable(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); | 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 | assert(try q.put(io, &.{item}, 1) == 1); | 1942 | assert(try q.put(io, &.{item}, 1) == 1); |
| 1819 | } | 1943 | } |
| 1820 | | 1944 | |
| 1821 | /// Same as `putOne`, except does not introduce a cancelation point. | 1945 | /// Same as `putOne`, except does not introduce a cancelation point. |
| 1822 | /// | 1946 | /// |
| 1823 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 1947 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1824 | pub fn putOneUncancelable(q: *@This(), io: Io, item: Elem) void { | 1948 | pub fn putOneUncancelable(q: *@This(), io: Io, item: Elem) QueueClosedError!void { |
| 1825 | assert(q.putUncancelable(io, &.{item}, 1) == 1); | 1949 | assert(try q.putUncancelable(io, &.{item}, 1) == 1); |
| 1826 | } | 1950 | } |
| 1827 | | 1951 | |
| 1828 | /// Receives elements from the beginning of the queue. The function | 1952 | /// Receives elements from the beginning of the queue, potentially blocking |
| 1829 | /// returns when at least `min` elements have been populated inside | 1953 | /// if there are insufficient elements currently in the queue. Returns when |
| 1830 | /// `buffer`. | 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`. | 1972 | /// Asserts that `buffer.len >= target`. |
| 1835 | pub fn get(q: *@This(), io: Io, buffer: []Elem, min: usize) Cancelable!usize { | 1973 | pub fn get(q: *@This(), io: Io, buffer: []Elem, target: usize) (QueueClosedError || Cancelable)!usize { |
| 1836 | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); | 1974 | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), target * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1837 | } | 1975 | } |
| 1838 | | 1976 | |
| 1839 | /// Same as `get`, except does not introduce a cancelation point. | 1977 | /// Same as `get`, except does not introduce a cancelation point. |
| 1840 | /// | 1978 | /// |
| 1841 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 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 | return @divExact(try q.type_erased.getUncancelable(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); | 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 | var buf: [1]Elem = undefined; | 1986 | var buf: [1]Elem = undefined; |
| 1848 | assert(try q.get(io, &buf, 1) == 1); | 1987 | assert(try q.get(io, &buf, 1) == 1); |
| 1849 | return buf[0]; | 1988 | return buf[0]; |
| ... | @@ -1852,9 +1991,9 @@ pub fn Queue(Elem: type) type { | ... | @@ -1852,9 +1991,9 @@ pub fn Queue(Elem: type) type { |
| 1852 | /// Same as `getOne`, except does not introduce a cancelation point. | 1991 | /// Same as `getOne`, except does not introduce a cancelation point. |
| 1853 | /// | 1992 | /// |
| 1854 | /// For a description of cancelation and cancelation points, see `Future.cancel`. | 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 | var buf: [1]Elem = undefined; | 1995 | var buf: [1]Elem = undefined; |
| 1857 | assert(q.getUncancelable(io, &buf, 1) == 1); | 1996 | assert(try q.getUncancelable(io, &buf, 1) == 1); |
| 1858 | return buf[0]; | 1997 | return buf[0]; |
| 1859 | } | 1998 | } |
| 1860 | | 1999 | |