| ... | ... | @@ -1173,12 +1173,20 @@ pub fn checkCancel(io: Io) Cancelable!void { |
| 1173 | 1173 | return io.vtable.checkCancel(io.userdata); |
| 1174 | 1174 | } |
| 1175 | 1175 | |
| 1176 | /// Executes tasks together, providing a mechanism to wait until one or more |
| 1177 | /// tasks complete. Similar to `Batch` but operates at the higher level task |
| 1178 | /// abstraction layer rather than lower level `Operation` abstraction layer. |
| 1179 | /// |
| 1180 | /// The provided tagged union will be used as the return type of the await |
| 1181 | /// function. When calling async or concurrent, one specifies which union field |
| 1182 | /// the called function's result will be placed into upon completion. |
| 1176 | 1183 | pub fn Select(comptime U: type) type { |
| 1177 | 1184 | return struct { |
| 1178 | 1185 | io: Io, |
| 1179 | 1186 | group: Group, |
| 1187 | /// The queue is never closed because there may be live resources |
| 1188 | /// inserted into it which would otherwise leak. |
| 1180 | 1189 | queue: Queue(U), |
| 1181 | | outstanding: usize, |
| 1182 | 1190 | |
| 1183 | 1191 | const S = @This(); |
| 1184 | 1192 | |
| ... | ... | @@ -1191,7 +1199,6 @@ pub fn Select(comptime U: type) type { |
| 1191 | 1199 | .io = io, |
| 1192 | 1200 | .queue = .init(buffer), |
| 1193 | 1201 | .group = .init, |
| 1194 | | .outstanding = 0, |
| 1195 | 1202 | }; |
| 1196 | 1203 | } |
| 1197 | 1204 | |
| ... | ... | @@ -1235,7 +1242,6 @@ pub fn Select(comptime U: type) type { |
| 1235 | 1242 | } |
| 1236 | 1243 | }; |
| 1237 | 1244 | const context: Context = .{ .select = s, .args = args }; |
| 1238 | | _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic); |
| 1239 | 1245 | s.io.vtable.groupAsync(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start); |
| 1240 | 1246 | } |
| 1241 | 1247 | |
| ... | ... | @@ -1276,22 +1282,31 @@ pub fn Select(comptime U: type) type { |
| 1276 | 1282 | }; |
| 1277 | 1283 | const context: Context = .{ .select = s, .args = args }; |
| 1278 | 1284 | try s.io.vtable.groupConcurrent(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start); |
| 1279 | | _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic); |
| 1280 | 1285 | } |
| 1281 | 1286 | |
| 1282 | 1287 | /// Blocks until another task of the select finishes. |
| 1283 | 1288 | /// |
| 1284 | | /// Asserts there is at least one more `outstanding` task. |
| 1285 | | /// |
| 1286 | | /// Not threadsafe. |
| 1289 | /// Threadsafe. |
| 1287 | 1290 | pub fn await(s: *S) Cancelable!U { |
| 1288 | | s.outstanding -= 1; |
| 1289 | 1291 | return s.queue.getOne(s.io) catch |err| switch (err) { |
| 1290 | 1292 | error.Canceled => |e| return e, |
| 1291 | 1293 | error.Closed => unreachable, |
| 1292 | 1294 | }; |
| 1293 | 1295 | } |
| 1294 | 1296 | |
| 1297 | /// Blocks until at least `min` number of results have been copied |
| 1298 | /// into `buffer`. |
| 1299 | /// |
| 1300 | /// Asserts that `buffer.len >= min`. |
| 1301 | /// |
| 1302 | /// Threadsafe. |
| 1303 | pub fn awaitMany(s: *S, buffer: []U, min: usize) Cancelable!usize { |
| 1304 | return s.queue.get(s.io, buffer, min) catch |err| switch (err) { |
| 1305 | error.Canceled => |e| return e, |
| 1306 | error.Closed => unreachable, |
| 1307 | }; |
| 1308 | } |
| 1309 | |
| 1295 | 1310 | /// Equivalent to `await` but requests cancelation on all remaining |
| 1296 | 1311 | /// tasks owned by the select. |
| 1297 | 1312 | /// |
| ... | ... | @@ -1299,9 +1314,8 @@ pub fn Select(comptime U: type) type { |
| 1299 | 1314 | /// |
| 1300 | 1315 | /// It is illegal to call `await` after this. |
| 1301 | 1316 | /// |
| 1302 | | /// Idempotent. Not threadsafe. |
| 1317 | /// Idempotent. Threadsafe. |
| 1303 | 1318 | pub fn cancel(s: *S) void { |
| 1304 | | s.outstanding = 0; |
| 1305 | 1319 | s.group.cancel(s.io); |
| 1306 | 1320 | } |
| 1307 | 1321 | }; |
| ... | ... | @@ -1731,7 +1745,7 @@ pub const TypeErasedQueue = struct { |
| 1731 | 1745 | return if (slice.len > 0) slice else null; |
| 1732 | 1746 | } |
| 1733 | 1747 | |
| 1734 | | fn putLocked(q: *TypeErasedQueue, io: Io, elements: []const u8, target: usize, uncancelable: bool) (QueueClosedError || Cancelable)!usize { |
| 1748 | fn putLocked(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize, uncancelable: bool) (QueueClosedError || Cancelable)!usize { |
| 1735 | 1749 | // A closed queue cannot be added to, even if there is space in the buffer. |
| 1736 | 1750 | if (q.closed) return error.Closed; |
| 1737 | 1751 | |
| ... | ... | @@ -1767,12 +1781,12 @@ pub const TypeErasedQueue = struct { |
| 1767 | 1781 | if (n == elements.len) return elements.len; |
| 1768 | 1782 | } |
| 1769 | 1783 | |
| 1770 | | // Don't block if we hit the target. |
| 1771 | | if (n >= target) return n; |
| 1784 | // Don't block if we hit the min. |
| 1785 | if (n >= min) return n; |
| 1772 | 1786 | |
| 1773 | 1787 | var pending: Put = .{ |
| 1774 | 1788 | .remaining = elements[n..], |
| 1775 | | .needed = target - n, |
| 1789 | .needed = min - n, |
| 1776 | 1790 | .condition = .init, |
| 1777 | 1791 | .node = .{}, |
| 1778 | 1792 | }; |
| ... | ... | @@ -1831,7 +1845,7 @@ pub const TypeErasedQueue = struct { |
| 1831 | 1845 | return if (slice.len > 0) slice else null; |
| 1832 | 1846 | } |
| 1833 | 1847 | |
| 1834 | | fn getLocked(q: *TypeErasedQueue, io: Io, buffer: []u8, target: usize, uncancelable: bool) (QueueClosedError || Cancelable)!usize { |
| 1848 | fn getLocked(q: *TypeErasedQueue, io: Io, buffer: []u8, min: usize, uncancelable: bool) (QueueClosedError || Cancelable)!usize { |
| 1835 | 1849 | // The ring buffer gets first priority, then data should come from any |
| 1836 | 1850 | // queued putters, then finally the ring buffer should be filled with |
| 1837 | 1851 | // data from putters so they can be resumed. |
| ... | ... | @@ -1877,15 +1891,15 @@ pub const TypeErasedQueue = struct { |
| 1877 | 1891 | // No need to call `fillRingBufferFromPutters` from this point onwards, |
| 1878 | 1892 | // because we emptied the ring buffer *and* the putter queue! |
| 1879 | 1893 | |
| 1880 | | // Don't block if we hit the target or if the queue is closed. Return how |
| 1894 | // Don't block if we hit the min or if the queue is closed. Return how |
| 1881 | 1895 | // many elements we could get immediately, unless the queue was closed and |
| 1882 | 1896 | // empty, in which case report `error.Closed`. |
| 1883 | 1897 | if (n == 0 and q.closed) return error.Closed; |
| 1884 | | if (n >= target or q.closed) return n; |
| 1898 | if (n >= min or q.closed) return n; |
| 1885 | 1899 | |
| 1886 | 1900 | var pending: Get = .{ |
| 1887 | 1901 | .remaining = buffer[n..], |
| 1888 | | .needed = target - n, |
| 1902 | .needed = min - n, |
| 1889 | 1903 | .condition = .init, |
| 1890 | 1904 | .node = .{}, |
| 1891 | 1905 | }; |
| ... | ... | @@ -1961,7 +1975,7 @@ pub fn Queue(Elem: type) type { |
| 1961 | 1975 | /// there is insufficient capacity. Returns when any one of the |
| 1962 | 1976 | /// following conditions is satisfied: |
| 1963 | 1977 | /// |
| 1964 | | /// * At least `target` elements have been added to the queue |
| 1978 | /// * At least `min` elements have been added to the queue |
| 1965 | 1979 | /// * The queue is closed |
| 1966 | 1980 | /// * The current task is canceled |
| 1967 | 1981 | /// |
| ... | ... | @@ -1970,16 +1984,16 @@ pub fn Queue(Elem: type) type { |
| 1970 | 1984 | /// |
| 1971 | 1985 | /// If the queue is closed or the task is canceled, but some items were |
| 1972 | 1986 | /// already added before the closure or cancelation, then `put` may |
| 1973 | | /// return a number lower than `target`, in which case future calls are |
| 1987 | /// return a number lower than `min`, in which case future calls are |
| 1974 | 1988 | /// guaranteed to return `error.Canceled` or `error.Closed`. |
| 1975 | 1989 | /// |
| 1976 | | /// A return value of 0 is only possible if `target` is 0, in which case |
| 1990 | /// A return value of 0 is only possible if `min` is 0, in which case |
| 1977 | 1991 | /// the call is guaranteed to queue as many of `elements` as is possible |
| 1978 | 1992 | /// *without* blocking. |
| 1979 | 1993 | /// |
| 1980 | | /// Asserts that `elements.len >= target`. |
| 1981 | | pub fn put(q: *@This(), io: Io, elements: []const Elem, target: usize) (QueueClosedError || Cancelable)!usize { |
| 1982 | | return @divExact(try q.type_erased.put(io, @ptrCast(elements), target * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1994 | /// Asserts that `elements.len >= min`. |
| 1995 | pub fn put(q: *@This(), io: Io, elements: []const Elem, min: usize) (QueueClosedError || Cancelable)!usize { |
| 1996 | return @divExact(try q.type_erased.put(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1983 | 1997 | } |
| 1984 | 1998 | |
| 1985 | 1999 | /// Same as `put` but blocks until all elements have been added to the queue. |
| ... | ... | @@ -2018,7 +2032,7 @@ pub fn Queue(Elem: type) type { |
| 2018 | 2032 | /// if there are insufficient elements currently in the queue. Returns when |
| 2019 | 2033 | /// any one of the following conditions is satisfied: |
| 2020 | 2034 | /// |
| 2021 | | /// * At least `target` elements have been received from the queue |
| 2035 | /// * At least `min` elements have been received from the queue |
| 2022 | 2036 | /// * The queue is closed and contains no buffered elements |
| 2023 | 2037 | /// * The current task is canceled |
| 2024 | 2038 | /// |
| ... | ... | @@ -2027,16 +2041,16 @@ pub fn Queue(Elem: type) type { |
| 2027 | 2041 | /// |
| 2028 | 2042 | /// If the queue is closed or the task is canceled, but some items were |
| 2029 | 2043 | /// already received before the closure or cancelation, then `get` may |
| 2030 | | /// return a number lower than `target`, in which case future calls are |
| 2044 | /// return a number lower than `min`, in which case future calls are |
| 2031 | 2045 | /// guaranteed to return `error.Canceled` or `error.Closed`. |
| 2032 | 2046 | /// |
| 2033 | | /// A return value of 0 is only possible if `target` is 0, in which case |
| 2047 | /// A return value of 0 is only possible if `min` is 0, in which case |
| 2034 | 2048 | /// the call is guaranteed to fill as much of `buffer` as is possible |
| 2035 | 2049 | /// *without* blocking. |
| 2036 | 2050 | /// |
| 2037 | | /// Asserts that `buffer.len >= target`. |
| 2038 | | pub fn get(q: *@This(), io: Io, buffer: []Elem, target: usize) (QueueClosedError || Cancelable)!usize { |
| 2039 | | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), target * @sizeOf(Elem)), @sizeOf(Elem)); |
| 2051 | /// Asserts that `buffer.len >= min`. |
| 2052 | pub fn get(q: *@This(), io: Io, buffer: []Elem, min: usize) (QueueClosedError || Cancelable)!usize { |
| 2053 | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 2040 | 2054 | } |
| 2041 | 2055 | |
| 2042 | 2056 | /// Same as `get`, except does not introduce a cancelation point. |