| ... | @@ -650,6 +650,10 @@ pub const VTable = struct { | ... | @@ -650,6 +650,10 @@ pub const VTable = struct { |
| 650 | groupWait: *const fn (?*anyopaque, *Group, token: *anyopaque) void, | 650 | groupWait: *const fn (?*anyopaque, *Group, token: *anyopaque) void, |
| 651 | groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void, | 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 | /// Blocks until one of the futures from the list has a result ready, such | 657 | /// Blocks until one of the futures from the list has a result ready, such |
| 654 | /// that awaiting it will not block. Returns that index. | 658 | /// that awaiting it will not block. Returns that index. |
| 655 | select: *const fn (?*anyopaque, futures: []const *AnyFuture) Cancelable!usize, | 659 | select: *const fn (?*anyopaque, futures: []const *AnyFuture) Cancelable!usize, |
| ... | @@ -982,7 +986,14 @@ pub fn Future(Result: type) type { | ... | @@ -982,7 +986,14 @@ pub fn Future(Result: type) type { |
| 982 | any_future: ?*AnyFuture, | 986 | any_future: ?*AnyFuture, |
| 983 | result: Result, | 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 | /// Idempotent. Not threadsafe. | 998 | /// Idempotent. Not threadsafe. |
| 988 | pub fn cancel(f: *@This(), io: Io) Result { | 999 | pub fn cancel(f: *@This(), io: Io) Result { |
| ... | @@ -1079,6 +1090,8 @@ pub const Group = struct { | ... | @@ -1079,6 +1090,8 @@ pub const Group = struct { |
| 1079 | /// Equivalent to `wait` but immediately requests cancellation on all | 1090 | /// Equivalent to `wait` but immediately requests cancellation on all |
| 1080 | /// members of the group. | 1091 | /// members of the group. |
| 1081 | /// | 1092 | /// |
| | 1093 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| | 1094 | /// |
| 1082 | /// Idempotent. Not threadsafe. | 1095 | /// Idempotent. Not threadsafe. |
| 1083 | pub fn cancel(g: *Group, io: Io) void { | 1096 | pub fn cancel(g: *Group, io: Io) void { |
| 1084 | const token = g.token orelse return; | 1097 | const token = g.token orelse return; |
| ... | @@ -1087,6 +1100,61 @@ pub const Group = struct { | ... | @@ -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 | pub fn Select(comptime U: type) type { | 1158 | pub fn Select(comptime U: type) type { |
| 1091 | return struct { | 1159 | return struct { |
| 1092 | io: Io, | 1160 | io: Io, |
| ... | @@ -1160,6 +1228,8 @@ pub fn Select(comptime U: type) type { | ... | @@ -1160,6 +1228,8 @@ pub fn Select(comptime U: type) type { |
| 1160 | /// Equivalent to `wait` but requests cancellation on all remaining | 1228 | /// Equivalent to `wait` but requests cancellation on all remaining |
| 1161 | /// tasks owned by the select. | 1229 | /// tasks owned by the select. |
| 1162 | /// | 1230 | /// |
| | 1231 | /// For a description of cancelation and cancelation points, see `Future.cancel`. |
| | 1232 | /// |
| 1163 | /// It is illegal to call `wait` after this. | 1233 | /// It is illegal to call `wait` after this. |
| 1164 | /// | 1234 | /// |
| 1165 | /// Idempotent. Not threadsafe. | 1235 | /// Idempotent. Not threadsafe. |
| ... | @@ -1193,7 +1263,9 @@ pub fn futexWaitTimeout(io: Io, comptime T: type, ptr: *align(@alignOf(u32)) con | ... | @@ -1193,7 +1263,9 @@ pub fn futexWaitTimeout(io: Io, comptime T: type, ptr: *align(@alignOf(u32)) con |
| 1193 | const expected_raw: *align(1) const u32 = @ptrCast(&expected); | 1263 | const expected_raw: *align(1) const u32 = @ptrCast(&expected); |
| 1194 | return io.vtable.futexWait(io.userdata, @ptrCast(ptr), expected_raw.*, timeout); | 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 | pub fn futexWaitUncancelable(io: Io, comptime T: type, ptr: *align(@alignOf(u32)) const T, expected: T) void { | 1269 | pub fn futexWaitUncancelable(io: Io, comptime T: type, ptr: *align(@alignOf(u32)) const T, expected: T) void { |
| 1198 | comptime assert(@sizeOf(T) == @sizeOf(u32)); | 1270 | comptime assert(@sizeOf(T) == @sizeOf(u32)); |
| 1199 | const expected_raw: *align(1) const u32 = @ptrCast(&expected); | 1271 | const expected_raw: *align(1) const u32 = @ptrCast(&expected); |
| ... | @@ -1247,6 +1319,9 @@ pub const Mutex = struct { | ... | @@ -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 | pub fn lockUncancelable(m: *Mutex, io: Io) void { | 1325 | pub fn lockUncancelable(m: *Mutex, io: Io) void { |
| 1251 | const initial_state = m.state.cmpxchgWeak( | 1326 | const initial_state = m.state.cmpxchgWeak( |
| 1252 | .unlocked, | 1327 | .unlocked, |
| ... | @@ -1296,6 +1371,9 @@ pub const Condition = struct { | ... | @@ -1296,6 +1371,9 @@ pub const Condition = struct { |
| 1296 | try waitInner(cond, io, mutex, false); | 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 | pub fn waitUncancelable(cond: *Condition, io: Io, mutex: *Mutex) void { | 1377 | pub fn waitUncancelable(cond: *Condition, io: Io, mutex: *Mutex) void { |
| 1300 | waitInner(cond, io, mutex, true) catch |err| switch (err) { | 1378 | waitInner(cond, io, mutex, true) catch |err| switch (err) { |
| 1301 | error.Canceled => unreachable, | 1379 | error.Canceled => unreachable, |
| ... | @@ -1424,7 +1502,9 @@ pub const Event = enum(u32) { | ... | @@ -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 | pub fn waitUncancelable(event: *Event, io: Io) void { | 1508 | pub fn waitUncancelable(event: *Event, io: Io) void { |
| 1429 | if (@cmpxchgStrong(Event, event, .unset, .waiting, .acquire, .acquire)) |prev| switch (prev) { | 1509 | if (@cmpxchgStrong(Event, event, .unset, .waiting, .acquire, .acquire)) |prev| switch (prev) { |
| 1430 | .unset => unreachable, | 1510 | .unset => unreachable, |
| ... | @@ -1531,7 +1611,9 @@ pub const TypeErasedQueue = struct { | ... | @@ -1531,7 +1611,9 @@ pub const TypeErasedQueue = struct { |
| 1531 | return q.putLocked(io, elements, min, false); | 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 | pub fn putUncancelable(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize) usize { | 1617 | pub fn putUncancelable(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize) usize { |
| 1536 | assert(elements.len >= min); | 1618 | assert(elements.len >= min); |
| 1537 | if (elements.len == 0) return 0; | 1619 | if (elements.len == 0) return 0; |
| ... | @@ -1602,7 +1684,10 @@ pub const TypeErasedQueue = struct { | ... | @@ -1602,7 +1684,10 @@ pub const TypeErasedQueue = struct { |
| 1602 | return q.getLocked(io, buffer, min, false); | 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 | assert(buffer.len >= min); | 1691 | assert(buffer.len >= min); |
| 1607 | if (buffer.len == 0) return 0; | 1692 | if (buffer.len == 0) return 0; |
| 1608 | q.mutex.lockUncancelable(io); | 1693 | q.mutex.lockUncancelable(io); |
| ... | @@ -1722,7 +1807,9 @@ pub fn Queue(Elem: type) type { | ... | @@ -1722,7 +1807,9 @@ pub fn Queue(Elem: type) type { |
| 1722 | assert(try q.put(io, elements, elements.len) == elements.len); | 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 | pub fn putUncancelable(q: *@This(), io: Io, elements: []const Elem, min: usize) usize { | 1813 | pub fn putUncancelable(q: *@This(), io: Io, elements: []const Elem, min: usize) usize { |
| 1727 | return @divExact(q.type_erased.putUncancelable(io, @ptrCast(elements), min * @sizeOf(Elem)), @sizeOf(Elem)); | 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,6 +1818,9 @@ pub fn Queue(Elem: type) type { |
| 1731 | assert(try q.put(io, &.{item}, 1) == 1); | 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 | pub fn putOneUncancelable(q: *@This(), io: Io, item: Elem) void { | 1824 | pub fn putOneUncancelable(q: *@This(), io: Io, item: Elem) void { |
| 1735 | assert(q.putUncancelable(io, &.{item}, 1) == 1); | 1825 | assert(q.putUncancelable(io, &.{item}, 1) == 1); |
| 1736 | } | 1826 | } |
| ... | @@ -1746,8 +1836,11 @@ pub fn Queue(Elem: type) type { | ... | @@ -1746,8 +1836,11 @@ pub fn Queue(Elem: type) type { |
| 1746 | return @divExact(try q.type_erased.get(io, @ptrCast(buffer), min * @sizeOf(Elem)), @sizeOf(Elem)); | 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 | pub fn getUncancelable(q: *@This(), io: Io, buffer: []Elem, min: usize) usize { | 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 | pub fn getOne(q: *@This(), io: Io) Cancelable!Elem { | 1846 | pub fn getOne(q: *@This(), io: Io) Cancelable!Elem { |
| ... | @@ -1756,6 +1849,9 @@ pub fn Queue(Elem: type) type { | ... | @@ -1756,6 +1849,9 @@ pub fn Queue(Elem: type) type { |
| 1756 | return buf[0]; | 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 | pub fn getOneUncancelable(q: *@This(), io: Io) Elem { | 1855 | pub fn getOneUncancelable(q: *@This(), io: Io) Elem { |
| 1760 | var buf: [1]Elem = undefined; | 1856 | var buf: [1]Elem = undefined; |
| 1761 | assert(q.getUncancelable(io, &buf, 1) == 1); | 1857 | assert(q.getUncancelable(io, &buf, 1) == 1); |
| ... | @@ -1846,10 +1942,6 @@ pub fn concurrent( | ... | @@ -1846,10 +1942,6 @@ pub fn concurrent( |
| 1846 | return future; | 1942 | return future; |
| 1847 | } | 1943 | } |
| 1848 | | 1944 | |
| 1849 | pub fn cancelRequested(io: Io) bool { | | |
| 1850 | return io.vtable.cancelRequested(io.userdata); | | |
| 1851 | } | | |
| 1852 | | | |
| 1853 | pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable; | 1945 | pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable; |
| 1854 | | 1946 | |
| 1855 | pub fn sleep(io: Io, duration: Duration, clock: Clock) SleepError!void { | 1947 | pub fn sleep(io: Io, duration: Duration, clock: Clock) SleepError!void { |