| ... | ... | @@ -650,6 +650,10 @@ pub const VTable = struct { |
| 650 | 650 | groupWait: *const fn (?*anyopaque, *Group, token: *anyopaque) void, |
| 651 | 651 | groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void, |
| 652 | 652 | |
| 653 | recancel: *const fn (?*anyopaque) void, |
| 654 | swapCancelProtection: *const fn (?*anyopaque, new: CancelProtection) CancelProtection, |
| 655 | checkCancel: *const fn (?*anyopaque) Cancelable!void, |
| 656 | |
| 653 | 657 | /// Blocks until one of the futures from the list has a result ready, such |
| 654 | 658 | /// that awaiting it will not block. Returns that index. |
| 655 | 659 | select: *const fn (?*anyopaque, futures: []const *AnyFuture) Cancelable!usize, |
| ... | ... | @@ -982,7 +986,14 @@ pub fn Future(Result: type) type { |
| 982 | 986 | any_future: ?*AnyFuture, |
| 983 | 987 | result: Result, |
| 984 | 988 | |
| 985 | | /// Equivalent to `await` but places a cancellation request. |
| 989 | /// Equivalent to `await` but places a cancellation request. This causes the task to receive |
| 990 | /// `error.Canceled` from its next "cancelation point" (if any). A cancelation point is a |
| 991 | /// call to a function in `Io` which can return `error.Canceled`. |
| 992 | /// |
| 993 | /// After cancelation of a task is requested, only the next cancelation point in that task |
| 994 | /// will return `error.Canceled`: future points will not re-signal the cancelation. As such, |
| 995 | /// it is usually a bug to ignore `error.Canceled`. However, to defer handling cancelation |
| 996 | /// requests, see also `recancel` and `CancelProtection`. |
| 986 | 997 | /// |
| 987 | 998 | /// Idempotent. Not threadsafe. |
| 988 | 999 | pub fn cancel(f: *@This(), io: Io) Result { |
| ... | ... | @@ -1079,6 +1090,8 @@ pub const Group = struct { |
| 1079 | 1090 | /// Equivalent to `wait` but immediately requests cancellation on all |
| 1080 | 1091 | /// members of the group. |
| 1081 | 1092 | /// |
| 1093 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1094 | /// |
| 1082 | 1095 | /// Idempotent. Not threadsafe. |
| 1083 | 1096 | pub fn cancel(g: *Group, io: Io) void { |
| 1084 | 1097 | const token = g.token orelse return; |
| ... | ... | @@ -1087,6 +1100,61 @@ pub const Group = struct { |
| 1087 | 1100 | } |
| 1088 | 1101 | }; |
| 1089 | 1102 | |
| 1103 | /// Asserts that `error.Canceled` was returned from a prior cancelation point, and "re-arms" the |
| 1104 | /// cancelation request, so that `error.Canceled` will be returned again from the next cancelation |
| 1105 | /// point. |
| 1106 | /// |
| 1107 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1108 | pub fn recancel(io: Io) void { |
| 1109 | io.vtable.recancel(io.userdata); |
| 1110 | } |
| 1111 | |
| 1112 | /// In rare cases, it is desirable to completely block cancelation notification, so that a region |
| 1113 | /// of code can run uninterrupted before `error.Canceled` is potentially observed. Therefore, every |
| 1114 | /// task has a "cancel protection" state which indicates whether or not `Io` functions can introduce |
| 1115 | /// cancelation points. |
| 1116 | /// |
| 1117 | /// To modify a task's cancel protection state, see `swapCancelProtection`. |
| 1118 | /// |
| 1119 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1120 | pub const CancelProtection = enum { |
| 1121 | /// Any call to an `Io` function with `error.Canceled` in its error set is a cancelation point. |
| 1122 | /// |
| 1123 | /// This is the default state, which all tasks are created in. |
| 1124 | unblocked, |
| 1125 | /// No `Io` function introduces a cancelation point (`error.Canceled` will never be returned). |
| 1126 | blocked, |
| 1127 | }; |
| 1128 | /// Updates the current task's cancel protection state (see `CancelProtection`). |
| 1129 | /// |
| 1130 | /// The typical usage for this function is to protect a block of code from cancelation: |
| 1131 | /// ``` |
| 1132 | /// const old_cancel_protect = io.swapCancelProtection(.blocked); |
| 1133 | /// defer _ = io.swapCancelProtection(old_cancel_protect); |
| 1134 | /// doSomeWork() catch |err| switch (err) { |
| 1135 | /// error.Canceled => unreachable, |
| 1136 | /// }; |
| 1137 | /// ``` |
| 1138 | /// |
| 1139 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1140 | pub fn swapCancelProtection(io: Io, new: CancelProtection) CancelProtection { |
| 1141 | return io.vtable.swapCancelProtection(io.userdata, new); |
| 1142 | } |
| 1143 | |
| 1144 | /// This function acts as a pure cancelation point (subject to protection; see `CancelProtection`) |
| 1145 | /// and does nothing else. In other words, it returns `error.Canceled` if there is an outstanding |
| 1146 | /// non-blocked cancelation request, but otherwise is a no-op. |
| 1147 | /// |
| 1148 | /// It is rarely necessary to call this function. The primary use case is in long-running CPU-bound |
| 1149 | /// tasks which may need to respond to cancelation before completing. Short tasks, or those which |
| 1150 | /// perform other `Io` operations (and hence have other cancelation points), will typically already |
| 1151 | /// respond quickly to cancelation requests. |
| 1152 | /// |
| 1153 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1154 | pub fn checkCancel(io: Io) Cancelable!void { |
| 1155 | return io.vtable.checkCancel(io.userdata); |
| 1156 | } |
| 1157 | |
| 1090 | 1158 | pub fn Select(comptime U: type) type { |
| 1091 | 1159 | return struct { |
| 1092 | 1160 | io: Io, |
| ... | ... | @@ -1160,6 +1228,8 @@ pub fn Select(comptime U: type) type { |
| 1160 | 1228 | /// Equivalent to `wait` but requests cancellation on all remaining |
| 1161 | 1229 | /// tasks owned by the select. |
| 1162 | 1230 | /// |
| 1231 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1232 | /// |
| 1163 | 1233 | /// It is illegal to call `wait` after this. |
| 1164 | 1234 | /// |
| 1165 | 1235 | /// Idempotent. Not threadsafe. |
| ... | ... | @@ -1193,7 +1263,9 @@ pub fn futexWaitTimeout(io: Io, comptime T: type, ptr: *align(@alignOf(u32)) con |
| 1193 | 1263 | const expected_raw: *align(1) const u32 = @ptrCast(&expected); |
| 1194 | 1264 | return io.vtable.futexWait(io.userdata, @ptrCast(ptr), expected_raw.*, timeout); |
| 1195 | 1265 | } |
| 1196 | | /// Same as `futexWait`, except is not affected by task cancelation. |
| 1266 | /// Same as `futexWait`, except does not introduce a cancelation point. |
| 1267 | /// |
| 1268 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1197 | 1269 | pub fn futexWaitUncancelable(io: Io, comptime T: type, ptr: *align(@alignOf(u32)) const T, expected: T) void { |
| 1198 | 1270 | comptime assert(@sizeOf(T) == @sizeOf(u32)); |
| 1199 | 1271 | const expected_raw: *align(1) const u32 = @ptrCast(&expected); |
| ... | ... | @@ -1247,6 +1319,9 @@ pub const Mutex = struct { |
| 1247 | 1319 | } |
| 1248 | 1320 | } |
| 1249 | 1321 | |
| 1322 | /// Same as `lock`, except does not introduce a cancelation point. |
| 1323 | /// |
| 1324 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1250 | 1325 | pub fn lockUncancelable(m: *Mutex, io: Io) void { |
| 1251 | 1326 | const initial_state = m.state.cmpxchgWeak( |
| 1252 | 1327 | .unlocked, |
| ... | ... | @@ -1296,6 +1371,9 @@ pub const Condition = struct { |
| 1296 | 1371 | try waitInner(cond, io, mutex, false); |
| 1297 | 1372 | } |
| 1298 | 1373 | |
| 1374 | /// Same as `wait`, except does not introduce a cancelation point. |
| 1375 | /// |
| 1376 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1299 | 1377 | pub fn waitUncancelable(cond: *Condition, io: Io, mutex: *Mutex) void { |
| 1300 | 1378 | waitInner(cond, io, mutex, true) catch |err| switch (err) { |
| 1301 | 1379 | error.Canceled => unreachable, |
| ... | ... | @@ -1424,7 +1502,9 @@ pub const Event = enum(u32) { |
| 1424 | 1502 | } |
| 1425 | 1503 | } |
| 1426 | 1504 | |
| 1427 | | /// Same as `wait` except uninterruptible. |
| 1505 | /// Same as `wait`, except does not introduce a cancelation point. |
| 1506 | /// |
| 1507 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1428 | 1508 | pub fn waitUncancelable(event: *Event, io: Io) void { |
| 1429 | 1509 | if (@cmpxchgStrong(Event, event, .unset, .waiting, .acquire, .acquire)) |prev| switch (prev) { |
| 1430 | 1510 | .unset => unreachable, |
| ... | ... | @@ -1531,7 +1611,9 @@ pub const TypeErasedQueue = struct { |
| 1531 | 1611 | return q.putLocked(io, elements, min, false); |
| 1532 | 1612 | } |
| 1533 | 1613 | |
| 1534 | | /// Same as `put` but cannot be canceled. |
| 1614 | /// Same as `put`, except does not introduce a cancelation point. |
| 1615 | /// |
| 1616 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1535 | 1617 | pub fn putUncancelable(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize) usize { |
| 1536 | 1618 | assert(elements.len >= min); |
| 1537 | 1619 | if (elements.len == 0) return 0; |
| ... | ... | @@ -1602,7 +1684,10 @@ pub const TypeErasedQueue = struct { |
| 1602 | 1684 | return q.getLocked(io, buffer, min, false); |
| 1603 | 1685 | } |
| 1604 | 1686 | |
| 1605 | | pub fn getUncancelable(q: *@This(), io: Io, buffer: []u8, min: usize) usize { |
| 1687 | /// Same as `get`, except does not introduce a cancelation point. |
| 1688 | /// |
| 1689 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1690 | pub fn getUncancelable(q: *TypeErasedQueue, io: Io, buffer: []u8, min: usize) usize { |
| 1606 | 1691 | assert(buffer.len >= min); |
| 1607 | 1692 | if (buffer.len == 0) return 0; |
| 1608 | 1693 | q.mutex.lockUncancelable(io); |
| ... | ... | @@ -1722,7 +1807,9 @@ pub fn Queue(Elem: type) type { |
| 1722 | 1807 | assert(try q.put(io, elements, elements.len) == elements.len); |
| 1723 | 1808 | } |
| 1724 | 1809 | |
| 1725 | | /// Same as `put` but cannot be interrupted. |
| 1810 | /// Same as `put`, except does not introduce a cancelation point. |
| 1811 | /// |
| 1812 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1726 | 1813 | pub fn putUncancelable(q: *@This(), io: Io, elements: []const Elem, min: usize) usize { |
| 1727 | 1814 | return @divExact(q.type_erased.putUncancelable(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1728 | 1815 | } |
| ... | ... | @@ -1731,6 +1818,9 @@ pub fn Queue(Elem: type) type { |
| 1731 | 1818 | assert(try q.put(io, &.{item}, 1) == 1); |
| 1732 | 1819 | } |
| 1733 | 1820 | |
| 1821 | /// Same as `putOne`, except does not introduce a cancelation point. |
| 1822 | /// |
| 1823 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1734 | 1824 | pub fn putOneUncancelable(q: *@This(), io: Io, item: Elem) void { |
| 1735 | 1825 | assert(q.putUncancelable(io, &.{item}, 1) == 1); |
| 1736 | 1826 | } |
| ... | ... | @@ -1746,8 +1836,11 @@ pub fn Queue(Elem: type) type { |
| 1746 | 1836 | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1747 | 1837 | } |
| 1748 | 1838 | |
| 1839 | /// Same as `get`, except does not introduce a cancelation point. |
| 1840 | /// |
| 1841 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1749 | 1842 | pub fn getUncancelable(q: *@This(), io: Io, buffer: []Elem, min: usize) usize { |
| 1750 | | return @divExact(q.type_erased.getUncancelable(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1843 | return @divExact(try q.type_erased.getUncancelable(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); |
| 1751 | 1844 | } |
| 1752 | 1845 | |
| 1753 | 1846 | pub fn getOne(q: *@This(), io: Io) Cancelable!Elem { |
| ... | ... | @@ -1756,6 +1849,9 @@ pub fn Queue(Elem: type) type { |
| 1756 | 1849 | return buf[0]; |
| 1757 | 1850 | } |
| 1758 | 1851 | |
| 1852 | /// Same as `getOne`, except does not introduce a cancelation point. |
| 1853 | /// |
| 1854 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| 1759 | 1855 | pub fn getOneUncancelable(q: *@This(), io: Io) Elem { |
| 1760 | 1856 | var buf: [1]Elem = undefined; |
| 1761 | 1857 | assert(q.getUncancelable(io, &buf, 1) == 1); |
| ... | ... | @@ -1846,10 +1942,6 @@ pub fn concurrent( |
| 1846 | 1942 | return future; |
| 1847 | 1943 | } |
| 1848 | 1944 | |
| 1849 | | pub fn cancelRequested(io: Io) bool { |
| 1850 | | return io.vtable.cancelRequested(io.userdata); |
| 1851 | | } |
| 1852 | | |
| 1853 | 1945 | pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable; |
| 1854 | 1946 | |
| 1855 | 1947 | pub fn sleep(io: Io, duration: Duration, clock: Clock) SleepError!void { |