authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-03 21:33:46+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-03 21:33:46+01:00
log8226d706e2cb69845c400ce22d8b263c4a390f11
tree9ddb96b16c8d6f7ffb141e9d7792f07274c0f8c7
parent04226193ccb69f50936e47804be56bd1bdc316d9
parent4de33579d8d8fdf310cd1a496eb68a7c5c62d81f

Merge pull request 'std.Io.Threaded: performance enhancements, bugfixes, and better Windows and NetBSD support' (#30634) from std.Io.Threaded-groups-2 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30634 Reviewed-by: Andrew Kelley <andrewrk@noreply.codeberg.org> Resolves: https://codeberg.org/ziglang/zig/issues/30049

17 files changed, 3721 insertions(+), 2778 deletions(-)

lib/compiler/build_runner.zig+1-1
...@@ -849,7 +849,7 @@ fn runStepNames(...@@ -849,7 +849,7 @@ fn runStepNames(
849 defer f.deinit();849 defer f.deinit();
850850
851 f.start();851 f.start();
852 f.waitAndPrintReport();852 try f.waitAndPrintReport();
853 }853 }
854854
855 // Every test has a state855 // Every test has a state
lib/std/Build/Fuzz.zig+2-2
...@@ -513,11 +513,11 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte...@@ -513,11 +513,11 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
513 try coverage_map.entry_points.append(fuzz.gpa, @intCast(index));513 try coverage_map.entry_points.append(fuzz.gpa, @intCast(index));
514}514}
515515
516pub fn waitAndPrintReport(fuzz: *Fuzz) void {516pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void {
517 assert(fuzz.mode == .limit);517 assert(fuzz.mode == .limit);
518 const io = fuzz.io;518 const io = fuzz.io;
519519
520 fuzz.group.awaitUncancelable(io);520 try fuzz.group.await(io);
521 fuzz.group = .init;521 fuzz.group = .init;
522522
523 std.debug.print("======= FUZZING REPORT =======\n", .{});523 std.debug.print("======= FUZZING REPORT =======\n", .{});
lib/std/Io.zig+41-58
...@@ -631,7 +631,7 @@ pub const VTable = struct {...@@ -631,7 +631,7 @@ pub const VTable = struct {
631 /// Copied and then passed to `start`.631 /// Copied and then passed to `start`.
632 context: []const u8,632 context: []const u8,
633 context_alignment: std.mem.Alignment,633 context_alignment: std.mem.Alignment,
634 start: *const fn (*Group, context: *const anyopaque) Cancelable!void,634 start: *const fn (context: *const anyopaque) Cancelable!void,
635 ) void,635 ) void,
636 /// Thread-safe.636 /// Thread-safe.
637 groupConcurrent: *const fn (637 groupConcurrent: *const fn (
...@@ -642,7 +642,7 @@ pub const VTable = struct {...@@ -642,7 +642,7 @@ pub const VTable = struct {
642 /// Copied and then passed to `start`.642 /// Copied and then passed to `start`.
643 context: []const u8,643 context: []const u8,
644 context_alignment: std.mem.Alignment,644 context_alignment: std.mem.Alignment,
645 start: *const fn (*Group, context: *const anyopaque) Cancelable!void,645 start: *const fn (context: *const anyopaque) Cancelable!void,
646 ) ConcurrentError!void,646 ) ConcurrentError!void,
647 groupAwait: *const fn (?*anyopaque, *Group, token: *anyopaque) Cancelable!void,647 groupAwait: *const fn (?*anyopaque, *Group, token: *anyopaque) Cancelable!void,
648 groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void,648 groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void,
...@@ -1050,40 +1050,40 @@ pub fn Future(Result: type) type {...@@ -1050,40 +1050,40 @@ pub fn Future(Result: type) type {
1050 };1050 };
1051}1051}
10521052
1053/// An unordered set of tasks which can only be awaited or canceled as a whole.
1054/// Tasks are spawned in the group with `Group.async` and `Group.concurrent`.
1055///
1056/// The resources associated with each task are *guaranteed* to be released when
1057/// the individual task returns, as opposed to when the whole group completes or
1058/// is awaited. For this reason, it is not a resource leak to have a long-lived
1059/// group which concurrent tasks are repeatedly added to. However, asynchronous
1060/// tasks are not guaranteed to run until `Group.await` or `Group.cancel` is
1061/// called, so adding async tasks to a group without ever awaiting it may leak
1062/// resources.
1053pub const Group = struct {1063pub const Group = struct {
1054 state: usize,
1055 context: ?*anyopaque,
1056 /// This value indicates whether or not a group has pending tasks. `null`1064 /// This value indicates whether or not a group has pending tasks. `null`
1057 /// means there are no pending tasks, and no resources associated with the1065 /// means there are no pending tasks, and no resources associated with the
1058 /// group, so `await` and `cancel` return immediately without calling the1066 /// group, so `await` and `cancel` return immediately without calling the
1059 /// implementation. This means that `token` must be accessed atomically to1067 /// implementation. This means that `token` must be accessed atomically to
1060 /// avoid racing with the check in `await` and `cancel`.1068 /// avoid racing with the check in `await` and `cancel`.
1061 token: std.atomic.Value(?*anyopaque),1069 token: std.atomic.Value(?*anyopaque),
1070 /// This value is available for the implementation to use as it wishes.
1071 state: usize,
10621072
1063 pub const init: Group = .{ .state = 0, .context = null, .token = .init(null) };1073 pub const init: Group = .{ .token = .init(null), .state = 0 };
10641074
1065 /// Calls `function` with `args` asynchronously. The resource spawned is1075 /// Equivalent to `Io.async`, except the task is spawned in this `Group`
1066 /// owned by the group.1076 /// instead of becoming associated with a `Future`.
1067 ///
1068 /// `function` *may* be called immediately, before `async` returns.
1069 ///1077 ///
1070 /// When this function returns, it is guaranteed that `function` has1078 /// The return type of `function` must be coercible to `Cancelable!void`.
1071 /// already been called and completed, or it has successfully been assigned
1072 /// a unit of concurrency.
1073 ///1079 ///
1074 /// After this is called, `await` or `cancel` must be called before the1080 /// Once this function is called, there are resources associated with the
1075 /// group is deinitialized.1081 /// group. To release those resources, `Group.await` or `Group.cancel` must
1076 ///1082 /// eventually be called.
1077 /// Threadsafe.
1078 ///
1079 /// See also:
1080 /// * `concurrent`
1081 /// * `Io.async`
1082 pub fn async(g: *Group, io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) void {1083 pub fn async(g: *Group, io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) void {
1083 const Args = @TypeOf(args);1084 const Args = @TypeOf(args);
1084 const TypeErased = struct {1085 const TypeErased = struct {
1085 fn start(group: *Group, context: *const anyopaque) Cancelable!void {1086 fn start(context: *const anyopaque) Cancelable!void {
1086 _ = group;
1087 const args_casted: *const Args = @ptrCast(@alignCast(context));1087 const args_casted: *const Args = @ptrCast(@alignCast(context));
1088 return @call(.auto, function, args_casted.*);1088 return @call(.auto, function, args_casted.*);
1089 }1089 }
...@@ -1091,27 +1091,18 @@ pub const Group = struct {...@@ -1091,27 +1091,18 @@ pub const Group = struct {
1091 io.vtable.groupAsync(io.userdata, g, @ptrCast(&args), .of(Args), TypeErased.start);1091 io.vtable.groupAsync(io.userdata, g, @ptrCast(&args), .of(Args), TypeErased.start);
1092 }1092 }
10931093
1094 /// Calls `function` with `args`, such that the function is not guaranteed1094 /// Equivalent to `Io.concurrent`, except the task is spawned in this
1095 /// to have returned until `await` is called, allowing the caller to1095 /// `Group` instead of becoming associated with a `Future`.
1096 /// progress while waiting for any `Io` operations.
1097 ///
1098 /// The resource spawned is owned by the group; after this is called,
1099 /// `await` or `cancel` must be called before the group is deinitialized.
1100 ///1096 ///
1101 /// This has stronger guarantee than `async`, placing restrictions on what kind1097 /// The return type of `function` must be coercible to `Cancelable!void`.
1102 /// of `Io` implementations are supported. By calling `async` instead, one
1103 /// allows, for example, stackful single-threaded blocking I/O.
1104 ///1098 ///
1105 /// Threadsafe.1099 /// Once this function is called, there are resources associated with the
1106 ///1100 /// group. To release those resources, `Group.await` or `Group.cancel` must
1107 /// See also:1101 /// eventually be called.
1108 /// * `async`
1109 /// * `Io.concurrent`
1110 pub fn concurrent(g: *Group, io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) ConcurrentError!void {1102 pub fn concurrent(g: *Group, io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) ConcurrentError!void {
1111 const Args = @TypeOf(args);1103 const Args = @TypeOf(args);
1112 const TypeErased = struct {1104 const TypeErased = struct {
1113 fn start(group: *Group, context: *const anyopaque) Cancelable!void {1105 fn start(context: *const anyopaque) Cancelable!void {
1114 _ = group;
1115 const args_casted: *const Args = @ptrCast(@alignCast(context));1106 const args_casted: *const Args = @ptrCast(@alignCast(context));
1116 return @call(.auto, function, args_casted.*);1107 return @call(.auto, function, args_casted.*);
1117 }1108 }
...@@ -1120,7 +1111,9 @@ pub const Group = struct {...@@ -1120,7 +1111,9 @@ pub const Group = struct {
1120 }1111 }
11211112
1122 /// Blocks until all tasks of the group finish. During this time,1113 /// Blocks until all tasks of the group finish. During this time,
1123 /// cancelation requests propagate to all members of the group.1114 /// cancelation requests propagate to all members of the group, and
1115 /// will also cause `error.Canceled` to be returned when the group
1116 /// does ultimately finish.
1124 ///1117 ///
1125 /// Idempotent. Not threadsafe.1118 /// Idempotent. Not threadsafe.
1126 ///1119 ///
...@@ -1133,17 +1126,6 @@ pub const Group = struct {...@@ -1133,17 +1126,6 @@ pub const Group = struct {
1133 assert(g.token.raw == null);1126 assert(g.token.raw == null);
1134 }1127 }
11351128
1136 /// Equivalent to `await` but temporarily blocks cancelation while waiting.
1137 pub fn awaitUncancelable(g: *Group, io: Io) void {
1138 const token = g.token.load(.acquire) orelse return;
1139 const prev = swapCancelProtection(io, .blocked);
1140 defer _ = swapCancelProtection(io, prev);
1141 io.vtable.groupAwait(io.userdata, g, token) catch |err| switch (err) {
1142 error.Canceled => unreachable,
1143 };
1144 assert(g.token.raw == null);
1145 }
1146
1147 /// Equivalent to `await` but immediately requests cancelation on all1129 /// Equivalent to `await` but immediately requests cancelation on all
1148 /// members of the group.1130 /// members of the group.
1149 ///1131 ///
...@@ -1263,19 +1245,20 @@ pub fn Select(comptime U: type) type {...@@ -1263,19 +1245,20 @@ pub fn Select(comptime U: type) type {
1263 function: anytype,1245 function: anytype,
1264 args: std.meta.ArgsTuple(@TypeOf(function)),1246 args: std.meta.ArgsTuple(@TypeOf(function)),
1265 ) void {1247 ) void {
1266 const Args = @TypeOf(args);1248 const Context = struct {
1267 const TypeErased = struct {1249 select: *S,
1268 fn start(group: *Group, context: *const anyopaque) Cancelable!void {1250 args: @TypeOf(args),
1269 const args_casted: *const Args = @ptrCast(@alignCast(context));1251 fn start(type_erased_context: *const anyopaque) Cancelable!void {
1270 const unerased_select: *S = @fieldParentPtr("group", group);1252 const context: *const @This() = @ptrCast(@alignCast(type_erased_context));
1271 const elem = @unionInit(U, @tagName(field), @call(.auto, function, args_casted.*));1253 const elem = @unionInit(U, @tagName(field), @call(.auto, function, context.args));
1272 unerased_select.queue.putOneUncancelable(unerased_select.io, elem) catch |err| switch (err) {1254 context.select.queue.putOneUncancelable(context.select.io, elem) catch |err| switch (err) {
1273 error.Closed => unreachable,1255 error.Closed => unreachable,
1274 };1256 };
1275 }1257 }
1276 };1258 };
1259 const context: Context = .{ .select = s, .args = args };
1277 _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic);1260 _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic);
1278 s.io.vtable.groupAsync(s.io.userdata, &s.group, @ptrCast(&args), .of(Args), TypeErased.start);1261 s.io.vtable.groupAsync(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start);
1279 }1262 }
12801263
1281 /// Blocks until another task of the select finishes.1264 /// Blocks until another task of the select finishes.
lib/std/Io/Threaded.zig+3373-2550
...@@ -37,9 +37,8 @@ cpu_count_error: ?std.Thread.CpuCountError,...@@ -37,9 +37,8 @@ cpu_count_error: ?std.Thread.CpuCountError,
37/// available count, subtract this from either `async_limit` or37/// available count, subtract this from either `async_limit` or
38/// `concurrent_limit`.38/// `concurrent_limit`.
39busy_count: usize = 0,39busy_count: usize = 0,
40main_thread: Thread,40worker_threads: std.atomic.Value(?*Thread),
41pid: Pid = .unknown,41pid: Pid = .unknown,
42robust_cancel: RobustCancel,
4342
44wsa: if (is_windows) Wsa else struct {} = .{},43wsa: if (is_windows) Wsa else struct {} = .{},
4544
...@@ -105,13 +104,6 @@ pub const Environ = struct {...@@ -105,13 +104,6 @@ pub const Environ = struct {
105 };104 };
106};105};
107106
108pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum {
109 enabled,
110 disabled,
111} else enum {
112 disabled,
113};
114
115pub const Pid = if (native_os == .linux) enum(posix.pid_t) {107pub const Pid = if (native_os == .linux) enum(posix.pid_t) {
116 unknown = 0,108 unknown = 0,
117 _,109 _,
...@@ -153,129 +145,507 @@ pub const UseFchmodat2 = if (have_fchmodat2 and !have_fchmodat_flags) enum {...@@ -153,129 +145,507 @@ pub const UseFchmodat2 = if (have_fchmodat2 and !have_fchmodat_flags) enum {
153 pub const default: UseFchmodat2 = .disabled;145 pub const default: UseFchmodat2 = .disabled;
154};146};
155147
156const Thread = struct {148const Runnable = struct {
157 /// The value that needs to be passed to pthread_kill or tgkill in order to149 node: std.SinglyLinkedList.Node,
158 /// send a signal.150 startFn: *const fn (*Runnable, *Thread, *Threaded) void,
159 signal_id: SignaleeId,151};
160 current_closure: ?*Closure,
161 /// Only populated if `current_closure != null`. Indicates the current cancel protection mode.
162 cancel_protection: Io.CancelProtection,
163
164 const SignaleeId = if (std.Thread.use_pthreads) std.c.pthread_t else std.Thread.Id;
165152
166 threadlocal var current: ?*Thread = null;153const Group = struct {
154 ptr: *Io.Group,
167155
168 fn getCurrent(t: *Threaded) *Thread {156 /// Returns a correctly-typed pointer to the `Io.Group.token` field.
169 return current orelse return &t.main_thread;157 ///
158 /// The status indicates how many pending tasks are in the group, whether the group has been
159 /// canceled, and whether the group has been awaited.
160 ///
161 /// Note that the zero value of `Status` intentionally represents the initial group state (empty
162 /// with no awaiters). This is a requirement of `Io.Group`.
163 fn status(g: Group) *std.atomic.Value(Status) {
164 return @ptrCast(&g.ptr.token);
165 }
166 /// Returns a correctly-typed pointer to the `Io.Group.state` field. The double-pointer here is
167 /// intentional, because the `state` field itself stores a pointer, and this function returns a
168 /// pointer to that field.
169 ///
170 /// On completion of the whole group, if `status` indicates that there is an awaiter, the last
171 /// task must increment this `u32` and do a futex wake on it to signal that awaiter.
172 fn awaiter(g: Group) **std.atomic.Value(u32) {
173 return @ptrCast(&g.ptr.state);
170 }174 }
171175
172 fn checkCancel(thread: *Thread) error{Canceled}!void {176 const Status = packed struct(usize) {
173 const closure = thread.current_closure orelse return;177 num_running: @Int(.unsigned, @bitSizeOf(usize) - 2),
178 have_awaiter: bool,
179 canceled: bool,
180 };
174181
175 switch (thread.cancel_protection) {182 const Task = struct {
176 .unblocked => {},183 runnable: Runnable,
177 .blocked => return,184 group: *Io.Group,
185 func: *const fn (context: *const anyopaque) Io.Cancelable!void,
186 context_alignment: Alignment,
187 alloc_len: usize,
188
189 /// `Task.runnable.node` is `undefined` in the created `Task`.
190 fn create(
191 gpa: Allocator,
192 group: Group,
193 context: []const u8,
194 context_alignment: Alignment,
195 func: *const fn (context: *const anyopaque) Io.Cancelable!void,
196 ) Allocator.Error!*Task {
197 const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(Task);
198 const worst_case_context_offset = context_alignment.forward(@sizeOf(Task) + max_context_misalignment);
199 const alloc_len = worst_case_context_offset + context.len;
200
201 const task: *Task = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(Task), alloc_len)));
202 errdefer comptime unreachable;
203
204 task.* = .{
205 .runnable = .{
206 .node = undefined,
207 .startFn = &start,
208 },
209 .group = group.ptr,
210 .func = func,
211 .context_alignment = context_alignment,
212 .alloc_len = alloc_len,
213 };
214 @memcpy(task.contextPointer()[0..context.len], context);
215 return task;
216 }
217
218 fn destroy(task: *Task, gpa: Allocator) void {
219 const base: [*]align(@alignOf(Task)) u8 = @ptrCast(task);
220 gpa.free(base[0..task.alloc_len]);
221 }
222
223 fn contextPointer(task: *Task) [*]u8 {
224 const base: [*]u8 = @ptrCast(task);
225 const offset = task.context_alignment.forward(@intFromPtr(base) + @sizeOf(Task)) - @intFromPtr(base);
226 return base + offset;
227 }
228
229 fn start(r: *Runnable, thread: *Thread, t: *Threaded) void {
230 const task: *Task = @fieldParentPtr("runnable", r);
231 const group: Group = .{ .ptr = task.group };
232
233 // This would be a simple store, but it's upgraded to an RMW so we can use `.acquire` to
234 // enforce the ordering between this and the `group.status().load` below. Paired with
235 // the `.release` rmw on `Thread.status` in `cancelThreads`, this creates a StoreLoad
236 // barrier which guarantees that when a group is canceled, either we see the cancelation
237 // in the group status, or the canceler sees our thread status so can directly notify us
238 // of the cancelation.
239 _ = thread.status.swap(.{
240 .cancelation = .none,
241 .awaitable = .fromGroup(group.ptr),
242 }, .acquire);
243 if (group.status().load(.monotonic).canceled) {
244 thread.status.store(.{
245 .cancelation = .canceling,
246 .awaitable = .fromGroup(group.ptr),
247 }, .monotonic);
248 }
249
250 const result = task.func(task.contextPointer());
251 const cancel_acknowledged = switch (thread.status.load(.monotonic).cancelation) {
252 .none, .canceling => false,
253 .canceled => true,
254 .parked => unreachable,
255 .blocked => unreachable,
256 .blocked_windows_dns => unreachable,
257 .blocked_canceling => unreachable,
258 };
259 if (result) {
260 assert(!cancel_acknowledged); // group task acknowledged cancelation but did not return `error.Canceled`
261 } else |err| switch (err) {
262 error.Canceled => assert(cancel_acknowledged), // group task returned `error.Canceled` but was never canceled
263 }
264
265 thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);
266 const old_status = group.status().fetchSub(.{
267 .num_running = 1,
268 .have_awaiter = false,
269 .canceled = false,
270 }, .acq_rel); // acquire `group.awaiter()`, release task results
271 assert(old_status.num_running > 0);
272 if (old_status.have_awaiter and old_status.num_running == 1) {
273 const to_signal = group.awaiter().*;
274 // `awaiter` should only be modified by us. For another thread to see `num_running`
275 // drop to 0 after this point would indicate that another task started up, meaning
276 // `async`/`cancel` was racing with awaited group completion.
277 group.awaiter().* = undefined;
278 _ = to_signal.fetchAdd(1, .release); // release results
279 Thread.futexWake(&to_signal.raw, 1);
280 }
281
282 // Task completed. Self-destruct sequence initiated.
283 task.destroy(t.allocator);
178 }284 }
285 };
179286
180 switch (@cmpxchgStrong(287 /// Assumes the caller has already atomically updated the group status to indicate cancelation,
181 CancelStatus,288 /// and notifies any already-running threads of this cancelation.
182 &closure.cancel_status,289 fn cancelThreads(g: Group, t: *Threaded) bool {
183 .requested,290 var any_blocked = false;
184 .acknowledged,291 var it = t.worker_threads.load(.acquire); // acquire `Thread` values
185 .acq_rel,292 while (it) |thread| : (it = thread.next) {
186 .acquire,293 // This non-mutating RMW exists for ordering reasons: see comment in `Group.Task.start` for reasons.
187 ) orelse return error.Canceled) {294 _ = thread.status.fetchOr(.{ .cancelation = @enumFromInt(0), .awaitable = .null }, .release);
188 .requested => unreachable,295 if (thread.cancelAwaitable(.fromGroup(g.ptr))) any_blocked = true;
189 .acknowledged => unreachable,
190 .none, _ => {},
191 }296 }
297 return any_blocked;
192 }298 }
193299
194 fn beginSyscall(thread: *Thread) error{Canceled}!void {300 /// Uses `Thread.signalCanceledSyscall` to signal any threads which are still blocked in a
195 const closure = thread.current_closure orelse return;301 /// syscall for this group and have not observed a cancelation request yet. Returns `true` if
196302 /// more signals may be necessary, in which case the caller must call this again after a delay.
197 switch (thread.cancel_protection) {303 fn signalAllCanceledSyscalls(g: Group, t: *Threaded) bool {
198 .unblocked => {},304 var any_signaled = false;
199 .blocked => return,305 var it = t.worker_threads.load(.acquire); // acquire `Thread` values
306 while (it) |thread| : (it = thread.next) {
307 if (thread.signalCanceledSyscall(t, .fromGroup(g.ptr))) any_signaled = true;
200 }308 }
309 return any_signaled;
310 }
201311
202 switch (@cmpxchgStrong(312 /// The caller has canceled `g`. Inform any threads working on that group of the cancelation if
203 CancelStatus,313 /// necessary, and wait for `g` to finish (indicated by `num_completed` being incremented from 0
204 &closure.cancel_status,314 /// to 1), while sending regular signals to threads if necessary for them to unblock from any
205 .none,315 /// cancelable syscalls.
206 .fromSignaleeId(thread.signal_id),316 ///
207 .acq_rel,317 /// `skip_signals` means it is already known that no threads are currently working on the group
208 .acquire,318 /// so no notifications or signals are necessary.
209 ) orelse return) {319 fn waitForCancelWithSignaling(
210 .none => unreachable,320 g: Group,
211 .requested => {321 t: *Threaded,
212 @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release);322 num_completed: *std.atomic.Value(u32),
213 return error.Canceled;323 skip_signals: bool,
214 },324 ) void {
215 .acknowledged => return,325 var need_signal: bool = !skip_signals and g.cancelThreads(t);
216 _ => unreachable,326 var timeout_ns: u64 = 1 << 10;
327 while (true) {
328 need_signal = need_signal and g.signalAllCanceledSyscalls(t);
329 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
330 switch (num_completed.load(.acquire)) { // acquire task results
331 0 => {},
332 1 => break,
333 else => unreachable,
334 }
335 timeout_ns <<|= 1;
217 }336 }
218 }337 }
338};
219339
220 fn endSyscall(thread: *Thread) void {340/// Trailing data:
221 const closure = thread.current_closure orelse return;341/// 1. context
342/// 2. result
343const Future = struct {
344 runnable: Runnable,
345 func: *const fn (context: *const anyopaque, result: *anyopaque) void,
346 status: std.atomic.Value(Status),
347 /// On completion, increment this `u32` and do a futex wake on it.
348 awaiter: *std.atomic.Value(u32),
349 context_alignment: Alignment,
350 result_offset: usize,
351 alloc_len: usize,
222352
223 switch (thread.cancel_protection) {353 const Status = packed struct(usize) {
224 .unblocked => {},354 /// The values of this enum are chosen so that await/cancel can just OR with 0b01 and 0b11
225 .blocked => return,355 /// respectively. That *does* clobber `.done`, but that's actually fine, because if the tag
226 }356 /// is `.done` then only the awaiter is referencing this `Future` anyway.
357 tag: enum(u2) {
358 /// The future is queued or running (depending on whether `thread` is set).
359 pending = 0b00,
360 /// Like `pending`, but the future is being awaited. `Future.awaiter` is populated.
361 pending_awaited = 0b01,
362 /// Like `pending`, but the future is being canceled. `Future.awaiter` is populated.
363 pending_canceled = 0b11,
364 /// The future has already completed. `thread` is `.null`, unless the future terminated
365 /// with an acknowledged cancel request, in which case `thread` is `.all_ones`.
366 done = 0b10,
367 },
368 /// When the future begins execution, this is atomically updated from `null` to the thread running the
369 /// `Future`, so that cancelation knows which thread to cancel.
370 thread: Thread.PackedPtr,
371 };
372
373 /// `Future.runnable.node` is `undefined` in the created `Future`.
374 fn create(
375 gpa: Allocator,
376 result_len: usize,
377 result_alignment: Alignment,
378 context: []const u8,
379 context_alignment: Alignment,
380 func: *const fn (context: *const anyopaque, result: *anyopaque) void,
381 ) Allocator.Error!*Future {
382 const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(Future);
383 const worst_case_context_offset = context_alignment.forward(@sizeOf(Future) + max_context_misalignment);
384 const worst_case_result_offset = result_alignment.forward(worst_case_context_offset + context.len);
385 const alloc_len = worst_case_result_offset + result_len;
386
387 const future: *Future = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(Future), alloc_len)));
388 errdefer comptime unreachable;
227389
228 _ = @cmpxchgStrong(390 const actual_context_addr = context_alignment.forward(@intFromPtr(future) + @sizeOf(Future));
229 CancelStatus,391 const actual_result_addr = result_alignment.forward(actual_context_addr + context.len);
230 &closure.cancel_status,392 const actual_result_offset = actual_result_addr - @intFromPtr(future);
231 .fromSignaleeId(thread.signal_id),393 future.* = .{
232 .none,394 .runnable = .{
233 .acq_rel,395 .node = undefined,
234 .acquire,396 .startFn = &start,
235 ) orelse return;397 },
398 .func = func,
399 .status = .init(.{
400 .tag = .pending,
401 .thread = .null,
402 }),
403 .awaiter = undefined,
404 .context_alignment = context_alignment,
405 .result_offset = actual_result_offset,
406 .alloc_len = alloc_len,
407 };
408 @memcpy(future.contextPointer()[0..context.len], context);
409 return future;
236 }410 }
237411
238 fn endSyscallErrnoBug(thread: *Thread, err: posix.E) Io.UnexpectedError {412 fn destroy(future: *Future, gpa: Allocator) void {
239 @branchHint(.cold);413 const base: [*]align(@alignOf(Future)) u8 = @ptrCast(future);
240 thread.endSyscall();414 gpa.free(base[0..future.alloc_len]);
241 return errnoBug(err);
242 }415 }
243416
244 fn endSyscallUnexpectedErrno(thread: *Thread, err: posix.E) Io.UnexpectedError {417 fn resultPointer(future: *Future) [*]u8 {
245 @branchHint(.cold);418 const base: [*]u8 = @ptrCast(future);
246 thread.endSyscall();419 return base + future.result_offset;
247 return posix.unexpectedErrno(err);
248 }420 }
249421
250 /// inline to make error return traces slightly shallower.422 fn contextPointer(future: *Future) [*]u8 {
251 inline fn endSyscallError(thread: *Thread, err: anytype) @TypeOf(err) {423 const base: [*]u8 = @ptrCast(future);
252 thread.endSyscall();424 const context_offset = future.context_alignment.forward(@intFromPtr(future) + @sizeOf(Future)) - @intFromPtr(future);
253 return err;425 return base + context_offset;
426 }
427
428 fn start(r: *Runnable, thread: *Thread, t: *Threaded) void {
429 _ = t;
430 const future: *Future = @fieldParentPtr("runnable", r);
431
432 thread.status.store(.{
433 .cancelation = .none,
434 .awaitable = .fromFuture(future),
435 }, .monotonic);
436 {
437 const old_status = future.status.fetchOr(.{
438 .tag = .pending,
439 .thread = .pack(thread),
440 }, .release);
441 assert(old_status.thread == .null);
442 switch (old_status.tag) {
443 .pending, .pending_awaited => {},
444 .pending_canceled => thread.status.store(.{
445 .cancelation = .canceling,
446 .awaitable = .fromFuture(future),
447 }, .monotonic),
448 .done => unreachable,
449 }
450 }
451
452 future.func(future.contextPointer(), future.resultPointer());
453
454 const had_acknowledged_cancel = switch (thread.status.load(.monotonic).cancelation) {
455 .none, .canceling => false,
456 .canceled => true,
457 .parked => unreachable,
458 .blocked => unreachable,
459 .blocked_windows_dns => unreachable,
460 .blocked_canceling => unreachable,
461 };
462 thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);
463 const old_status = future.status.swap(.{
464 .tag = .done,
465 .thread = if (had_acknowledged_cancel) .all_ones else .null,
466 }, .acq_rel); // acquire `future.awaiter`, release results
467 switch (old_status.tag) {
468 .pending => {},
469 .pending_awaited, .pending_canceled => {
470 const to_signal = future.awaiter;
471 _ = to_signal.fetchAdd(1, .release); // release results
472 Thread.futexWake(&to_signal.raw, 1);
473 },
474 .done => unreachable,
475 }
476 }
477
478 /// The caller has canceled `future`. `thread` is the thread currently running that future.
479 /// Inform `thread` of the cancelation if necessary, and wait for `future` to finish (indicated
480 /// by `num_completed` being incremented from 0 to 1), while sending regular signals to `thread`
481 /// if necessary for it to unblock from a cancelable syscall.
482 fn waitForCancelWithSignaling(
483 future: *Future,
484 t: *Threaded,
485 num_completed: *std.atomic.Value(u32),
486 thread: ?*Thread,
487 ) void {
488 var need_signal: bool = thread != null and thread.?.cancelAwaitable(.fromFuture(future));
489 var timeout_ns: u64 = 1 << 10;
490 while (true) {
491 need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));
492 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
493 switch (num_completed.load(.acquire)) { // acquire task results
494 0 => {},
495 1 => break,
496 else => unreachable,
497 }
498 timeout_ns <<|= 1;
499 }
500 }
501};
502
503/// A sequence of (ptr_bit_width - 3) bits which uniquely identifies a group or future. The bits are
504/// the MSBs of the `*Io.Group` or `*Future`. These things do not necessarily have 3 zero bits at
505/// the end (they are pointer-aligned, so on 32-bit targets only have 2), but because they both have
506/// a *size* of at least 8 bytes, no two groups/futures in memory at the same time will have the
507/// same value for all of these bits. In other words, given a group/future pointer, the next group
508/// or future must be at least 8 bytes later, so its address will have a different value for one of
509/// the top (ptr_bit_width - 3) bits.
510const AwaitableId = enum(@Int(.unsigned, @bitSizeOf(usize) - 3)) {
511 comptime {
512 assert(@sizeOf(Future) >= 8);
513 assert(@sizeOf(Io.Group) >= 8);
514 }
515 null = 0,
516 all_ones = std.math.maxInt(@Int(.unsigned, @bitSizeOf(usize) - 3)),
517 _,
518 const Split = packed struct(usize) { low: u3, high: AwaitableId };
519 fn fromGroup(g: *Io.Group) AwaitableId {
520 const split: Split = @bitCast(@intFromPtr(g));
521 return split.high;
254 }522 }
523 fn fromFuture(f: *Future) AwaitableId {
524 const split: Split = @bitCast(@intFromPtr(f));
525 return split.high;
526 }
527};
528
529const Thread = struct {
530 next: ?*Thread,
531
532 id: std.Thread.Id,
533 handle: Handle,
534
535 status: std.atomic.Value(Status),
536
537 cancel_protection: Io.CancelProtection,
538 /// Always released when `Status.cancelation` is set to `.parked`.
539 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
540
541 const Handle = Handle: {
542 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
543 if (builtin.target.os.tag == .windows) break :Handle windows.HANDLE;
544 break :Handle void;
545 };
546
547 const Status = packed struct(usize) {
548 /// The specific values of these enum fields are chosen to simplify the implementation of
549 /// the transformations we need to apply to this state.
550 cancelation: enum(u3) {
551 /// The thread has not yet been canceled, and is not in a cancelable operation.
552 /// To request cancelation, just set the status to `.canceling`.
553 none = 0b000,
554
555 /// The thread is parked in a cancelable futex wait or sleep.
556 /// Only applicable if `use_parking_futex` or `use_parking_sleep`.
557 /// To request cancelation, set the status to `.canceling` and unpark the thread.
558 /// To unpark for another reason (futex wake), set the status to `.none` and unpark the thread.
559 parked = 0b001,
560
561 /// The thread is blocked in a cancelable system call.
562 /// To request cancelation, set the status to `.blocked_canceling` and repeatedly interrupt the system call until the status changes.
563 blocked = 0b011,
564
565 /// Windows-only: the thread is blocked in a call to `GetAddrInfoExW`.
566 /// To request cancelation, set the status to `.canceling` and call `GetAddrInfoExCancel`.
567 blocked_windows_dns = 0b010,
568
569 /// The thread has an outstanding cancelation request but is not in a cancelable operation.
570 /// When it acknowledges the cancelation, it will set the status to `.canceled`.
571 canceling = 0b110,
572
573 /// The thread has received and acknowledged a cancelation request.
574 /// If `recancel` is called, the status will revert to `.canceling`, but otherwise, the status
575 /// will not change for the remainder of this task's execution.
576 canceled = 0b111,
577
578 /// The thread is blocked in a cancelable system call, and is being canceled. The thread which triggered the cancelation will send signals to this thread
579 /// until its status changes.
580 blocked_canceling = 0b101,
581 },
582
583 /// We cannot turn this value back into a pointer. Instead, it exists so that a task can be
584 /// canceled by a cmpxchg on thread status: if it is running the task we want to cancel,
585 /// then update the `cancelation` field.
586 awaitable: AwaitableId,
587 };
255588
256 fn currentSignalId() SignaleeId {589 const SignaleeId = if (std.Thread.use_pthreads) std.c.pthread_t else std.Thread.Id;
257 return if (std.Thread.use_pthreads) std.c.pthread_self() else std.Thread.getCurrentId();590
591 threadlocal var current: ?*Thread = null;
592
593 /// The thread is neither in a syscall nor entering one, but we want to check for cancelation
594 /// anyway. If there is a pending cancel request, acknowledge it and return `error.Canceled`.
595 fn checkCancel() Io.Cancelable!void {
596 const thread = Thread.current orelse return;
597 switch (thread.cancel_protection) {
598 .blocked => return,
599 .unblocked => {},
600 }
601 // Here, unlike `Syscall.checkCancel`, it's not particularly likely that we're canceled, so
602 // it seems preferable to do a cheap atomic load and, in the unlikely case, a separate store
603 // to acknowledge. Besides, the state transitions we need here can't be done with one atomic
604 // OR/AND/XOR on `Status.cancelation`, so we don't actually have any other option.
605 const status = thread.status.load(.monotonic);
606 switch (status.cancelation) {
607 .parked => unreachable,
608 .blocked => unreachable,
609 .blocked_windows_dns => unreachable,
610 .blocked_canceling => unreachable,
611 .none, .canceled => {},
612 .canceling => {
613 thread.status.store(.{
614 .cancelation = .canceled,
615 .awaitable = status.awaitable,
616 }, .monotonic);
617 return error.Canceled;
618 },
619 }
258 }620 }
259621
260 fn futexWaitUncancelable(ptr: *const u32, expect: u32) void {622 fn futexWaitUncancelable(ptr: *const u32, expect: u32, timeout_ns: ?u64) void {
261 return Thread.futexWaitTimed(null, ptr, expect, null) catch unreachable;623 return Thread.futexWaitInner(ptr, expect, true, timeout_ns) catch unreachable;
262 }624 }
263625
264 fn futexWait(thread: *Thread, ptr: *const u32, expect: u32) Io.Cancelable!void {626 fn futexWait(ptr: *const u32, expect: u32, timeout_ns: ?u64) Io.Cancelable!void {
265 return Thread.futexWaitTimed(thread, ptr, expect, null) catch |err| switch (err) {627 return Thread.futexWaitInner(ptr, expect, false, timeout_ns);
266 error.Canceled => return error.Canceled,
267 error.Timeout => unreachable,
268 };
269 }628 }
270629
271 fn futexWaitTimed(thread: ?*Thread, ptr: *const u32, expect: u32, timeout_ns: ?u64) Io.Cancelable!void {630 fn futexWaitInner(ptr: *const u32, expect: u32, uncancelable: bool, timeout_ns: ?u64) Io.Cancelable!void {
272 @branchHint(.cold);631 @branchHint(.cold);
273632
274 if (builtin.single_threaded) unreachable; // nobody would ever wake us633 if (builtin.single_threaded) unreachable; // nobody would ever wake us
275634
276 if (builtin.cpu.arch.isWasm()) {635 if (use_parking_futex) {
636 return parking_futex.wait(
637 ptr,
638 expect,
639 uncancelable,
640 if (timeout_ns) |ns| .{ .duration = .{
641 .raw = .fromNanoseconds(ns),
642 .clock = .boot,
643 } } else .none,
644 );
645 } else if (builtin.cpu.arch.isWasm()) {
277 comptime assert(builtin.cpu.has(.wasm, .atomics));646 comptime assert(builtin.cpu.has(.wasm, .atomics));
278 if (thread) |t| try t.checkCancel();647 // TODO implement cancelation for WASM futex waits by signaling the futex
648 if (!uncancelable) try Thread.checkCancel();
279 const to: i64 = if (timeout_ns) |ns| ns else -1;649 const to: i64 = if (timeout_ns) |ns| ns else -1;
280 const signed_expect: i32 = @bitCast(expect);650 const signed_expect: i32 = @bitCast(expect);
281 const result = asm volatile (651 const result = asm volatile (
...@@ -303,9 +673,9 @@ const Thread = struct {...@@ -303,9 +673,9 @@ const Thread = struct {
303 ts_buffer = timestampToPosix(ns);673 ts_buffer = timestampToPosix(ns);
304 break :ts &ts_buffer;674 break :ts &ts_buffer;
305 } else null;675 } else null;
306 if (thread) |t| try t.beginSyscall();676 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
307 const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, ts);677 const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, ts);
308 if (thread) |t| t.endSyscall();678 syscall.finish();
309 switch (linux.errno(rc)) {679 switch (linux.errno(rc)) {
310 .SUCCESS => {}, // notified by `wake()`680 .SUCCESS => {}, // notified by `wake()`
311 .INTR => {}, // caller's responsibility to retry681 .INTR => {}, // caller's responsibility to retry
...@@ -322,7 +692,7 @@ const Thread = struct {...@@ -322,7 +692,7 @@ const Thread = struct {
322 .op = .COMPARE_AND_WAIT,692 .op = .COMPARE_AND_WAIT,
323 .NO_ERRNO = true,693 .NO_ERRNO = true,
324 };694 };
325 if (thread) |t| try t.beginSyscall();695 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
326 const status = switch (darwin_supports_ulock_wait2) {696 const status = switch (darwin_supports_ulock_wait2) {
327 true => c.__ulock_wait2(flags, ptr, expect, ns: {697 true => c.__ulock_wait2(flags, ptr, expect, ns: {
328 const ns = timeout_ns orelse break :ns 0;698 const ns = timeout_ns orelse break :ns 0;
...@@ -336,7 +706,7 @@ const Thread = struct {...@@ -336,7 +706,7 @@ const Thread = struct {
336 break :us us;706 break :us us;
337 }),707 }),
338 };708 };
339 if (thread) |t| t.endSyscall();709 syscall.finish();
340 if (status >= 0) return;710 if (status >= 0) return;
341 switch (@as(c.E, @enumFromInt(-status))) {711 switch (@as(c.E, @enumFromInt(-status))) {
342 .INTR => {}, // spurious wake712 .INTR => {}, // spurious wake
...@@ -348,24 +718,6 @@ const Thread = struct {...@@ -348,24 +718,6 @@ const Thread = struct {
348 else => recoverableOsBugDetected(),718 else => recoverableOsBugDetected(),
349 }719 }
350 },720 },
351 .windows => {
352 var timeout_value: windows.LARGE_INTEGER = undefined;
353 var timeout_ptr: ?*const windows.LARGE_INTEGER = null;
354 // NTDLL functions work with time in units of 100 nanoseconds.
355 // Positive values are absolute deadlines while negative values are relative durations.
356 if (timeout_ns) |delay| {
357 timeout_value = @as(windows.LARGE_INTEGER, @intCast(delay / 100));
358 timeout_value = -timeout_value;
359 timeout_ptr = &timeout_value;
360 }
361 if (thread) |t| try t.checkCancel();
362 switch (windows.ntdll.RtlWaitOnAddress(ptr, &expect, @sizeOf(@TypeOf(expect)), timeout_ptr)) {
363 .SUCCESS => {},
364 .CANCELLED => {},
365 .TIMEOUT => {}, // timeout
366 else => recoverableOsBugDetected(),
367 }
368 },
369 .freebsd => {721 .freebsd => {
370 const flags = @intFromEnum(std.c.UMTX_OP.WAIT_UINT_PRIVATE);722 const flags = @intFromEnum(std.c.UMTX_OP.WAIT_UINT_PRIVATE);
371 var tm_size: usize = 0;723 var tm_size: usize = 0;
...@@ -378,9 +730,9 @@ const Thread = struct {...@@ -378,9 +730,9 @@ const Thread = struct {
378 tm.clockid = .MONOTONIC;730 tm.clockid = .MONOTONIC;
379 tm.timeout = timestampToPosix(ns);731 tm.timeout = timestampToPosix(ns);
380 }732 }
381 if (thread) |t| try t.beginSyscall();733 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
382 const rc = std.c._umtx_op(@intFromPtr(ptr), flags, @as(c_ulong, expect), tm_size, @intFromPtr(tm_ptr));734 const rc = std.c._umtx_op(@intFromPtr(ptr), flags, @as(c_ulong, expect), tm_size, @intFromPtr(tm_ptr));
383 if (thread) |t| t.endSyscall();735 syscall.finish();
384 if (is_debug) switch (posix.errno(rc)) {736 if (is_debug) switch (posix.errno(rc)) {
385 .SUCCESS => {},737 .SUCCESS => {},
386 .FAULT => unreachable, // one of the args points to invalid memory738 .FAULT => unreachable, // one of the args points to invalid memory
...@@ -397,7 +749,7 @@ const Thread = struct {...@@ -397,7 +749,7 @@ const Thread = struct {
397 tm_ptr = &tm;749 tm_ptr = &tm;
398 tm = timestampToPosix(ns);750 tm = timestampToPosix(ns);
399 }751 }
400 if (thread) |t| try t.beginSyscall();752 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
401 const rc = std.c.futex(753 const rc = std.c.futex(
402 ptr,754 ptr,
403 std.c.FUTEX.WAIT | std.c.FUTEX.PRIVATE_FLAG,755 std.c.FUTEX.WAIT | std.c.FUTEX.PRIVATE_FLAG,
...@@ -405,7 +757,7 @@ const Thread = struct {...@@ -405,7 +757,7 @@ const Thread = struct {
405 tm_ptr,757 tm_ptr,
406 null, // uaddr2 is ignored758 null, // uaddr2 is ignored
407 );759 );
408 if (thread) |t| t.endSyscall();760 syscall.finish();
409 if (is_debug) switch (posix.errno(rc)) {761 if (is_debug) switch (posix.errno(rc)) {
410 .SUCCESS => {},762 .SUCCESS => {},
411 .NOSYS => unreachable, // constant op known good value763 .NOSYS => unreachable, // constant op known good value
...@@ -424,9 +776,9 @@ const Thread = struct {...@@ -424,9 +776,9 @@ const Thread = struct {
424 } else {776 } else {
425 timeout_us = 0;777 timeout_us = 0;
426 }778 }
427 if (thread) |t| try t.beginSyscall();779 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
428 const rc = std.c.umtx_sleep(@ptrCast(ptr), @bitCast(expect), timeout_us);780 const rc = std.c.umtx_sleep(@ptrCast(ptr), @bitCast(expect), timeout_us);
429 if (thread) |t| t.endSyscall();781 syscall.finish();
430 if (is_debug) switch (std.posix.errno(rc)) {782 if (is_debug) switch (std.posix.errno(rc)) {
431 .SUCCESS => {},783 .SUCCESS => {},
432 .BUSY => {}, // ptr != expect784 .BUSY => {}, // ptr != expect
...@@ -436,14 +788,7 @@ const Thread = struct {...@@ -436,14 +788,7 @@ const Thread = struct {
436 else => unreachable,788 else => unreachable,
437 };789 };
438 },790 },
439 else => if (std.Thread.use_pthreads) {791 else => @compileError("unimplemented: futexWait"),
440 // TODO integrate the following function being called with robust cancelation.
441 return pthreads_futex.wait(ptr, expect, timeout_ns) catch |err| switch (err) {
442 error.Timeout => {},
443 };
444 } else {
445 @compileError("unimplemented: futexWait");
446 },
447 }792 }
448 }793 }
449794
...@@ -453,7 +798,9 @@ const Thread = struct {...@@ -453,7 +798,9 @@ const Thread = struct {
453798
454 if (builtin.single_threaded) return; // nothing to wake up799 if (builtin.single_threaded) return; // nothing to wake up
455800
456 if (builtin.cpu.arch.isWasm()) {801 if (use_parking_futex) {
802 return parking_futex.wake(ptr, max_waiters);
803 } else if (builtin.cpu.arch.isWasm()) {
457 comptime assert(builtin.cpu.has(.wasm, .atomics));804 comptime assert(builtin.cpu.has(.wasm, .atomics));
458 const woken_count = asm volatile (805 const woken_count = asm volatile (
459 \\local.get %[ptr]806 \\local.get %[ptr]
...@@ -498,12 +845,6 @@ const Thread = struct {...@@ -498,12 +845,6 @@ const Thread = struct {
498 }845 }
499 }846 }
500 },847 },
501 .windows => {
502 switch (max_waiters) {
503 1 => windows.ntdll.RtlWakeAddressSingle(ptr),
504 else => windows.ntdll.RtlWakeAddressAll(ptr),
505 }
506 },
507 .freebsd => {848 .freebsd => {
508 const rc = std.c._umtx_op(849 const rc = std.c._umtx_op(
509 @intFromPtr(ptr),850 @intFromPtr(ptr),
...@@ -536,130 +877,239 @@ const Thread = struct {...@@ -536,130 +877,239 @@ const Thread = struct {
536 @min(max_waiters, std.math.maxInt(c_int)),877 @min(max_waiters, std.math.maxInt(c_int)),
537 );878 );
538 },879 },
539 else => if (std.Thread.use_pthreads) {880 else => @compileError("unimplemented: futexWake"),
540 return pthreads_futex.wake(ptr, max_waiters);
541 } else {
542 @compileError("unimplemented: futexWake");
543 },
544 }881 }
545 }882 }
546};
547
548const max_iovecs_len = 8;
549const splat_buffer_size = 64;
550883
551comptime {884 /// Cancels `thread` if it is working on `awaitable`.
552 if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);885 ///
553}886 /// It is possible that `thread` gets canceled by this function, but is blocked in a syscall. In
887 /// that case, the thread may need to be sent a signal to interrupt the call. This function will
888 /// return `true` to indicate this, in which case the caller must call `signalCanceledSyscall`.
889 fn cancelAwaitable(thread: *Thread, awaitable: AwaitableId) bool {
890 var status = thread.status.load(.monotonic);
891 while (true) {
892 if (status.awaitable != awaitable) return false; // thread is working on something else
893 status = switch (status.cancelation) {
894 .none => thread.status.cmpxchgWeak(
895 .{ .cancelation = .none, .awaitable = awaitable },
896 .{ .cancelation = .canceling, .awaitable = awaitable },
897 .monotonic,
898 .monotonic,
899 ) orelse return false,
900
901 .parked => thread.status.cmpxchgWeak(
902 .{ .cancelation = .parked, .awaitable = awaitable },
903 .{ .cancelation = .canceling, .awaitable = awaitable },
904 .acquire, // acquire `thread.futex_waiter`
905 .monotonic,
906 ) orelse {
907 if (!use_parking_futex and !use_parking_sleep) unreachable;
908 if (thread.futex_waiter) |futex_waiter| {
909 parking_futex.removeCanceledWaiter(futex_waiter);
910 }
911 unpark(&.{thread.id}, null);
912 return false;
913 },
554914
555const CancelStatus = enum(usize) {915 .blocked => thread.status.cmpxchgWeak(
556 /// Cancellation has neither been requested, nor checked. The async916 .{ .cancelation = .blocked, .awaitable = awaitable },
557 /// operation will check status before entering a blocking syscall.917 .{ .cancelation = .blocked_canceling, .awaitable = awaitable },
558 /// This is also the status used for uninteruptible tasks.918 .monotonic,
559 none = 0,919 .monotonic,
560 /// Cancellation has been requested and the status will be checked before920 ) orelse return true,
561 /// entering a blocking syscall.921
562 requested = std.math.maxInt(usize) - 1,922 .blocked_windows_dns => thread.status.cmpxchgWeak(
563 /// Cancellation has been acknowledged and is in progress. Signals should923 .{ .cancelation = .blocked_windows_dns, .awaitable = awaitable },
564 /// not be sent.924 .{ .cancelation = .canceling, .awaitable = awaitable },
565 acknowledged = std.math.maxInt(usize),925 .monotonic,
566 /// Stores a `Thread.SignaleeId` and indicates that sending a signal to this thread926 .monotonic,
567 /// is needed in order to cancel. This state is set before going into927 ) orelse {
568 /// a blocking operation that needs to get unblocked via signal.928 if (builtin.target.os.tag != .windows) unreachable;
569 _,929 if (true) {
930 // TODO: cancel Windows DNS queries. This code path is currently impossible
931 // as `netLookupFallible` doesn't actually use `.blocked_windows_dns` yet.
932 unreachable;
933 }
934 return false;
935 },
570936
571 const Unpacked = union(enum) {937 .canceling, .canceled => {
572 none,938 // This can happen when the task start raced with the cancelation, so the thread
573 requested,939 // saw the cancelation on the future/group *and* we are trying to signal the
574 acknowledged,940 // thread here.
575 signal_id: Thread.SignaleeId,941 return false;
576 };942 },
577
578 fn unpack(cs: CancelStatus) Unpacked {
579 return switch (cs) {
580 .none => .none,
581 .requested => .requested,
582 .acknowledged => .acknowledged,
583 _ => |signal_id| .{
584 .signal_id = if (std.Thread.use_pthreads)
585 @ptrFromInt(@intFromEnum(signal_id))
586 else
587 @truncate(@intFromEnum(signal_id)),
588 },
589 };
590 }
591943
592 fn fromSignaleeId(signal_id: Thread.SignaleeId) CancelStatus {944 .blocked_canceling => unreachable,
593 return if (std.Thread.use_pthreads)945 };
594 @enumFromInt(@intFromPtr(signal_id))946 }
595 else
596 @enumFromInt(signal_id);
597 }947 }
598};
599
600const Closure = struct {
601 start: Start,
602 node: std.SinglyLinkedList.Node = .{},
603 cancel_status: CancelStatus,
604948
605 const Start = *const fn (*Closure, *Threaded) void;949 /// Sends a signal to `thread` if it is still blocked in a syscall (i.e. has not yet observed
606950 /// the cancelation request from `cancelAwaitable`).
607 fn requestCancel(closure: *Closure, t: *Threaded) void {951 ///
608 var signal_id = switch (@atomicRmw(CancelStatus, &closure.cancel_status, .Xchg, .requested, .monotonic).unpack()) {952 /// Unfortunately, the signal could arrive before the syscall actually starts, so the interrupt
609 .none, .acknowledged, .requested => return,953 /// is missed. To handle this, we may need to send multiple signals. As such, if this function
610 .signal_id => |signal_id| signal_id,954 /// returns `true`, then it should be called again after a short delay to send another signal if
611 };955 /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and
612 // The task will enter a blocking syscall before checking for cancellation again.956 /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and
613 // We can send a signal to interrupt the syscall, but if it arrives before957 /// doubling each call. In practice, it is rare to send more than one signal.
614 // the syscall instruction, it will be missed. Therefore, this code tries958 fn signalCanceledSyscall(thread: *Thread, t: *Threaded, awaitable: AwaitableId) bool {
615 // again until the cancellation request is acknowledged.959 const bad_status: Status = .{ .cancelation = .blocked_canceling, .awaitable = awaitable };
616960 if (thread.status.load(.monotonic) != bad_status) return false;
617 // 1 << 10 ns is about 1 microsecond, approximately syscall overhead.961
618 // 1 << 20 ns is about 1 millisecond.962 // The thread ID and/or handle can be read non-atomically because they never change and were
619 // 1 << 30 ns is about 1 second.963 // released by the store that made `thread` available to us.
620 //964
621 // On a heavily loaded Linux 6.17.5, I observed a maximum of 20965 if (std.Thread.use_pthreads) {
622 // attempts not acknowledged before the timeout (including exponential966 return switch (std.c.pthread_kill(thread.handle, .IO)) {
623 // backoff) was sufficient, despite the heavy load.967 0 => true,
624 const max_attempts = 22;968 else => false,
625969 };
626 for (0..max_attempts) |attempt_index| {970 } else switch (builtin.target.os.tag) {
627 if (std.Thread.use_pthreads) {971 .linux => {
628 if (std.c.pthread_kill(signal_id, .IO) != 0) return;972 const pid: posix.pid_t = pid: {
629 } else if (native_os == .linux) {
630 const pid: posix.pid_t = p: {
631 const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);973 const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);
632 if (cached_pid != .unknown) break :p @intFromEnum(cached_pid);974 if (cached_pid != .unknown) break :pid @intFromEnum(cached_pid);
633 const pid = std.os.linux.getpid();975 const pid = std.os.linux.getpid();
634 @atomicStore(Pid, &t.pid, @enumFromInt(pid), .monotonic);976 @atomicStore(Pid, &t.pid, @enumFromInt(pid), .monotonic);
635 break :p pid;977 break :pid pid;
636 };978 };
637 if (std.os.linux.tgkill(pid, @bitCast(signal_id), .IO) != 0) return;979 return switch (std.os.linux.tgkill(pid, @bitCast(thread.id), .IO)) {
638 } else {980 0 => true,
639 return;981 else => false,
640 }982 };
983 },
984 .windows => {
985 var iosb: windows.IO_STATUS_BLOCK = undefined;
986 return switch (windows.ntdll.NtCancelSynchronousIoFile(thread.handle, null, &iosb)) {
987 .NOT_FOUND => true, // this might mean the operation hasn't started yet
988 .SUCCESS => false, // the OS confirmed that our cancelation worked
989 else => false,
990 };
991 },
992 else => return false,
993 }
994 }
641995
642 if (t.robust_cancel != .enabled) return;996 /// Like a `*Thread`, but 2 bits smaller than a pointer (because the LSBs are always 0 due to
997 /// alignment) so that those two bits can be used in a `packed struct`.
998 const PackedPtr = enum(@Int(.unsigned, @bitSizeOf(usize) - 2)) {
999 null = 0,
1000 all_ones = std.math.maxInt(@Int(.unsigned, @bitSizeOf(usize) - 2)),
1001 _,
6431002
644 var timespec: posix.timespec = .{1003 const Split = packed struct(usize) { low: u2, high: PackedPtr };
645 .sec = 0,1004 fn pack(ptr: *Thread) PackedPtr {
646 .nsec = @as(isize, 1) << @intCast(attempt_index),1005 const split: Split = @bitCast(@intFromPtr(ptr));
647 };1006 assert(split.low == 0);
648 if (native_os == .linux) {1007 return split.high;
649 _ = std.os.linux.clock_nanosleep(posix.CLOCK.MONOTONIC, .{ .ABSTIME = false }, &timespec, &timespec);1008 }
650 } else {1009 fn unpack(ptr: PackedPtr) ?*Thread {
651 _ = posix.system.nanosleep(&timespec, &timespec);1010 const split: Split = .{ .low = 0, .high = ptr };
652 }1011 return @ptrFromInt(@as(usize, @bitCast(split)));
1012 }
1013 };
1014};
6531015
654 switch (@atomicRmw(CancelStatus, &closure.cancel_status, .Xchg, .requested, .monotonic).unpack()) {1016const Syscall = struct {
655 .requested => continue, // Retry needed in case other thread hasn't yet entered the syscall.1017 thread: ?*Thread,
656 .none, .acknowledged => return,1018 /// Marks entry to a syscall region. This should be tightly scoped around the actual syscall
657 .signal_id => |new_signal_id| signal_id = new_signal_id,1019 /// to minimize races. The syscall must be marked as "finished" by `checkCancel`, `finish`,
658 }1020 /// or one of the wrappers of `finish`.
1021 fn start() Io.Cancelable!Syscall {
1022 const thread = Thread.current orelse return .{ .thread = null };
1023 switch (thread.cancel_protection) {
1024 .blocked => return .{ .thread = null },
1025 .unblocked => {},
659 }1026 }
1027 switch (thread.status.fetchOr(.{
1028 .cancelation = @enumFromInt(0b011),
1029 .awaitable = .null,
1030 }, .monotonic).cancelation) {
1031 .parked => unreachable,
1032 .blocked => unreachable,
1033 .blocked_windows_dns => unreachable,
1034 .blocked_canceling => unreachable,
1035 .none => return .{ .thread = thread }, // new status is `.blocked`
1036 .canceling => return error.Canceled, // new status is `.canceled`
1037 .canceled => return .{ .thread = null }, // new status is `.canceled` (unchanged)
1038 }
1039 }
1040 /// Checks whether this syscall has been canceled. This should be called when a syscall is
1041 /// interrupted through a mechanism which may indicate cancelation, or may be spurious. If
1042 /// the syscall was canceled, it is finished and `error.Canceled` is returned. Otherwise,
1043 /// the syscall is not marked finished, and the caller should retry.
1044 fn checkCancel(s: Syscall) Io.Cancelable!void {
1045 const thread = s.thread orelse return;
1046 switch (thread.status.fetchOr(.{
1047 .cancelation = @enumFromInt(0b010),
1048 .awaitable = .null,
1049 }, .monotonic).cancelation) {
1050 .none => unreachable,
1051 .parked => unreachable,
1052 .blocked_windows_dns => unreachable,
1053 .canceling => unreachable,
1054 .canceled => unreachable,
1055 .blocked => {}, // new status is `.blocked` (unchanged)
1056 .blocked_canceling => return error.Canceled, // new status is `.canceled`
1057 }
1058 }
1059 /// Marks this syscall as finished.
1060 fn finish(s: Syscall) void {
1061 const thread = s.thread orelse return;
1062 switch (thread.status.fetchXor(.{
1063 .cancelation = @enumFromInt(0b011),
1064 .awaitable = .null,
1065 }, .monotonic).cancelation) {
1066 .none => unreachable,
1067 .parked => unreachable,
1068 .blocked_windows_dns => unreachable,
1069 .canceling => unreachable,
1070 .canceled => unreachable,
1071 .blocked => {}, // new status is `.none`
1072 .blocked_canceling => {}, // new status is `.canceling`
1073 }
1074 }
1075 /// Convenience wrapper which calls `finish`, then returns `err`.
1076 fn fail(s: Syscall, err: anytype) @TypeOf(err) {
1077 s.finish();
1078 return err;
1079 }
1080 /// Convenience wrapper which calls `finish`, then calls `Threaded.errnoBug`.
1081 fn errnoBug(s: Syscall, err: posix.E) Io.UnexpectedError {
1082 @branchHint(.cold);
1083 s.finish();
1084 return Threaded.errnoBug(err);
1085 }
1086 /// Convenience wrapper which calls `finish`, then calls `posix.unexpectedErrno`.
1087 fn unexpectedErrno(s: Syscall, err: posix.E) Io.UnexpectedError {
1088 @branchHint(.cold);
1089 s.finish();
1090 return posix.unexpectedErrno(err);
1091 }
1092 /// Convenience wrapper which calls `finish`, then calls `windows.statusBug`.
1093 fn ntstatusBug(s: Syscall, status: windows.NTSTATUS) Io.UnexpectedError {
1094 @branchHint(.cold);
1095 s.finish();
1096 return windows.statusBug(status);
1097 }
1098 /// Convenience wrapper which calls `finish`, then calls `windows.unexpectedStatus`.
1099 fn unexpectedNtstatus(s: Syscall, status: windows.NTSTATUS) Io.UnexpectedError {
1100 @branchHint(.cold);
1101 s.finish();
1102 return windows.unexpectedStatus(status);
660 }1103 }
661};1104};
6621105
1106const max_iovecs_len = 8;
1107const splat_buffer_size = 64;
1108
1109comptime {
1110 if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);
1111}
1112
663pub const InitOptions = struct {1113pub const InitOptions = struct {
664 /// Affects how many bytes are memory-mapped for threads.1114 /// Affects how many bytes are memory-mapped for threads.
665 stack_size: usize = std.Thread.SpawnConfig.default_stack_size,1115 stack_size: usize = std.Thread.SpawnConfig.default_stack_size,
...@@ -681,17 +1131,6 @@ pub const InitOptions = struct {...@@ -681,17 +1131,6 @@ pub const InitOptions = struct {
681 /// concurrent tasks. After this number, calls to `Io.concurrent` return1131 /// concurrent tasks. After this number, calls to `Io.concurrent` return
682 /// `error.ConcurrencyUnavailable`.1132 /// `error.ConcurrencyUnavailable`.
683 concurrent_limit: Io.Limit = .unlimited,1133 concurrent_limit: Io.Limit = .unlimited,
684 /// When a cancel request is made, blocking syscalls can be unblocked by
685 /// issuing a signal. However, if the signal arrives after the check and before
686 /// the syscall instruction, it is missed.
687 ///
688 /// This option solves the race condition by retrying the signal delivery
689 /// until it is acknowledged, with an exponential backoff.
690 ///
691 /// Unfortunately, trying again until the cancellation request is acknowledged
692 /// has been observed to be relatively slow, and usually strong cancellation
693 /// guarantees are not needed, so this defaults to off.
694 robust_cancel: RobustCancel = .disabled,
695 /// Affects the following operations:1134 /// Affects the following operations:
696 /// * `processExecutablePath` on OpenBSD and Haiku.1135 /// * `processExecutablePath` on OpenBSD and Haiku.
697 argv0: Argv0 = .{},1136 argv0: Argv0 = .{},
...@@ -727,14 +1166,9 @@ pub fn init(...@@ -727,14 +1166,9 @@ pub fn init(
727 .old_sig_io = undefined,1166 .old_sig_io = undefined,
728 .old_sig_pipe = undefined,1167 .old_sig_pipe = undefined,
729 .have_signal_handler = false,1168 .have_signal_handler = false,
730 .main_thread = .{
731 .signal_id = Thread.currentSignalId(),
732 .current_closure = null,
733 .cancel_protection = .unblocked,
734 },
735 .argv0 = options.argv0,1169 .argv0 = options.argv0,
736 .environ = options.environ,1170 .environ = options.environ,
737 .robust_cancel = options.robust_cancel,1171 .worker_threads = .init(null),
738 };1172 };
7391173
740 if (posix.Sigaction != void) {1174 if (posix.Sigaction != void) {
...@@ -768,14 +1202,9 @@ pub const init_single_threaded: Threaded = .{...@@ -768,14 +1202,9 @@ pub const init_single_threaded: Threaded = .{
768 .old_sig_io = undefined,1202 .old_sig_io = undefined,
769 .old_sig_pipe = undefined,1203 .old_sig_pipe = undefined,
770 .have_signal_handler = false,1204 .have_signal_handler = false,
771 .main_thread = .{
772 .signal_id = undefined,
773 .current_closure = null,
774 .cancel_protection = .unblocked,
775 },
776 .robust_cancel = .disabled,
777 .argv0 = .{},1205 .argv0 = .{},
778 .environ = .{},1206 .environ = .{},
1207 .worker_threads = .init(null),
779};1208};
7801209
781var global_single_threaded_instance: Threaded = .init_single_threaded;1210var global_single_threaded_instance: Threaded = .init_single_threaded;
...@@ -822,22 +1251,70 @@ fn join(t: *Threaded) void {...@@ -822,22 +1251,70 @@ fn join(t: *Threaded) void {
8221251
823fn worker(t: *Threaded) void {1252fn worker(t: *Threaded) void {
824 var thread: Thread = .{1253 var thread: Thread = .{
825 .signal_id = Thread.currentSignalId(),1254 .next = undefined,
826 .current_closure = null,1255 .id = std.Thread.getCurrentId(),
1256 .handle = handle: {
1257 if (std.Thread.use_pthreads) break :handle std.c.pthread_self();
1258 if (builtin.target.os.tag == .windows) break :handle undefined; // populated below
1259 },
1260 .status = .init(.{
1261 .cancelation = .none,
1262 .awaitable = .null,
1263 }),
827 .cancel_protection = .unblocked,1264 .cancel_protection = .unblocked,
1265 .futex_waiter = undefined,
828 };1266 };
829 Thread.current = &thread;1267 Thread.current = &thread;
8301268
1269 if (builtin.target.os.tag == .windows) {
1270 assert(windows.ntdll.NtOpenThread(
1271 &thread.handle,
1272 .{
1273 .SPECIFIC = .{
1274 .THREAD = .{
1275 .TERMINATE = true, // for `NtCancelSynchronousIoFile`
1276 },
1277 },
1278 },
1279 &.{
1280 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
1281 .RootDirectory = null,
1282 .ObjectName = null,
1283 .Attributes = .{},
1284 .SecurityDescriptor = null,
1285 .SecurityQualityOfService = null,
1286 },
1287 &windows.teb().ClientId,
1288 ) == .SUCCESS);
1289 }
1290 defer if (builtin.target.os.tag == .windows) {
1291 windows.CloseHandle(thread.handle);
1292 };
1293
1294 {
1295 var head = t.worker_threads.load(.monotonic);
1296 while (true) {
1297 thread.next = head;
1298 head = t.worker_threads.cmpxchgWeak(
1299 head,
1300 &thread,
1301 .release,
1302 .monotonic,
1303 ) orelse break;
1304 }
1305 }
1306
831 defer t.wait_group.finish();1307 defer t.wait_group.finish();
8321308
833 t.mutex.lock();1309 t.mutex.lock();
834 defer t.mutex.unlock();1310 defer t.mutex.unlock();
8351311
836 while (true) {1312 while (true) {
837 while (t.run_queue.popFirst()) |closure_node| {1313 while (t.run_queue.popFirst()) |runnable_node| {
838 t.mutex.unlock();1314 t.mutex.unlock();
839 const closure: *Closure = @fieldParentPtr("node", closure_node);1315 thread.cancel_protection = .unblocked;
840 closure.start(closure, t);1316 const runnable: *Runnable = @fieldParentPtr("node", runnable_node);
1317 runnable.startFn(runnable, &thread, t);
841 t.mutex.lock();1318 t.mutex.lock();
842 t.busy_count -= 1;1319 t.busy_count -= 1;
843 }1320 }
...@@ -1145,103 +1622,6 @@ const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid...@@ -1145,103 +1622,6 @@ const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid
1145});1622});
1146const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux;1623const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux;
11471624
1148/// Trailing data:
1149/// 1. context
1150/// 2. result
1151const AsyncClosure = struct {
1152 closure: Closure,
1153 func: *const fn (context: *anyopaque, result: *anyopaque) void,
1154 event: Io.Event,
1155 select_condition: ?*Io.Event,
1156 context_alignment: Alignment,
1157 result_offset: usize,
1158 alloc_len: usize,
1159
1160 const done_event: *Io.Event = @ptrFromInt(@alignOf(Io.Event));
1161
1162 fn start(closure: *Closure, t: *Threaded) void {
1163 const ac: *AsyncClosure = @alignCast(@fieldParentPtr("closure", closure));
1164 const current_thread = Thread.getCurrent(t);
1165
1166 current_thread.current_closure = closure;
1167 current_thread.cancel_protection = .unblocked;
1168
1169 ac.func(ac.contextPointer(), ac.resultPointer());
1170
1171 current_thread.current_closure = null;
1172 current_thread.cancel_protection = undefined;
1173
1174 if (@atomicRmw(?*Io.Event, &ac.select_condition, .Xchg, done_event, .release)) |select_event| {
1175 assert(select_event != done_event);
1176 select_event.set(ioBasic(t));
1177 }
1178 ac.event.set(ioBasic(t));
1179 }
1180
1181 fn resultPointer(ac: *AsyncClosure) [*]u8 {
1182 const base: [*]u8 = @ptrCast(ac);
1183 return base + ac.result_offset;
1184 }
1185
1186 fn contextPointer(ac: *AsyncClosure) [*]u8 {
1187 const base: [*]u8 = @ptrCast(ac);
1188 const context_offset = ac.context_alignment.forward(@intFromPtr(ac) + @sizeOf(AsyncClosure)) - @intFromPtr(ac);
1189 return base + context_offset;
1190 }
1191
1192 fn init(
1193 gpa: Allocator,
1194 result_len: usize,
1195 result_alignment: Alignment,
1196 context: []const u8,
1197 context_alignment: Alignment,
1198 func: *const fn (context: *const anyopaque, result: *anyopaque) void,
1199 ) Allocator.Error!*AsyncClosure {
1200 const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(AsyncClosure);
1201 const worst_case_context_offset = context_alignment.forward(@sizeOf(AsyncClosure) + max_context_misalignment);
1202 const worst_case_result_offset = result_alignment.forward(worst_case_context_offset + context.len);
1203 const alloc_len = worst_case_result_offset + result_len;
1204
1205 const ac: *AsyncClosure = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(AsyncClosure), alloc_len)));
1206 errdefer comptime unreachable;
1207
1208 const actual_context_addr = context_alignment.forward(@intFromPtr(ac) + @sizeOf(AsyncClosure));
1209 const actual_result_addr = result_alignment.forward(actual_context_addr + context.len);
1210 const actual_result_offset = actual_result_addr - @intFromPtr(ac);
1211 ac.* = .{
1212 .closure = .{
1213 .cancel_status = .none,
1214 .start = start,
1215 },
1216 .func = func,
1217 .context_alignment = context_alignment,
1218 .result_offset = actual_result_offset,
1219 .alloc_len = alloc_len,
1220 .event = .unset,
1221 .select_condition = null,
1222 };
1223 @memcpy(ac.contextPointer()[0..context.len], context);
1224 return ac;
1225 }
1226
1227 fn waitAndDeinit(ac: *AsyncClosure, t: *Threaded, result: []u8) void {
1228 ac.event.wait(ioBasic(t)) catch |err| switch (err) {
1229 error.Canceled => {
1230 ac.closure.requestCancel(t);
1231 ac.event.waitUncancelable(ioBasic(t));
1232 recancel(t);
1233 },
1234 };
1235 @memcpy(result, ac.resultPointer()[0..result.len]);
1236 ac.deinit(t.allocator);
1237 }
1238
1239 fn deinit(ac: *AsyncClosure, gpa: Allocator) void {
1240 const base: [*]align(@alignOf(AsyncClosure)) u8 = @ptrCast(ac);
1241 gpa.free(base[0..ac.alloc_len]);
1242 }
1243};
1244
1245fn async(1625fn async(
1246 userdata: ?*anyopaque,1626 userdata: ?*anyopaque,
1247 result: []u8,1627 result: []u8,
...@@ -1255,10 +1635,13 @@ fn async(...@@ -1255,10 +1635,13 @@ fn async(
1255 start(context.ptr, result.ptr);1635 start(context.ptr, result.ptr);
1256 return null;1636 return null;
1257 }1637 }
1638
1258 const gpa = t.allocator;1639 const gpa = t.allocator;
1259 const ac = AsyncClosure.init(gpa, result.len, result_alignment, context, context_alignment, start) catch {1640 const future = Future.create(gpa, result.len, result_alignment, context, context_alignment, start) catch |err| switch (err) {
1260 start(context.ptr, result.ptr);1641 error.OutOfMemory => {
1261 return null;1642 start(context.ptr, result.ptr);
1643 return null;
1644 },
1262 };1645 };
12631646
1264 t.mutex.lock();1647 t.mutex.lock();
...@@ -1267,7 +1650,7 @@ fn async(...@@ -1267,7 +1650,7 @@ fn async(
12671650
1268 if (busy_count >= @intFromEnum(t.async_limit)) {1651 if (busy_count >= @intFromEnum(t.async_limit)) {
1269 t.mutex.unlock();1652 t.mutex.unlock();
1270 ac.deinit(gpa);1653 future.destroy(gpa);
1271 start(context.ptr, result.ptr);1654 start(context.ptr, result.ptr);
1272 return null;1655 return null;
1273 }1656 }
...@@ -1281,17 +1664,18 @@ fn async(...@@ -1281,17 +1664,18 @@ fn async(
1281 t.wait_group.finish();1664 t.wait_group.finish();
1282 t.busy_count = busy_count;1665 t.busy_count = busy_count;
1283 t.mutex.unlock();1666 t.mutex.unlock();
1284 ac.deinit(gpa);1667 future.destroy(gpa);
1285 start(context.ptr, result.ptr);1668 start(context.ptr, result.ptr);
1286 return null;1669 return null;
1287 };1670 };
1288 thread.detach();1671 thread.detach();
1289 }1672 }
12901673
1291 t.run_queue.prepend(&ac.closure.node);1674 t.run_queue.prepend(&future.runnable.node);
1675
1292 t.mutex.unlock();1676 t.mutex.unlock();
1293 t.cond.signal();1677 t.cond.signal();
1294 return @ptrCast(ac);1678 return @ptrCast(future);
1295}1679}
12961680
1297fn concurrent(1681fn concurrent(
...@@ -1307,9 +1691,10 @@ fn concurrent(...@@ -1307,9 +1691,10 @@ fn concurrent(
1307 const t: *Threaded = @ptrCast(@alignCast(userdata));1691 const t: *Threaded = @ptrCast(@alignCast(userdata));
13081692
1309 const gpa = t.allocator;1693 const gpa = t.allocator;
1310 const ac = AsyncClosure.init(gpa, result_len, result_alignment, context, context_alignment, start) catch1694 const future = Future.create(gpa, result_len, result_alignment, context, context_alignment, start) catch |err| switch (err) {
1311 return error.ConcurrencyUnavailable;1695 error.OutOfMemory => return error.ConcurrencyUnavailable,
1312 errdefer ac.deinit(gpa);1696 };
1697 errdefer future.destroy(gpa);
13131698
1314 t.mutex.lock();1699 t.mutex.lock();
1315 defer t.mutex.unlock();1700 defer t.mutex.unlock();
...@@ -1329,110 +1714,32 @@ fn concurrent(...@@ -1329,110 +1714,32 @@ fn concurrent(
13291714
1330 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch1715 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch
1331 return error.ConcurrencyUnavailable;1716 return error.ConcurrencyUnavailable;
1717
1332 thread.detach();1718 thread.detach();
1333 }1719 }
13341720
1335 t.run_queue.prepend(&ac.closure.node);1721 t.run_queue.prepend(&future.runnable.node);
1722
1336 t.cond.signal();1723 t.cond.signal();
1337 return @ptrCast(ac);1724 return @ptrCast(future);
1338}1725}
13391726
1340const GroupClosure = struct {
1341 closure: Closure,
1342 group: *Io.Group,
1343 /// Points to sibling `GroupClosure`. Used for walking the group to cancel all.
1344 node: std.SinglyLinkedList.Node,
1345 func: *const fn (*Io.Group, context: *anyopaque) Io.Cancelable!void,
1346 context_alignment: Alignment,
1347 alloc_len: usize,
1348
1349 fn start(closure: *Closure, t: *Threaded) void {
1350 const gc: *GroupClosure = @alignCast(@fieldParentPtr("closure", closure));
1351 const current_thread = Thread.getCurrent(t);
1352 const group = gc.group;
1353 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
1354 const event: *Io.Event = @ptrCast(&group.context);
1355 current_thread.current_closure = closure;
1356 current_thread.cancel_protection = .unblocked;
1357
1358 assertResult(closure, gc.func(group, gc.contextPointer()));
1359
1360 current_thread.current_closure = null;
1361 current_thread.cancel_protection = undefined;
1362
1363 const prev_state = group_state.fetchSub(sync_one_pending, .acq_rel);
1364 assert((prev_state / sync_one_pending) > 0);
1365 if (prev_state == (sync_one_pending | sync_is_waiting)) event.set(ioBasic(t));
1366 }
1367
1368 fn assertResult(closure: *Closure, result: Io.Cancelable!void) void {
1369 if (result) |_| switch (closure.cancel_status.unpack()) {
1370 .none, .requested => {},
1371 .acknowledged => unreachable, // task illegally swallowed error.Canceled
1372 .signal_id => unreachable,
1373 } else |err| switch (err) {
1374 error.Canceled => assert(closure.cancel_status == .acknowledged),
1375 }
1376 }
1377
1378 fn contextPointer(gc: *GroupClosure) [*]u8 {
1379 const base: [*]u8 = @ptrCast(gc);
1380 const context_offset = gc.context_alignment.forward(@intFromPtr(gc) + @sizeOf(GroupClosure)) - @intFromPtr(gc);
1381 return base + context_offset;
1382 }
1383
1384 /// Does not initialize the `node` field.
1385 fn init(
1386 gpa: Allocator,
1387 group: *Io.Group,
1388 context: []const u8,
1389 context_alignment: Alignment,
1390 func: *const fn (*Io.Group, context: *const anyopaque) Io.Cancelable!void,
1391 ) Allocator.Error!*GroupClosure {
1392 const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(GroupClosure);
1393 const worst_case_context_offset = context_alignment.forward(@sizeOf(GroupClosure) + max_context_misalignment);
1394 const alloc_len = worst_case_context_offset + context.len;
1395
1396 const gc: *GroupClosure = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(GroupClosure), alloc_len)));
1397 errdefer comptime unreachable;
1398
1399 gc.* = .{
1400 .closure = .{
1401 .cancel_status = .none,
1402 .start = start,
1403 },
1404 .group = group,
1405 .node = undefined,
1406 .func = func,
1407 .context_alignment = context_alignment,
1408 .alloc_len = alloc_len,
1409 };
1410 @memcpy(gc.contextPointer()[0..context.len], context);
1411 return gc;
1412 }
1413
1414 fn deinit(gc: *GroupClosure, gpa: Allocator) void {
1415 const base: [*]align(@alignOf(GroupClosure)) u8 = @ptrCast(gc);
1416 gpa.free(base[0..gc.alloc_len]);
1417 }
1418
1419 const sync_is_waiting: usize = 1 << 0;
1420 const sync_one_pending: usize = 1 << 1;
1421};
1422
1423fn groupAsync(1727fn groupAsync(
1424 userdata: ?*anyopaque,1728 userdata: ?*anyopaque,
1425 group: *Io.Group,1729 type_erased: *Io.Group,
1426 context: []const u8,1730 context: []const u8,
1427 context_alignment: Alignment,1731 context_alignment: Alignment,
1428 start: *const fn (*Io.Group, context: *const anyopaque) Io.Cancelable!void,1732 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
1429) void {1733) void {
1430 const t: *Threaded = @ptrCast(@alignCast(userdata));1734 const t: *Threaded = @ptrCast(@alignCast(userdata));
1431 if (builtin.single_threaded) return start(group, context.ptr) catch unreachable;1735 const g: Group = .{ .ptr = type_erased };
1736
1737 if (builtin.single_threaded) return groupAsyncEager(start, context.ptr);
14321738
1433 const gpa = t.allocator;1739 const gpa = t.allocator;
1434 const gc = GroupClosure.init(gpa, group, context, context_alignment, start) catch1740 const task = Group.Task.create(gpa, g, context, context_alignment, start) catch |err| switch (err) {
1435 return t.assertGroupResult(start(group, context.ptr));1741 error.OutOfMemory => return groupAsyncEager(start, context.ptr),
1742 };
14361743
1437 t.mutex.lock();1744 t.mutex.lock();
14381745
...@@ -1440,8 +1747,8 @@ fn groupAsync(...@@ -1440,8 +1747,8 @@ fn groupAsync(
14401747
1441 if (busy_count >= @intFromEnum(t.async_limit)) {1748 if (busy_count >= @intFromEnum(t.async_limit)) {
1442 t.mutex.unlock();1749 t.mutex.unlock();
1443 gc.deinit(gpa);1750 task.destroy(gpa);
1444 return t.assertGroupResult(start(group, context.ptr));1751 return groupAsyncEager(start, context.ptr);
1445 }1752 }
14461753
1447 t.busy_count = busy_count + 1;1754 t.busy_count = busy_count + 1;
...@@ -1453,48 +1760,84 @@ fn groupAsync(...@@ -1453,48 +1760,84 @@ fn groupAsync(
1453 t.wait_group.finish();1760 t.wait_group.finish();
1454 t.busy_count = busy_count;1761 t.busy_count = busy_count;
1455 t.mutex.unlock();1762 t.mutex.unlock();
1456 gc.deinit(gpa);1763 task.destroy(gpa);
1457 return t.assertGroupResult(start(group, context.ptr));1764 return groupAsyncEager(start, context.ptr);
1458 };1765 };
1459 thread.detach();1766 thread.detach();
1460 }1767 }
14611768
1462 // Append to the group linked list inside the mutex to make `Io.Group.async` thread-safe.1769 // TODO: if this logic is changed to be lock-free, this `fetchAdd` must be released by the queue
1463 gc.node = .{ .next = @ptrCast(@alignCast(group.token.load(.monotonic))) };1770 // prepend so that the task doesn't finish without observing this and try to decrement the count
1464 group.token.store(&gc.node, .monotonic);1771 // below zero.
14651772 _ = g.status().fetchAdd(.{
1466 t.run_queue.prepend(&gc.closure.node);1773 .num_running = 1,
14671774 .have_awaiter = false,
1468 // This needs to be done before unlocking the mutex to avoid a race with1775 .canceled = false,
1469 // the associated task finishing.1776 }, .monotonic);
1470 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);1777 t.run_queue.prepend(&task.runnable.node);
1471 const prev_state = group_state.fetchAdd(GroupClosure.sync_one_pending, .monotonic);
1472 assert((prev_state / GroupClosure.sync_one_pending) < (std.math.maxInt(usize) / GroupClosure.sync_one_pending));
14731778
1474 t.mutex.unlock();1779 t.mutex.unlock();
1475 t.cond.signal();1780 t.cond.signal();
1476}1781}
1782fn groupAsyncEager(
1783 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
1784 context: *const anyopaque,
1785) void {
1786 const pre_acknowledged = if (Thread.current) |thread| ack: {
1787 break :ack switch (thread.status.load(.monotonic).cancelation) {
1788 .none, .canceling => false,
1789 .canceled => true,
1790 .parked => unreachable,
1791 .blocked => unreachable,
1792 .blocked_windows_dns => unreachable,
1793 .blocked_canceling => unreachable,
1794 };
1795 } else false;
1796 const result = start(context);
1797 const post_acknowledged = if (Thread.current) |thread| ack: {
1798 break :ack switch (thread.status.load(.monotonic).cancelation) {
1799 .none, .canceling => false,
1800 .canceled => true,
1801 .parked => unreachable,
1802 .blocked => unreachable,
1803 .blocked_windows_dns => unreachable,
1804 .blocked_canceling => unreachable,
1805 };
1806 } else false;
14771807
1478fn assertGroupResult(t: *Threaded, result: Io.Cancelable!void) void {1808 if (result) {
1479 const current_thread: *Thread = .getCurrent(t);1809 if (pre_acknowledged) {
1480 const current_closure = current_thread.current_closure orelse return;1810 assert(post_acknowledged); // group task called `recancel` but was not canceled
1481 GroupClosure.assertResult(current_closure, result);1811 } else {
1812 assert(!post_acknowledged); // group task acknowledged cancelation but did not return `error.Canceled`
1813 }
1814 } else |err| switch (err) {
1815 // Don't swallow the cancelation: make it visible to the `Group.async` caller.
1816 error.Canceled => {
1817 assert(!pre_acknowledged); // group task called `recancel` but was not canceled
1818 assert(post_acknowledged); // group task returned `error.Canceled` but was never canceled
1819 recancelInner();
1820 },
1821 }
1482}1822}
14831823
1484fn groupConcurrent(1824fn groupConcurrent(
1485 userdata: ?*anyopaque,1825 userdata: ?*anyopaque,
1486 group: *Io.Group,1826 type_erased: *Io.Group,
1487 context: []const u8,1827 context: []const u8,
1488 context_alignment: Alignment,1828 context_alignment: Alignment,
1489 start: *const fn (*Io.Group, context: *const anyopaque) Io.Cancelable!void,1829 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
1490) Io.ConcurrentError!void {1830) Io.ConcurrentError!void {
1491 if (builtin.single_threaded) return error.ConcurrencyUnavailable;1831 if (builtin.single_threaded) return error.ConcurrencyUnavailable;
14921832
1493 const t: *Threaded = @ptrCast(@alignCast(userdata));1833 const t: *Threaded = @ptrCast(@alignCast(userdata));
1834 const g: Group = .{ .ptr = type_erased };
14941835
1495 const gpa = t.allocator;1836 const gpa = t.allocator;
1496 const gc = GroupClosure.init(gpa, group, context, context_alignment, start) catch1837 const task = Group.Task.create(gpa, g, context, context_alignment, start) catch |err| switch (err) {
1497 return error.ConcurrencyUnavailable;1838 error.OutOfMemory => return error.ConcurrencyUnavailable,
1839 };
1840 errdefer task.destroy(gpa);
14981841
1499 t.mutex.lock();1842 t.mutex.lock();
1500 defer t.mutex.unlock();1843 defer t.mutex.unlock();
...@@ -1514,115 +1857,144 @@ fn groupConcurrent(...@@ -1514,115 +1857,144 @@ fn groupConcurrent(
15141857
1515 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch1858 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch
1516 return error.ConcurrencyUnavailable;1859 return error.ConcurrencyUnavailable;
1860
1517 thread.detach();1861 thread.detach();
1518 }1862 }
15191863
1520 // Append to the group linked list inside the mutex to make `Io.Group.concurrent` thread-safe.1864 // TODO: if this logic is changed to be lock-free, this `fetchAdd` must be released by the queue
1521 gc.node = .{ .next = @ptrCast(@alignCast(group.token.load(.monotonic))) };1865 // prepend so that the task doesn't finish without observing this and try to decrement the count
1522 group.token.store(&gc.node, .monotonic);1866 // below zero.
15231867 _ = g.status().fetchAdd(.{
1524 t.run_queue.prepend(&gc.closure.node);1868 .num_running = 1,
15251869 .have_awaiter = false,
1526 // This needs to be done before unlocking the mutex to avoid a race with1870 .canceled = false,
1527 // the associated task finishing.1871 }, .monotonic);
1528 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);1872 t.run_queue.prepend(&task.runnable.node);
1529 const prev_state = group_state.fetchAdd(GroupClosure.sync_one_pending, .monotonic);
1530 assert((prev_state / GroupClosure.sync_one_pending) < (std.math.maxInt(usize) / GroupClosure.sync_one_pending));
15311873
1532 t.cond.signal();1874 t.cond.signal();
1533}1875}
15341876
1535fn groupAwait(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void {1877fn groupAwait(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void {
1878 _ = initial_token; // we need to load `token` *after* the group finishes
1536 const t: *Threaded = @ptrCast(@alignCast(userdata));1879 const t: *Threaded = @ptrCast(@alignCast(userdata));
1537 const gpa = t.allocator;1880 const g: Group = .{ .ptr = type_erased };
15381881
1539 _ = initial_token; // we need to load `token` *after* the group finishes1882 var num_completed: std.atomic.Value(u32) = .init(0);
1883 g.awaiter().* = &num_completed;
15401884
1541 if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null`1885 const pre_await_status = g.status().fetchOr(.{
1886 .num_running = 0,
1887 .have_awaiter = true,
1888 .canceled = false,
1889 }, .acq_rel); // acquire results if complete; release `g.awaiter()`
15421890
1543 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);1891 assert(!pre_await_status.have_awaiter);
1544 const event: *Io.Event = @ptrCast(&group.context);1892 assert(!pre_await_status.canceled);
1545 const prev_state = group_state.fetchAdd(GroupClosure.sync_is_waiting, .acquire);1893 if (pre_await_status.num_running == 0) {
1546 assert(prev_state & GroupClosure.sync_is_waiting == 0);1894 // Already done. Since the group is finished, it's illegal to spawn more tasks in it
1547 {1895 // until we return, so we can access `g.status()` non-atomically.
1548 errdefer _ = group_state.fetchSub(GroupClosure.sync_is_waiting, .monotonic);1896 g.status().raw.have_awaiter = false;
1549 // This event.wait can return error.Canceled, in which case this logic does1897 return;
1550 // *not* propagate cancel requests to each group member. Instead, the user
1551 // code will likely do this with a defered call to groupCancel, or,
1552 // intentionally not do this.
1553 if ((prev_state / GroupClosure.sync_one_pending) > 0) try event.wait(ioBasic(t));
1554 }1898 }
15551899
1556 // Since the group has now finished, it's illegal to add more tasks to it until we return. It's1900 while (Thread.futexWait(&num_completed.raw, 0, null)) {
1557 // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only1901 switch (num_completed.load(.acquire)) { // acquire task results
1558 // thread who can access `group` right now.1902 0 => continue,
1559 var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.raw));1903 1 => break,
1560 group.token.raw = null;1904 else => unreachable, // group was reused before `await` returned
1561 while (it) |node| {1905 }
1562 it = node.next; // update `it` now, because `deinit` will invalidate `node`1906 } else |err| switch (err) {
1563 const gc: *GroupClosure = @fieldParentPtr("node", node);1907 error.Canceled => {
1564 gc.deinit(gpa);1908 const pre_cancel_status = g.status().fetchOr(.{
1909 .num_running = 0,
1910 .have_awaiter = false,
1911 .canceled = true,
1912 }, .acq_rel); // acquire results if complete; release `g.awaiter()`
1913 assert(pre_cancel_status.have_awaiter);
1914 assert(!pre_cancel_status.canceled);
1915
1916 // Even if `pre_cancel_status.num_running == 0`, we still need to wait for the signal,
1917 // because in that case the last member of the group is already trying to modify it.
1918 // However, if we know everything is done, we *can* skip signaling blocked threads.
1919 const skip_signals = pre_cancel_status.num_running == 0;
1920 g.waitForCancelWithSignaling(t, &num_completed, skip_signals);
1921
1922 // The group is finished, so it's illegal to spawn more tasks in it until we return, so
1923 // we can access `g.status()` non-atomically.
1924 g.status().raw.canceled = false;
1925 g.status().raw.have_awaiter = false;
1926 return error.Canceled;
1927 },
1565 }1928 }
1929
1930 // The group is finished, so it's illegal to spawn more tasks in it until we return, so
1931 // we can access `g.status()` non-atomically.
1932 g.status().raw.have_awaiter = false;
1566}1933}
15671934
1568fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) void {1935fn groupCancel(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) void {
1936 _ = initial_token;
1569 const t: *Threaded = @ptrCast(@alignCast(userdata));1937 const t: *Threaded = @ptrCast(@alignCast(userdata));
1570 const gpa = t.allocator;1938 const g: Group = .{ .ptr = type_erased };
15711939
1572 _ = initial_token; // we need to load `token` *after* the group finishes1940 var num_completed: std.atomic.Value(u32) = .init(0);
1941 g.awaiter().* = &num_completed;
15731942
1574 if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null`1943 const pre_cancel_status = g.status().fetchOr(.{
1944 .num_running = 0,
1945 .have_awaiter = true,
1946 .canceled = true,
1947 }, .acq_rel); // acquire results if complete; release `g.awaiter()`
15751948
1576 {1949 assert(!pre_cancel_status.have_awaiter);
1577 var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.load(.monotonic)));1950 assert(!pre_cancel_status.canceled);
1578 while (it) |node| : (it = node.next) {1951 if (pre_cancel_status.num_running == 0) {
1579 const gc: *GroupClosure = @fieldParentPtr("node", node);1952 // Already done. Since the group is finished, it's illegal to spawn more tasks in it
1580 gc.closure.requestCancel(t);1953 // until we return, so we can access `g.status()` non-atomically.
1581 }1954 g.status().raw.have_awaiter = false;
1955 g.status().raw.canceled = false;
1956 return;
1582 }1957 }
15831958
1584 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);1959 g.waitForCancelWithSignaling(t, &num_completed, false);
1585 const event: *Io.Event = @ptrCast(&group.context);
1586 const prev_state = group_state.fetchAdd(GroupClosure.sync_is_waiting, .acquire);
1587 assert(prev_state & GroupClosure.sync_is_waiting == 0);
1588 if ((prev_state / GroupClosure.sync_one_pending) > 0) event.waitUncancelable(ioBasic(t));
15891960
1590 // Since the group has now finished, it's illegal to add more tasks to it until we return. It's1961 g.status().raw = .{ .num_running = 0, .have_awaiter = false, .canceled = false };
1591 // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only
1592 // thread who can access `group` right now.
1593 var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.raw));
1594 group.token.raw = null;
1595 while (it) |node| {
1596 it = node.next; // update `it` now, because `deinit` will invalidate `node`
1597 const gc: *GroupClosure = @fieldParentPtr("node", node);
1598 gc.deinit(gpa);
1599 }
1600}1962}
16011963
1602fn recancel(userdata: ?*anyopaque) void {1964fn recancel(userdata: ?*anyopaque) void {
1603 const t: *Threaded = @ptrCast(@alignCast(userdata));1965 const t: *Threaded = @ptrCast(@alignCast(userdata));
1604 const current_thread: *Thread = .getCurrent(t);1966 _ = t;
1605 const cancel_status = &current_thread.current_closure.?.cancel_status;1967 recancelInner();
1606 switch (@atomicLoad(CancelStatus, cancel_status, .monotonic)) {1968}
1607 .none => unreachable, // called `recancel` when not canceled1969fn recancelInner() void {
1608 .requested => unreachable, // called `recancel` when cancelation was already outstanding1970 const thread = Thread.current.?; // called `recancel` but was not canceled
1609 .acknowledged => {},1971 switch (thread.status.fetchXor(.{
1610 _ => unreachable, // invalid state: not in a syscall1972 .cancelation = @enumFromInt(0b001),
1973 .awaitable = .null,
1974 }, .monotonic).cancelation) {
1975 .canceled => {},
1976 .none => unreachable, // called `recancel` but was not canceled
1977 .canceling => unreachable, // called `recancel` but cancelation was already pending
1978 .parked => unreachable,
1979 .blocked => unreachable,
1980 .blocked_windows_dns => unreachable,
1981 .blocked_canceling => unreachable,
1611 }1982 }
1612 @atomicStore(CancelStatus, cancel_status, .requested, .monotonic);
1613}1983}
16141984
1615fn swapCancelProtection(userdata: ?*anyopaque, new: Io.CancelProtection) Io.CancelProtection {1985fn swapCancelProtection(userdata: ?*anyopaque, new: Io.CancelProtection) Io.CancelProtection {
1616 const t: *Threaded = @ptrCast(@alignCast(userdata));1986 const t: *Threaded = @ptrCast(@alignCast(userdata));
1617 const current_thread: *Thread = .getCurrent(t);1987 _ = t;
1618 const old = current_thread.cancel_protection;1988 const thread = Thread.current orelse return .unblocked;
1619 current_thread.cancel_protection = new;1989 const old = thread.cancel_protection;
1990 thread.cancel_protection = new;
1620 return old;1991 return old;
1621}1992}
16221993
1623fn checkCancel(userdata: ?*anyopaque) Io.Cancelable!void {1994fn checkCancel(userdata: ?*anyopaque) Io.Cancelable!void {
1624 const t: *Threaded = @ptrCast(@alignCast(userdata));1995 const t: *Threaded = @ptrCast(@alignCast(userdata));
1625 return Thread.getCurrent(t).checkCancel();1996 _ = t;
1997 return Thread.checkCancel();
1626}1998}
16271999
1628fn await(2000fn await(
...@@ -1633,8 +2005,59 @@ fn await(...@@ -1633,8 +2005,59 @@ fn await(
1633) void {2005) void {
1634 _ = result_alignment;2006 _ = result_alignment;
1635 const t: *Threaded = @ptrCast(@alignCast(userdata));2007 const t: *Threaded = @ptrCast(@alignCast(userdata));
1636 const closure: *AsyncClosure = @ptrCast(@alignCast(any_future));2008 const future: *Future = @ptrCast(@alignCast(any_future));
1637 closure.waitAndDeinit(t, result);2009
2010 var num_completed: std.atomic.Value(u32) = .init(0);
2011 future.awaiter = &num_completed;
2012
2013 const pre_await_status = future.status.fetchOr(.{
2014 .tag = .pending_awaited,
2015 .thread = .null,
2016 }, .acq_rel); // acquire results if complete; release `future.awaiter`
2017 switch (pre_await_status.tag) {
2018 .pending => while (Thread.futexWait(&num_completed.raw, 0, null)) {
2019 switch (num_completed.load(.acquire)) { // acquire task results
2020 0 => continue,
2021 1 => break,
2022 else => unreachable, // group was reused before `await` returned
2023 }
2024 } else |err| switch (err) {
2025 error.Canceled => {
2026 const pre_cancel_status = future.status.fetchOr(.{
2027 .tag = .pending_canceled,
2028 .thread = .null,
2029 }, .acq_rel); // acquire results if complete; release `future.awaiter`
2030 switch (pre_cancel_status.tag) {
2031 .pending => unreachable, // invalid state: we already awaited
2032 .pending_awaited => {
2033 const working_thread = pre_cancel_status.thread.unpack();
2034 future.waitForCancelWithSignaling(t, &num_completed, @alignCast(working_thread));
2035 },
2036 .pending_canceled => unreachable, // `await` raced with `cancel`
2037 .done => {
2038 // The task just finished, but we still need to wait for the signal, because the
2039 // task thread already figured out that they need to update `future.awaiter`.
2040 future.waitForCancelWithSignaling(t, &num_completed, null);
2041 },
2042 }
2043 // If the future did not acknowledge the cancelation, we need to mark it outstanding
2044 // for us. Because `future.status.tag == .done`, the information about whether there
2045 // was an acknowledged cancelation is encoded in `future.status.thread`.
2046 const final_status = future.status.load(.monotonic);
2047 assert(final_status.tag == .done);
2048 switch (final_status.thread) {
2049 .null => recancelInner(), // cancelation was not acknowledged, so it's ours
2050 .all_ones => {}, // cancelation was acknowledged, so it was this task's job to propagate it
2051 _ => unreachable,
2052 }
2053 },
2054 },
2055 .pending_awaited => unreachable, // `await` raced with `await`
2056 .pending_canceled => unreachable, // `await` raced with `cancel`
2057 .done => {},
2058 }
2059 @memcpy(result, future.resultPointer());
2060 future.destroy(t.allocator);
1638}2061}
16392062
1640fn cancel(2063fn cancel(
...@@ -1645,28 +2068,44 @@ fn cancel(...@@ -1645,28 +2068,44 @@ fn cancel(
1645) void {2068) void {
1646 _ = result_alignment;2069 _ = result_alignment;
1647 const t: *Threaded = @ptrCast(@alignCast(userdata));2070 const t: *Threaded = @ptrCast(@alignCast(userdata));
1648 const ac: *AsyncClosure = @ptrCast(@alignCast(any_future));2071 const future: *Future = @ptrCast(@alignCast(any_future));
1649 ac.closure.requestCancel(t);2072
1650 ac.waitAndDeinit(t, result);2073 var num_completed: std.atomic.Value(u32) = .init(0);
2074 future.awaiter = &num_completed;
2075
2076 const pre_cancel_status = future.status.fetchOr(.{
2077 .tag = .pending_canceled,
2078 .thread = .null,
2079 }, .acq_rel); // acquire results if complete; release `future.awaiter`
2080 switch (pre_cancel_status.tag) {
2081 .pending => {
2082 const working_thread = pre_cancel_status.thread.unpack();
2083 future.waitForCancelWithSignaling(t, &num_completed, @alignCast(working_thread));
2084 },
2085 .pending_awaited => unreachable, // `await` raced with `await`
2086 .pending_canceled => unreachable, // `await` raced with `cancel`
2087 .done => {},
2088 }
2089 @memcpy(result, future.resultPointer());
2090 future.destroy(t.allocator);
1651}2091}
16522092
1653fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io.Timeout) Io.Cancelable!void {2093fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io.Timeout) Io.Cancelable!void {
1654 if (builtin.single_threaded) unreachable; // Deadlock.2094 if (builtin.single_threaded) unreachable; // Deadlock.
1655 const t: *Threaded = @ptrCast(@alignCast(userdata));2095 const t: *Threaded = @ptrCast(@alignCast(userdata));
1656 const current_thread = Thread.getCurrent(t);
1657 const t_io = ioBasic(t);2096 const t_io = ioBasic(t);
1658 const timeout_ns: ?u64 = ns: {2097 const timeout_ns: ?u64 = ns: {
1659 const d = (timeout.toDurationFromNow(t_io) catch break :ns 10) orelse break :ns null;2098 const d = (timeout.toDurationFromNow(t_io) catch break :ns 10) orelse break :ns null;
1660 break :ns std.math.lossyCast(u64, d.raw.toNanoseconds());2099 break :ns std.math.lossyCast(u64, d.raw.toNanoseconds());
1661 };2100 };
1662 return Thread.futexWaitTimed(current_thread, ptr, expected, timeout_ns);2101 return Thread.futexWait(ptr, expected, timeout_ns);
1663}2102}
16642103
1665fn futexWaitUncancelable(userdata: ?*anyopaque, ptr: *const u32, expected: u32) void {2104fn futexWaitUncancelable(userdata: ?*anyopaque, ptr: *const u32, expected: u32) void {
1666 if (builtin.single_threaded) unreachable; // Deadlock.2105 if (builtin.single_threaded) unreachable; // Deadlock.
1667 const t: *Threaded = @ptrCast(@alignCast(userdata));2106 const t: *Threaded = @ptrCast(@alignCast(userdata));
1668 _ = t;2107 _ = t;
1669 Thread.futexWaitUncancelable(ptr, expected);2108 Thread.futexWaitUncancelable(ptr, expected, null);
1670}2109}
16712110
1672fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {2111fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {
...@@ -1684,24 +2123,24 @@ const dirCreateDir = switch (native_os) {...@@ -1684,24 +2123,24 @@ const dirCreateDir = switch (native_os) {
16842123
1685fn dirCreateDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {2124fn dirCreateDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
1686 const t: *Threaded = @ptrCast(@alignCast(userdata));2125 const t: *Threaded = @ptrCast(@alignCast(userdata));
1687 const current_thread = Thread.getCurrent(t);2126 _ = t;
16882127
1689 var path_buffer: [posix.PATH_MAX]u8 = undefined;2128 var path_buffer: [posix.PATH_MAX]u8 = undefined;
1690 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);2129 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
16912130
1692 try current_thread.beginSyscall();2131 const syscall: Syscall = try .start();
1693 while (true) {2132 while (true) {
1694 switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, permissions.toMode()))) {2133 switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, permissions.toMode()))) {
1695 .SUCCESS => {2134 .SUCCESS => {
1696 current_thread.endSyscall();2135 syscall.finish();
1697 return;2136 return;
1698 },2137 },
1699 .INTR => {2138 .INTR => {
1700 try current_thread.checkCancel();2139 try syscall.checkCancel();
1701 continue;2140 continue;
1702 },2141 },
1703 else => |e| {2142 else => |e| {
1704 current_thread.endSyscall();2143 syscall.finish();
1705 switch (e) {2144 switch (e) {
1706 .ACCES => return error.AccessDenied,2145 .ACCES => return error.AccessDenied,
1707 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2146 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -1730,20 +2169,20 @@ fn dirCreateDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, perm...@@ -1730,20 +2169,20 @@ fn dirCreateDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, perm
1730fn dirCreateDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {2169fn dirCreateDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
1731 if (builtin.link_libc) return dirCreateDirPosix(userdata, dir, sub_path, permissions);2170 if (builtin.link_libc) return dirCreateDirPosix(userdata, dir, sub_path, permissions);
1732 const t: *Threaded = @ptrCast(@alignCast(userdata));2171 const t: *Threaded = @ptrCast(@alignCast(userdata));
1733 const current_thread = Thread.getCurrent(t);2172 _ = t;
1734 try current_thread.beginSyscall();2173 const syscall: Syscall = try .start();
1735 while (true) {2174 while (true) {
1736 switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) {2175 switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) {
1737 .SUCCESS => {2176 .SUCCESS => {
1738 current_thread.endSyscall();2177 syscall.finish();
1739 return;2178 return;
1740 },2179 },
1741 .INTR => {2180 .INTR => {
1742 try current_thread.checkCancel();2181 try syscall.checkCancel();
1743 continue;2182 continue;
1744 },2183 },
1745 else => |e| {2184 else => |e| {
1746 current_thread.endSyscall();2185 syscall.finish();
1747 switch (e) {2186 switch (e) {
1748 .ACCES => return error.AccessDenied,2187 .ACCES => return error.AccessDenied,
1749 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2188 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -1770,27 +2209,35 @@ fn dirCreateDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permi...@@ -1770,27 +2209,35 @@ fn dirCreateDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permi
17702209
1771fn dirCreateDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {2210fn dirCreateDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
1772 const t: *Threaded = @ptrCast(@alignCast(userdata));2211 const t: *Threaded = @ptrCast(@alignCast(userdata));
1773 const current_thread = Thread.getCurrent(t);2212 _ = t;
1774 try current_thread.checkCancel();
17752213
1776 const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);2214 const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
1777 _ = permissions; // TODO use this value2215 _ = permissions; // TODO use this value
1778 const sub_dir_handle = windows.OpenFile(sub_path_w.span(), .{2216
1779 .dir = dir.handle,2217 const syscall: Syscall = try .start();
1780 .access_mask = .{2218 const sub_dir_handle = while (true) {
1781 .GENERIC = .{ .READ = true },2219 break windows.OpenFile(sub_path_w.span(), .{
1782 .STANDARD = .{ .SYNCHRONIZE = true },2220 .dir = dir.handle,
1783 },2221 .access_mask = .{
1784 .creation = .CREATE,2222 .GENERIC = .{ .READ = true },
1785 .filter = .dir_only,2223 .STANDARD = .{ .SYNCHRONIZE = true },
1786 }) catch |err| switch (err) {2224 },
1787 error.IsDir => return error.Unexpected,2225 .creation = .CREATE,
1788 error.PipeBusy => return error.Unexpected,2226 .filter = .dir_only,
1789 error.NoDevice => return error.Unexpected,2227 }) catch |err| switch (err) {
1790 error.WouldBlock => return error.Unexpected,2228 error.IsDir => return syscall.fail(error.Unexpected),
1791 error.AntivirusInterference => return error.Unexpected,2229 error.PipeBusy => return syscall.fail(error.Unexpected),
1792 else => |e| return e,2230 error.NoDevice => return syscall.fail(error.Unexpected),
2231 error.WouldBlock => return syscall.fail(error.Unexpected),
2232 error.AntivirusInterference => return syscall.fail(error.Unexpected),
2233 error.OperationCanceled => {
2234 try syscall.checkCancel();
2235 continue;
2236 },
2237 else => |e| return syscall.fail(e),
2238 };
1793 };2239 };
2240 syscall.finish();
1794 windows.CloseHandle(sub_dir_handle);2241 windows.CloseHandle(sub_dir_handle);
1795}2242}
17962243
...@@ -1858,7 +2305,6 @@ fn dirCreateDirPathOpenWindows(...@@ -1858,7 +2305,6 @@ fn dirCreateDirPathOpenWindows(
1858 options: Dir.OpenOptions,2305 options: Dir.OpenOptions,
1859) Dir.CreateDirPathOpenError!Dir {2306) Dir.CreateDirPathOpenError!Dir {
1860 const t: *Threaded = @ptrCast(@alignCast(userdata));2307 const t: *Threaded = @ptrCast(@alignCast(userdata));
1861 const current_thread = Thread.getCurrent(t);
1862 const w = windows;2308 const w = windows;
18632309
1864 _ = permissions; // TODO apply these permissions2310 _ = permissions; // TODO apply these permissions
...@@ -1870,9 +2316,7 @@ fn dirCreateDirPathOpenWindows(...@@ -1870,9 +2316,7 @@ fn dirCreateDirPathOpenWindows(
1870 .path = sub_path,2316 .path = sub_path,
1871 };2317 };
18722318
1873 while (true) {2319 components: while (true) {
1874 try current_thread.checkCancel();
1875
1876 const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, component.path);2320 const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, component.path);
1877 const sub_path_w = sub_path_w_array.span();2321 const sub_path_w = sub_path_w_array.span();
1878 const is_last = it.peekNext() == null;2322 const is_last = it.peekNext() == null;
...@@ -1887,7 +2331,9 @@ fn dirCreateDirPathOpenWindows(...@@ -1887,7 +2331,9 @@ fn dirCreateDirPathOpenWindows(
1887 .Buffer = @constCast(sub_path_w.ptr),2331 .Buffer = @constCast(sub_path_w.ptr),
1888 };2332 };
1889 var io_status_block: w.IO_STATUS_BLOCK = undefined;2333 var io_status_block: w.IO_STATUS_BLOCK = undefined;
1890 const rc = w.ntdll.NtCreateFile(2334
2335 const syscall: Syscall = try .start();
2336 while (true) switch (w.ntdll.NtCreateFile(
1891 &result.handle,2337 &result.handle,
1892 .{2338 .{
1893 .SPECIFIC = .{ .FILE_DIRECTORY = .{2339 .SPECIFIC = .{ .FILE_DIRECTORY = .{
...@@ -1922,16 +2368,20 @@ fn dirCreateDirPathOpenWindows(...@@ -1922,16 +2368,20 @@ fn dirCreateDirPathOpenWindows(
1922 },2368 },
1923 null,2369 null,
1924 0,2370 0,
1925 );2371 )) {
1926
1927 switch (rc) {
1928 .SUCCESS => {2372 .SUCCESS => {
2373 syscall.finish();
1929 component = it.next() orelse return result;2374 component = it.next() orelse return result;
1930 w.CloseHandle(result.handle);2375 w.CloseHandle(result.handle);
2376 continue :components;
2377 },
2378 .CANCELLED => {
2379 try syscall.checkCancel();
1931 continue;2380 continue;
1932 },2381 },
1933 .OBJECT_NAME_INVALID => return error.BadPathName,2382 .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
1934 .OBJECT_NAME_COLLISION => {2383 .OBJECT_NAME_COLLISION => {
2384 syscall.finish();
1935 assert(!is_last);2385 assert(!is_last);
1936 // stat the file and return an error if it's not a directory2386 // stat the file and return an error if it's not a directory
1937 // this is important because otherwise a dangling symlink2387 // this is important because otherwise a dangling symlink
...@@ -1942,23 +2392,24 @@ fn dirCreateDirPathOpenWindows(...@@ -1942,23 +2392,24 @@ fn dirCreateDirPathOpenWindows(
1942 if (fstat.kind != .directory) return error.NotDir;2392 if (fstat.kind != .directory) return error.NotDir;
19432393
1944 component = it.next().?;2394 component = it.next().?;
1945 continue;2395 continue :components;
1946 },2396 },
19472397
1948 .OBJECT_NAME_NOT_FOUND,2398 .OBJECT_NAME_NOT_FOUND,
1949 .OBJECT_PATH_NOT_FOUND,2399 .OBJECT_PATH_NOT_FOUND,
1950 => {2400 => {
2401 syscall.finish();
1951 component = it.previous() orelse return error.FileNotFound;2402 component = it.previous() orelse return error.FileNotFound;
1952 continue;2403 continue :components;
1953 },2404 },
19542405
1955 .NOT_A_DIRECTORY => return error.NotDir,2406 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
1956 // This can happen if the directory has 'List folder contents' permission set to 'Deny'2407 // This can happen if the directory has 'List folder contents' permission set to 'Deny'
1957 // and the directory is trying to be opened for iteration.2408 // and the directory is trying to be opened for iteration.
1958 .ACCESS_DENIED => return error.AccessDenied,2409 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
1959 .INVALID_PARAMETER => |err| return w.statusBug(err),2410 .INVALID_PARAMETER => |s| return syscall.ntstatusBug(s),
1960 else => return w.unexpectedStatus(rc),2411 else => |s| return syscall.unexpectedNtstatus(s),
1961 }2412 };
1962 }2413 }
1963}2414}
19642415
...@@ -2000,7 +2451,7 @@ fn dirStatFileLinux(...@@ -2000,7 +2451,7 @@ fn dirStatFileLinux(
2000 options: Dir.StatFileOptions,2451 options: Dir.StatFileOptions,
2001) Dir.StatFileError!File.Stat {2452) Dir.StatFileError!File.Stat {
2002 const t: *Threaded = @ptrCast(@alignCast(userdata));2453 const t: *Threaded = @ptrCast(@alignCast(userdata));
2003 const current_thread = Thread.getCurrent(t);2454 _ = t;
2004 const linux = std.os.linux;2455 const linux = std.os.linux;
2005 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())2456 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
2006 .{ .major = 30, .minor = 0, .patch = 0 }2457 .{ .major = 30, .minor = 0, .patch = 0 }
...@@ -2014,20 +2465,20 @@ fn dirStatFileLinux(...@@ -2014,20 +2465,20 @@ fn dirStatFileLinux(
2014 const flags: u32 = linux.AT.NO_AUTOMOUNT |2465 const flags: u32 = linux.AT.NO_AUTOMOUNT |
2015 @as(u32, if (!options.follow_symlinks) linux.AT.SYMLINK_NOFOLLOW else 0);2466 @as(u32, if (!options.follow_symlinks) linux.AT.SYMLINK_NOFOLLOW else 0);
20162467
2017 try current_thread.beginSyscall();2468 const syscall: Syscall = try .start();
2018 while (true) {2469 while (true) {
2019 var statx = std.mem.zeroes(linux.Statx);2470 var statx = std.mem.zeroes(linux.Statx);
2020 switch (sys.errno(sys.statx(dir.handle, sub_path_posix, flags, linux_statx_request, &statx))) {2471 switch (sys.errno(sys.statx(dir.handle, sub_path_posix, flags, linux_statx_request, &statx))) {
2021 .SUCCESS => {2472 .SUCCESS => {
2022 current_thread.endSyscall();2473 syscall.finish();
2023 return statFromLinux(&statx);2474 return statFromLinux(&statx);
2024 },2475 },
2025 .INTR => {2476 .INTR => {
2026 try current_thread.checkCancel();2477 try syscall.checkCancel();
2027 continue;2478 continue;
2028 },2479 },
2029 else => |e| {2480 else => |e| {
2030 current_thread.endSyscall();2481 syscall.finish();
2031 switch (e) {2482 switch (e) {
2032 .ACCES => return error.AccessDenied,2483 .ACCES => return error.AccessDenied,
2033 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2484 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2052,31 +2503,31 @@ fn dirStatFilePosix(...@@ -2052,31 +2503,31 @@ fn dirStatFilePosix(
2052 options: Dir.StatFileOptions,2503 options: Dir.StatFileOptions,
2053) Dir.StatFileError!File.Stat {2504) Dir.StatFileError!File.Stat {
2054 const t: *Threaded = @ptrCast(@alignCast(userdata));2505 const t: *Threaded = @ptrCast(@alignCast(userdata));
2055 const current_thread = Thread.getCurrent(t);2506 _ = t;
20562507
2057 var path_buffer: [posix.PATH_MAX]u8 = undefined;2508 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2058 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);2509 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
20592510
2060 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;2511 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
20612512
2062 return posixStatFile(current_thread, dir.handle, sub_path_posix, flags);2513 return posixStatFile(dir.handle, sub_path_posix, flags);
2063}2514}
20642515
2065fn posixStatFile(current_thread: *Thread, dir_fd: posix.fd_t, sub_path: [:0]const u8, flags: u32) Dir.StatFileError!File.Stat {2516fn posixStatFile(dir_fd: posix.fd_t, sub_path: [:0]const u8, flags: u32) Dir.StatFileError!File.Stat {
2066 try current_thread.beginSyscall();2517 const syscall: Syscall = try .start();
2067 while (true) {2518 while (true) {
2068 var stat = std.mem.zeroes(posix.Stat);2519 var stat = std.mem.zeroes(posix.Stat);
2069 switch (posix.errno(fstatat_sym(dir_fd, sub_path, &stat, flags))) {2520 switch (posix.errno(fstatat_sym(dir_fd, sub_path, &stat, flags))) {
2070 .SUCCESS => {2521 .SUCCESS => {
2071 current_thread.endSyscall();2522 syscall.finish();
2072 return statFromPosix(&stat);2523 return statFromPosix(&stat);
2073 },2524 },
2074 .INTR => {2525 .INTR => {
2075 try current_thread.checkCancel();2526 try syscall.checkCancel();
2076 continue;2527 continue;
2077 },2528 },
2078 else => |e| {2529 else => |e| {
2079 current_thread.endSyscall();2530 syscall.finish();
2080 switch (e) {2531 switch (e) {
2081 .INVAL => |err| return errnoBug(err),2532 .INVAL => |err| return errnoBug(err),
2082 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2533 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2118,25 +2569,25 @@ fn dirStatFileWasi(...@@ -2118,25 +2569,25 @@ fn dirStatFileWasi(
2118) Dir.StatFileError!File.Stat {2569) Dir.StatFileError!File.Stat {
2119 if (builtin.link_libc) return dirStatFilePosix(userdata, dir, sub_path, options);2570 if (builtin.link_libc) return dirStatFilePosix(userdata, dir, sub_path, options);
2120 const t: *Threaded = @ptrCast(@alignCast(userdata));2571 const t: *Threaded = @ptrCast(@alignCast(userdata));
2121 const current_thread = Thread.getCurrent(t);2572 _ = t;
2122 const wasi = std.os.wasi;2573 const wasi = std.os.wasi;
2123 const flags: wasi.lookupflags_t = .{2574 const flags: wasi.lookupflags_t = .{
2124 .SYMLINK_FOLLOW = options.follow_symlinks,2575 .SYMLINK_FOLLOW = options.follow_symlinks,
2125 };2576 };
2126 var stat: wasi.filestat_t = undefined;2577 var stat: wasi.filestat_t = undefined;
2127 try current_thread.beginSyscall();2578 const syscall: Syscall = try .start();
2128 while (true) {2579 while (true) {
2129 switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {2580 switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
2130 .SUCCESS => {2581 .SUCCESS => {
2131 current_thread.endSyscall();2582 syscall.finish();
2132 return statFromWasi(&stat);2583 return statFromWasi(&stat);
2133 },2584 },
2134 .INTR => {2585 .INTR => {
2135 try current_thread.checkCancel();2586 try syscall.checkCancel();
2136 continue;2587 continue;
2137 },2588 },
2138 else => |e| {2589 else => |e| {
2139 current_thread.endSyscall();2590 syscall.finish();
2140 switch (e) {2591 switch (e) {
2141 .INVAL => |err| return errnoBug(err),2592 .INVAL => |err| return errnoBug(err),
2142 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2593 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2159,24 +2610,23 @@ fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {...@@ -2159,24 +2610,23 @@ fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
2159 const t: *Threaded = @ptrCast(@alignCast(userdata));2610 const t: *Threaded = @ptrCast(@alignCast(userdata));
21602611
2161 if (native_os == .linux) {2612 if (native_os == .linux) {
2162 const current_thread = Thread.getCurrent(t);
2163 const linux = std.os.linux;2613 const linux = std.os.linux;
21642614
2165 try current_thread.beginSyscall();2615 const syscall: Syscall = try .start();
2166 while (true) {2616 while (true) {
2167 var statx = std.mem.zeroes(linux.Statx);2617 var statx = std.mem.zeroes(linux.Statx);
2168 switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .{ .SIZE = true }, &statx))) {2618 switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .{ .SIZE = true }, &statx))) {
2169 .SUCCESS => {2619 .SUCCESS => {
2170 current_thread.endSyscall();2620 syscall.finish();
2171 if (!statx.mask.SIZE) return error.Unexpected;2621 if (!statx.mask.SIZE) return error.Unexpected;
2172 return statx.size;2622 return statx.size;
2173 },2623 },
2174 .INTR => {2624 .INTR => {
2175 try current_thread.checkCancel();2625 try syscall.checkCancel();
2176 continue;2626 continue;
2177 },2627 },
2178 else => |e| {2628 else => |e| {
2179 current_thread.endSyscall();2629 syscall.finish();
2180 switch (e) {2630 switch (e) {
2181 .ACCES => |err| return errnoBug(err),2631 .ACCES => |err| return errnoBug(err),
2182 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2632 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2209,24 +2659,24 @@ const fileStat = switch (native_os) {...@@ -2209,24 +2659,24 @@ const fileStat = switch (native_os) {
22092659
2210fn fileStatPosix(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {2660fn fileStatPosix(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2211 const t: *Threaded = @ptrCast(@alignCast(userdata));2661 const t: *Threaded = @ptrCast(@alignCast(userdata));
2212 const current_thread = Thread.getCurrent(t);2662 _ = t;
22132663
2214 if (posix.Stat == void) return error.Streaming;2664 if (posix.Stat == void) return error.Streaming;
22152665
2216 try current_thread.beginSyscall();2666 const syscall: Syscall = try .start();
2217 while (true) {2667 while (true) {
2218 var stat = std.mem.zeroes(posix.Stat);2668 var stat = std.mem.zeroes(posix.Stat);
2219 switch (posix.errno(fstat_sym(file.handle, &stat))) {2669 switch (posix.errno(fstat_sym(file.handle, &stat))) {
2220 .SUCCESS => {2670 .SUCCESS => {
2221 current_thread.endSyscall();2671 syscall.finish();
2222 return statFromPosix(&stat);2672 return statFromPosix(&stat);
2223 },2673 },
2224 .INTR => {2674 .INTR => {
2225 try current_thread.checkCancel();2675 try syscall.checkCancel();
2226 continue;2676 continue;
2227 },2677 },
2228 else => |e| {2678 else => |e| {
2229 current_thread.endSyscall();2679 syscall.finish();
2230 switch (e) {2680 switch (e) {
2231 .INVAL => |err| return errnoBug(err),2681 .INVAL => |err| return errnoBug(err),
2232 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2682 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2241,7 +2691,7 @@ fn fileStatPosix(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -2241,7 +2691,7 @@ fn fileStatPosix(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
22412691
2242fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {2692fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2243 const t: *Threaded = @ptrCast(@alignCast(userdata));2693 const t: *Threaded = @ptrCast(@alignCast(userdata));
2244 const current_thread = Thread.getCurrent(t);2694 _ = t;
2245 const linux = std.os.linux;2695 const linux = std.os.linux;
2246 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())2696 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
2247 .{ .major = 30, .minor = 0, .patch = 0 }2697 .{ .major = 30, .minor = 0, .patch = 0 }
...@@ -2249,20 +2699,20 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -2249,20 +2699,20 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2249 .{ .major = 2, .minor = 28, .patch = 0 });2699 .{ .major = 2, .minor = 28, .patch = 0 });
2250 const sys = if (use_c) std.c else std.os.linux;2700 const sys = if (use_c) std.c else std.os.linux;
22512701
2252 try current_thread.beginSyscall();2702 const syscall: Syscall = try .start();
2253 while (true) {2703 while (true) {
2254 var statx = std.mem.zeroes(linux.Statx);2704 var statx = std.mem.zeroes(linux.Statx);
2255 switch (sys.errno(sys.statx(file.handle, "", linux.AT.EMPTY_PATH, linux_statx_request, &statx))) {2705 switch (sys.errno(sys.statx(file.handle, "", linux.AT.EMPTY_PATH, linux_statx_request, &statx))) {
2256 .SUCCESS => {2706 .SUCCESS => {
2257 current_thread.endSyscall();2707 syscall.finish();
2258 return statFromLinux(&statx);2708 return statFromLinux(&statx);
2259 },2709 },
2260 .INTR => {2710 .INTR => {
2261 try current_thread.checkCancel();2711 try syscall.checkCancel();
2262 continue;2712 continue;
2263 },2713 },
2264 else => |e| {2714 else => |e| {
2265 current_thread.endSyscall();2715 syscall.finish();
2266 switch (e) {2716 switch (e) {
2267 .ACCES => |err| return errnoBug(err),2717 .ACCES => |err| return errnoBug(err),
2268 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2718 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2282,21 +2732,32 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -2282,21 +2732,32 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
22822732
2283fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {2733fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2284 const t: *Threaded = @ptrCast(@alignCast(userdata));2734 const t: *Threaded = @ptrCast(@alignCast(userdata));
2285 const current_thread = Thread.getCurrent(t);2735 _ = t;
2286 try current_thread.checkCancel();
22872736
2288 var io_status_block: windows.IO_STATUS_BLOCK = undefined;2737 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
2289 var info: windows.FILE.ALL_INFORMATION = undefined;2738 var info: windows.FILE.ALL_INFORMATION = undefined;
2290 const rc = windows.ntdll.NtQueryInformationFile(file.handle, &io_status_block, &info, @sizeOf(windows.FILE.ALL_INFORMATION), .All);2739 {
2291 switch (rc) {2740 const syscall: Syscall = try .start();
2292 .SUCCESS => {},2741 while (true) switch (windows.ntdll.NtQueryInformationFile(
2293 // Buffer overflow here indicates that there is more information available than was able to be stored in the buffer2742 file.handle,
2294 // size provided. This is treated as success because the type of variable-length information that this would be relevant for2743 &io_status_block,
2295 // (name, volume name, etc) we don't care about.2744 &info,
2296 .BUFFER_OVERFLOW => {},2745 @sizeOf(windows.FILE.ALL_INFORMATION),
2297 .INVALID_PARAMETER => |err| return windows.statusBug(err),2746 .All,
2298 .ACCESS_DENIED => return error.AccessDenied,2747 )) {
2299 else => return windows.unexpectedStatus(rc),2748 .SUCCESS => break syscall.finish(),
2749 // Buffer overflow here indicates that there is more information available than was able to be stored in the buffer
2750 // size provided. This is treated as success because the type of variable-length information that this would be relevant for
2751 // (name, volume name, etc) we don't care about.
2752 .BUFFER_OVERFLOW => break syscall.finish(),
2753 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
2754 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
2755 .CANCELLED => {
2756 try syscall.checkCancel();
2757 continue;
2758 },
2759 else => |s| return syscall.unexpectedNtstatus(s),
2760 };
2300 }2761 }
2301 return .{2762 return .{
2302 .inode = info.InternalInformation.IndexNumber,2763 .inode = info.InternalInformation.IndexNumber,
...@@ -2304,15 +2765,25 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -2304,15 +2765,25 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2304 .permissions = .default_file,2765 .permissions = .default_file,
2305 .kind = if (info.BasicInformation.FileAttributes.REPARSE_POINT) reparse_point: {2766 .kind = if (info.BasicInformation.FileAttributes.REPARSE_POINT) reparse_point: {
2306 var tag_info: windows.FILE.ATTRIBUTE_TAG_INFO = undefined;2767 var tag_info: windows.FILE.ATTRIBUTE_TAG_INFO = undefined;
2307 const tag_rc = windows.ntdll.NtQueryInformationFile(file.handle, &io_status_block, &tag_info, @sizeOf(windows.FILE.ATTRIBUTE_TAG_INFO), .AttributeTag);2768 const syscall: Syscall = try .start();
2308 switch (tag_rc) {2769 while (true) switch (windows.ntdll.NtQueryInformationFile(
2309 .SUCCESS => {},2770 file.handle,
2771 &io_status_block,
2772 &tag_info,
2773 @sizeOf(windows.FILE.ATTRIBUTE_TAG_INFO),
2774 .AttributeTag,
2775 )) {
2776 .SUCCESS => break syscall.finish(),
2310 // INFO_LENGTH_MISMATCH and ACCESS_DENIED are the only documented possible errors2777 // INFO_LENGTH_MISMATCH and ACCESS_DENIED are the only documented possible errors
2311 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e2778 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e
2312 .INFO_LENGTH_MISMATCH => |err| return windows.statusBug(err),2779 .INFO_LENGTH_MISMATCH => |err| return syscall.ntstatusBug(err),
2313 .ACCESS_DENIED => return error.AccessDenied,2780 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
2314 else => return windows.unexpectedStatus(rc),2781 .CANCELLED => {
2315 }2782 try syscall.checkCancel();
2783 continue;
2784 },
2785 else => |s| return syscall.unexpectedNtstatus(s),
2786 };
2316 if (tag_info.ReparseTag.IsSurrogate) break :reparse_point .sym_link;2787 if (tag_info.ReparseTag.IsSurrogate) break :reparse_point .sym_link;
2317 // Unknown reparse point2788 // Unknown reparse point
2318 break :reparse_point .unknown;2789 break :reparse_point .unknown;
...@@ -2331,22 +2802,22 @@ fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -2331,22 +2802,22 @@ fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2331 if (builtin.link_libc) return fileStatPosix(userdata, file);2802 if (builtin.link_libc) return fileStatPosix(userdata, file);
23322803
2333 const t: *Threaded = @ptrCast(@alignCast(userdata));2804 const t: *Threaded = @ptrCast(@alignCast(userdata));
2334 const current_thread = Thread.getCurrent(t);2805 _ = t;
23352806
2336 try current_thread.beginSyscall();2807 const syscall: Syscall = try .start();
2337 while (true) {2808 while (true) {
2338 var stat: std.os.wasi.filestat_t = undefined;2809 var stat: std.os.wasi.filestat_t = undefined;
2339 switch (std.os.wasi.fd_filestat_get(file.handle, &stat)) {2810 switch (std.os.wasi.fd_filestat_get(file.handle, &stat)) {
2340 .SUCCESS => {2811 .SUCCESS => {
2341 current_thread.endSyscall();2812 syscall.finish();
2342 return statFromWasi(&stat);2813 return statFromWasi(&stat);
2343 },2814 },
2344 .INTR => {2815 .INTR => {
2345 try current_thread.checkCancel();2816 try syscall.checkCancel();
2346 continue;2817 continue;
2347 },2818 },
2348 else => |e| {2819 else => |e| {
2349 current_thread.endSyscall();2820 syscall.finish();
2350 switch (e) {2821 switch (e) {
2351 .INVAL => |err| return errnoBug(err),2822 .INVAL => |err| return errnoBug(err),
2352 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2823 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2373,7 +2844,7 @@ fn dirAccessPosix(...@@ -2373,7 +2844,7 @@ fn dirAccessPosix(
2373 options: Dir.AccessOptions,2844 options: Dir.AccessOptions,
2374) Dir.AccessError!void {2845) Dir.AccessError!void {
2375 const t: *Threaded = @ptrCast(@alignCast(userdata));2846 const t: *Threaded = @ptrCast(@alignCast(userdata));
2376 const current_thread = Thread.getCurrent(t);2847 _ = t;
23772848
2378 var path_buffer: [posix.PATH_MAX]u8 = undefined;2849 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2379 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);2850 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
...@@ -2385,19 +2856,19 @@ fn dirAccessPosix(...@@ -2385,19 +2856,19 @@ fn dirAccessPosix(
2385 @as(u32, if (options.write) posix.W_OK else 0) |2856 @as(u32, if (options.write) posix.W_OK else 0) |
2386 @as(u32, if (options.execute) posix.X_OK else 0);2857 @as(u32, if (options.execute) posix.X_OK else 0);
23872858
2388 try current_thread.beginSyscall();2859 const syscall: Syscall = try .start();
2389 while (true) {2860 while (true) {
2390 switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) {2861 switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) {
2391 .SUCCESS => {2862 .SUCCESS => {
2392 current_thread.endSyscall();2863 syscall.finish();
2393 return;2864 return;
2394 },2865 },
2395 .INTR => {2866 .INTR => {
2396 try current_thread.checkCancel();2867 try syscall.checkCancel();
2397 continue;2868 continue;
2398 },2869 },
2399 else => |e| {2870 else => |e| {
2400 current_thread.endSyscall();2871 syscall.finish();
2401 switch (e) {2872 switch (e) {
2402 .ACCES => return error.AccessDenied,2873 .ACCES => return error.AccessDenied,
2403 .PERM => return error.PermissionDenied,2874 .PERM => return error.PermissionDenied,
...@@ -2427,26 +2898,26 @@ fn dirAccessWasi(...@@ -2427,26 +2898,26 @@ fn dirAccessWasi(
2427) Dir.AccessError!void {2898) Dir.AccessError!void {
2428 if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);2899 if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);
2429 const t: *Threaded = @ptrCast(@alignCast(userdata));2900 const t: *Threaded = @ptrCast(@alignCast(userdata));
2430 const current_thread = Thread.getCurrent(t);2901 _ = t;
2431 const wasi = std.os.wasi;2902 const wasi = std.os.wasi;
2432 const flags: wasi.lookupflags_t = .{2903 const flags: wasi.lookupflags_t = .{
2433 .SYMLINK_FOLLOW = options.follow_symlinks,2904 .SYMLINK_FOLLOW = options.follow_symlinks,
2434 };2905 };
2435 var stat: wasi.filestat_t = undefined;2906 var stat: wasi.filestat_t = undefined;
24362907
2437 try current_thread.beginSyscall();2908 const syscall: Syscall = try .start();
2438 while (true) {2909 while (true) {
2439 switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {2910 switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
2440 .SUCCESS => {2911 .SUCCESS => {
2441 current_thread.endSyscall();2912 syscall.finish();
2442 break;2913 break;
2443 },2914 },
2444 .INTR => {2915 .INTR => {
2445 try current_thread.checkCancel();2916 try syscall.checkCancel();
2446 continue;2917 continue;
2447 },2918 },
2448 else => |e| {2919 else => |e| {
2449 current_thread.endSyscall();2920 syscall.finish();
2450 switch (e) {2921 switch (e) {
2451 .INVAL => |err| return errnoBug(err),2922 .INVAL => |err| return errnoBug(err),
2452 .BADF => |err| return errnoBug(err), // File descriptor used after closed.2923 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -2498,8 +2969,7 @@ fn dirAccessWindows(...@@ -2498,8 +2969,7 @@ fn dirAccessWindows(
2498 options: Dir.AccessOptions,2969 options: Dir.AccessOptions,
2499) Dir.AccessError!void {2970) Dir.AccessError!void {
2500 const t: *Threaded = @ptrCast(@alignCast(userdata));2971 const t: *Threaded = @ptrCast(@alignCast(userdata));
2501 const current_thread = Thread.getCurrent(t);2972 _ = t;
2502 try current_thread.checkCancel();
25032973
2504 _ = options; // TODO2974 _ = options; // TODO
25052975
...@@ -2525,16 +2995,21 @@ fn dirAccessWindows(...@@ -2525,16 +2995,21 @@ fn dirAccessWindows(
2525 .SecurityQualityOfService = null,2995 .SecurityQualityOfService = null,
2526 };2996 };
2527 var basic_info: windows.FILE.BASIC_INFORMATION = undefined;2997 var basic_info: windows.FILE.BASIC_INFORMATION = undefined;
2528 switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {2998 const syscall: Syscall = try .start();
2529 .SUCCESS => return,2999 while (true) switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
2530 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,3000 .SUCCESS => return syscall.finish(),
2531 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,3001 .CANCELLED => {
2532 .OBJECT_NAME_INVALID => |err| return windows.statusBug(err),3002 try syscall.checkCancel();
2533 .INVALID_PARAMETER => |err| return windows.statusBug(err),3003 continue;
2534 .ACCESS_DENIED => return error.AccessDenied,3004 },
2535 .OBJECT_PATH_SYNTAX_BAD => |err| return windows.statusBug(err),3005 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
2536 else => |rc| return windows.unexpectedStatus(rc),3006 .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
2537 }3007 .OBJECT_NAME_INVALID => |err| return syscall.ntstatusBug(err),
3008 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
3009 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
3010 .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
3011 else => |rc| return syscall.unexpectedNtstatus(rc),
3012 };
2538}3013}
25393014
2540const dirCreateFile = switch (native_os) {3015const dirCreateFile = switch (native_os) {
...@@ -2550,7 +3025,7 @@ fn dirCreateFilePosix(...@@ -2550,7 +3025,7 @@ fn dirCreateFilePosix(
2550 flags: File.CreateFlags,3025 flags: File.CreateFlags,
2551) File.OpenError!File {3026) File.OpenError!File {
2552 const t: *Threaded = @ptrCast(@alignCast(userdata));3027 const t: *Threaded = @ptrCast(@alignCast(userdata));
2553 const current_thread = Thread.getCurrent(t);3028 _ = t;
25543029
2555 var path_buffer: [posix.PATH_MAX]u8 = undefined;3030 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2556 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);3031 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
...@@ -2579,49 +3054,51 @@ fn dirCreateFilePosix(...@@ -2579,49 +3054,51 @@ fn dirCreateFilePosix(
2579 },3054 },
2580 };3055 };
25813056
2582 try current_thread.beginSyscall();3057 const fd: posix.fd_t = fd: {
2583 const fd: posix.fd_t = while (true) {3058 const syscall: Syscall = try .start();
2584 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, flags.permissions.toMode());3059 while (true) {
2585 switch (posix.errno(rc)) {3060 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, flags.permissions.toMode());
2586 .SUCCESS => {3061 switch (posix.errno(rc)) {
2587 current_thread.endSyscall();3062 .SUCCESS => {
2588 break @intCast(rc);3063 syscall.finish();
2589 },3064 break :fd @intCast(rc);
2590 .INTR => {3065 },
2591 try current_thread.checkCancel();3066 .INTR => {
2592 continue;3067 try syscall.checkCancel();
2593 },3068 continue;
2594 else => |e| {3069 },
2595 current_thread.endSyscall();3070 else => |e| {
2596 switch (e) {3071 syscall.finish();
2597 .FAULT => |err| return errnoBug(err),3072 switch (e) {
2598 .INVAL => return error.BadPathName,3073 .FAULT => |err| return errnoBug(err),
2599 .BADF => |err| return errnoBug(err), // File descriptor used after closed.3074 .INVAL => return error.BadPathName,
2600 .ACCES => return error.AccessDenied,3075 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
2601 .FBIG => return error.FileTooBig,3076 .ACCES => return error.AccessDenied,
2602 .OVERFLOW => return error.FileTooBig,3077 .FBIG => return error.FileTooBig,
2603 .ISDIR => return error.IsDir,3078 .OVERFLOW => return error.FileTooBig,
2604 .LOOP => return error.SymLinkLoop,3079 .ISDIR => return error.IsDir,
2605 .MFILE => return error.ProcessFdQuotaExceeded,3080 .LOOP => return error.SymLinkLoop,
2606 .NAMETOOLONG => return error.NameTooLong,3081 .MFILE => return error.ProcessFdQuotaExceeded,
2607 .NFILE => return error.SystemFdQuotaExceeded,3082 .NAMETOOLONG => return error.NameTooLong,
2608 .NODEV => return error.NoDevice,3083 .NFILE => return error.SystemFdQuotaExceeded,
2609 .NOENT => return error.FileNotFound,3084 .NODEV => return error.NoDevice,
2610 .SRCH => return error.FileNotFound, // Linux when accessing procfs.3085 .NOENT => return error.FileNotFound,
2611 .NOMEM => return error.SystemResources,3086 .SRCH => return error.FileNotFound, // Linux when accessing procfs.
2612 .NOSPC => return error.NoSpaceLeft,3087 .NOMEM => return error.SystemResources,
2613 .NOTDIR => return error.NotDir,3088 .NOSPC => return error.NoSpaceLeft,
2614 .PERM => return error.PermissionDenied,3089 .NOTDIR => return error.NotDir,
2615 .EXIST => return error.PathAlreadyExists,3090 .PERM => return error.PermissionDenied,
2616 .BUSY => return error.DeviceBusy,3091 .EXIST => return error.PathAlreadyExists,
2617 .OPNOTSUPP => return error.FileLocksUnsupported,3092 .BUSY => return error.DeviceBusy,
2618 .AGAIN => return error.WouldBlock,3093 .OPNOTSUPP => return error.FileLocksUnsupported,
2619 .TXTBSY => return error.FileBusy,3094 .AGAIN => return error.WouldBlock,
2620 .NXIO => return error.NoDevice,3095 .TXTBSY => return error.FileBusy,
2621 .ILSEQ => return error.BadPathName,3096 .NXIO => return error.NoDevice,
2622 else => |err| return posix.unexpectedErrno(err),3097 .ILSEQ => return error.BadPathName,
2623 }3098 else => |err| return posix.unexpectedErrno(err),
2624 },3099 }
3100 },
3101 }
2625 }3102 }
2626 };3103 };
2627 errdefer posix.close(fd);3104 errdefer posix.close(fd);
...@@ -2634,19 +3111,19 @@ fn dirCreateFilePosix(...@@ -2634,19 +3111,19 @@ fn dirCreateFilePosix(
2634 .exclusive => posix.LOCK.EX | lock_nonblocking,3111 .exclusive => posix.LOCK.EX | lock_nonblocking,
2635 };3112 };
26363113
2637 try current_thread.beginSyscall();3114 const syscall: Syscall = try .start();
2638 while (true) {3115 while (true) {
2639 switch (posix.errno(posix.system.flock(fd, lock_flags))) {3116 switch (posix.errno(posix.system.flock(fd, lock_flags))) {
2640 .SUCCESS => {3117 .SUCCESS => {
2641 current_thread.endSyscall();3118 syscall.finish();
2642 break;3119 break;
2643 },3120 },
2644 .INTR => {3121 .INTR => {
2645 try current_thread.checkCancel();3122 try syscall.checkCancel();
2646 continue;3123 continue;
2647 },3124 },
2648 else => |e| {3125 else => |e| {
2649 current_thread.endSyscall();3126 syscall.finish();
2650 switch (e) {3127 switch (e) {
2651 .BADF => |err| return errnoBug(err), // File descriptor used after closed.3128 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
2652 .INVAL => |err| return errnoBug(err), // invalid parameters3129 .INVAL => |err| return errnoBug(err), // invalid parameters
...@@ -2661,40 +3138,42 @@ fn dirCreateFilePosix(...@@ -2661,40 +3138,42 @@ fn dirCreateFilePosix(
2661 }3138 }
26623139
2663 if (have_flock_open_flags and flags.lock_nonblocking) {3140 if (have_flock_open_flags and flags.lock_nonblocking) {
2664 try current_thread.beginSyscall();3141 var fl_flags: usize = fl: {
2665 var fl_flags: usize = while (true) {3142 const syscall: Syscall = try .start();
2666 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));3143 while (true) {
2667 switch (posix.errno(rc)) {3144 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
2668 .SUCCESS => {3145 switch (posix.errno(rc)) {
2669 current_thread.endSyscall();3146 .SUCCESS => {
2670 break @intCast(rc);3147 syscall.finish();
2671 },3148 break :fl @intCast(rc);
2672 .INTR => {3149 },
2673 try current_thread.checkCancel();3150 .INTR => {
2674 continue;3151 try syscall.checkCancel();
2675 },3152 continue;
2676 else => |err| {3153 },
2677 current_thread.endSyscall();3154 else => |err| {
2678 return posix.unexpectedErrno(err);3155 syscall.finish();
2679 },3156 return posix.unexpectedErrno(err);
3157 },
3158 }
2680 }3159 }
2681 };3160 };
26823161
2683 fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));3162 fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));
26843163
2685 try current_thread.beginSyscall();3164 const syscall: Syscall = try .start();
2686 while (true) {3165 while (true) {
2687 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {3166 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {
2688 .SUCCESS => {3167 .SUCCESS => {
2689 current_thread.endSyscall();3168 syscall.finish();
2690 break;3169 break;
2691 },3170 },
2692 .INTR => {3171 .INTR => {
2693 try current_thread.checkCancel();3172 try syscall.checkCancel();
2694 continue;3173 continue;
2695 },3174 },
2696 else => |err| {3175 else => |err| {
2697 current_thread.endSyscall();3176 syscall.finish();
2698 return posix.unexpectedErrno(err);3177 return posix.unexpectedErrno(err);
2699 },3178 },
2700 }3179 }
...@@ -2712,28 +3191,41 @@ fn dirCreateFileWindows(...@@ -2712,28 +3191,41 @@ fn dirCreateFileWindows(
2712) File.OpenError!File {3191) File.OpenError!File {
2713 const w = windows;3192 const w = windows;
2714 const t: *Threaded = @ptrCast(@alignCast(userdata));3193 const t: *Threaded = @ptrCast(@alignCast(userdata));
2715 const current_thread = Thread.getCurrent(t);3194 _ = t;
2716 try current_thread.checkCancel();
27173195
2718 const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path);3196 const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path);
2719 const sub_path_w = sub_path_w_array.span();3197 const sub_path_w = sub_path_w_array.span();
27203198
2721 const handle = try w.OpenFile(sub_path_w, .{3199 const handle = handle: {
2722 .dir = dir.handle,3200 const syscall: Syscall = try .start();
2723 .access_mask = .{3201 while (true) {
2724 .STANDARD = .{ .SYNCHRONIZE = true },3202 if (w.OpenFile(sub_path_w, .{
2725 .GENERIC = .{3203 .dir = dir.handle,
2726 .WRITE = true,3204 .access_mask = .{
2727 .READ = flags.read,3205 .STANDARD = .{ .SYNCHRONIZE = true },
2728 },3206 .GENERIC = .{
2729 },3207 .WRITE = true,
2730 .creation = if (flags.exclusive)3208 .READ = flags.read,
2731 .CREATE3209 },
2732 else if (flags.truncate)3210 },
2733 .OVERWRITE_IF3211 .creation = if (flags.exclusive)
2734 else3212 .CREATE
2735 .OPEN_IF,3213 else if (flags.truncate)
2736 });3214 .OVERWRITE_IF
3215 else
3216 .OPEN_IF,
3217 })) |handle| {
3218 syscall.finish();
3219 break :handle handle;
3220 } else |err| switch (err) {
3221 error.OperationCanceled => {
3222 try syscall.checkCancel();
3223 continue;
3224 },
3225 else => |e| return syscall.fail(e),
3226 }
3227 }
3228 };
2737 errdefer w.CloseHandle(handle);3229 errdefer w.CloseHandle(handle);
27383230
2739 var io_status_block: w.IO_STATUS_BLOCK = undefined;3231 var io_status_block: w.IO_STATUS_BLOCK = undefined;
...@@ -2742,7 +3234,8 @@ fn dirCreateFileWindows(...@@ -2742,7 +3234,8 @@ fn dirCreateFileWindows(
2742 .shared => false,3234 .shared => false,
2743 .exclusive => true,3235 .exclusive => true,
2744 };3236 };
2745 const status = w.ntdll.NtLockFile(3237 const syscall: Syscall = try .start();
3238 while (true) switch (w.ntdll.NtLockFile(
2746 handle,3239 handle,
2747 null,3240 null,
2748 null,3241 null,
...@@ -2753,16 +3246,16 @@ fn dirCreateFileWindows(...@@ -2753,16 +3246,16 @@ fn dirCreateFileWindows(
2753 null,3246 null,
2754 @intFromBool(flags.lock_nonblocking),3247 @intFromBool(flags.lock_nonblocking),
2755 @intFromBool(exclusive),3248 @intFromBool(exclusive),
2756 );3249 )) {
2757 switch (status) {3250 .SUCCESS => {
2758 .SUCCESS => {},3251 syscall.finish();
2759 .INSUFFICIENT_RESOURCES => return error.SystemResources,3252 return .{ .handle = handle };
2760 .LOCK_NOT_GRANTED => return error.WouldBlock,3253 },
2761 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer3254 .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
2762 else => return windows.unexpectedStatus(status),3255 .LOCK_NOT_GRANTED => return syscall.fail(error.WouldBlock),
2763 }3256 .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
27643257 else => |status| return syscall.unexpectedNtstatus(status),
2765 return .{ .handle = handle };3258 };
2766}3259}
27673260
2768fn dirCreateFileWasi(3261fn dirCreateFileWasi(
...@@ -2772,7 +3265,7 @@ fn dirCreateFileWasi(...@@ -2772,7 +3265,7 @@ fn dirCreateFileWasi(
2772 flags: File.CreateFlags,3265 flags: File.CreateFlags,
2773) File.OpenError!File {3266) File.OpenError!File {
2774 const t: *Threaded = @ptrCast(@alignCast(userdata));3267 const t: *Threaded = @ptrCast(@alignCast(userdata));
2775 const current_thread = Thread.getCurrent(t);3268 _ = t;
2776 const wasi = std.os.wasi;3269 const wasi = std.os.wasi;
2777 const lookup_flags: wasi.lookupflags_t = .{};3270 const lookup_flags: wasi.lookupflags_t = .{};
2778 const oflags: wasi.oflags_t = .{3271 const oflags: wasi.oflags_t = .{
...@@ -2800,19 +3293,19 @@ fn dirCreateFileWasi(...@@ -2800,19 +3293,19 @@ fn dirCreateFileWasi(
2800 };3293 };
2801 const inheriting: wasi.rights_t = .{};3294 const inheriting: wasi.rights_t = .{};
2802 var fd: posix.fd_t = undefined;3295 var fd: posix.fd_t = undefined;
2803 try current_thread.beginSyscall();3296 const syscall: Syscall = try .start();
2804 while (true) {3297 while (true) {
2805 switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {3298 switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {
2806 .SUCCESS => {3299 .SUCCESS => {
2807 current_thread.endSyscall();3300 syscall.finish();
2808 return .{ .handle = fd };3301 return .{ .handle = fd };
2809 },3302 },
2810 .INTR => {3303 .INTR => {
2811 try current_thread.checkCancel();3304 try syscall.checkCancel();
2812 continue;3305 continue;
2813 },3306 },
2814 else => |e| {3307 else => |e| {
2815 current_thread.endSyscall();3308 syscall.finish();
2816 switch (e) {3309 switch (e) {
2817 .FAULT => |err| return errnoBug(err),3310 .FAULT => |err| return errnoBug(err),
2818 .INVAL => return error.BadPathName,3311 .INVAL => return error.BadPathName,
...@@ -2855,7 +3348,6 @@ fn dirOpenFilePosix(...@@ -2855,7 +3348,6 @@ fn dirOpenFilePosix(
2855 flags: File.OpenFlags,3348 flags: File.OpenFlags,
2856) File.OpenError!File {3349) File.OpenError!File {
2857 const t: *Threaded = @ptrCast(@alignCast(userdata));3350 const t: *Threaded = @ptrCast(@alignCast(userdata));
2858 const current_thread = Thread.getCurrent(t);
28593351
2860 var path_buffer: [posix.PATH_MAX]u8 = undefined;3352 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2861 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);3353 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
...@@ -2895,49 +3387,51 @@ fn dirOpenFilePosix(...@@ -2895,49 +3387,51 @@ fn dirOpenFilePosix(
2895 },3387 },
2896 };3388 };
28973389
2898 try current_thread.beginSyscall();3390 const fd: posix.fd_t = fd: {
2899 const fd: posix.fd_t = while (true) {3391 const syscall: Syscall = try .start();
2900 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0));3392 while (true) {
2901 switch (posix.errno(rc)) {3393 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0));
2902 .SUCCESS => {3394 switch (posix.errno(rc)) {
2903 current_thread.endSyscall();3395 .SUCCESS => {
2904 break @intCast(rc);3396 syscall.finish();
2905 },3397 break :fd @intCast(rc);
2906 .INTR => {3398 },
2907 try current_thread.checkCancel();3399 .INTR => {
2908 continue;3400 try syscall.checkCancel();
2909 },3401 continue;
2910 else => |e| {3402 },
2911 current_thread.endSyscall();3403 else => |e| {
2912 switch (e) {3404 syscall.finish();
2913 .FAULT => |err| return errnoBug(err),3405 switch (e) {
2914 .INVAL => return error.BadPathName,3406 .FAULT => |err| return errnoBug(err),
2915 .BADF => |err| return errnoBug(err), // File descriptor used after closed.3407 .INVAL => return error.BadPathName,
2916 .ACCES => return error.AccessDenied,3408 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
2917 .FBIG => return error.FileTooBig,3409 .ACCES => return error.AccessDenied,
2918 .OVERFLOW => return error.FileTooBig,3410 .FBIG => return error.FileTooBig,
2919 .ISDIR => return error.IsDir,3411 .OVERFLOW => return error.FileTooBig,
2920 .LOOP => return error.SymLinkLoop,3412 .ISDIR => return error.IsDir,
2921 .MFILE => return error.ProcessFdQuotaExceeded,3413 .LOOP => return error.SymLinkLoop,
2922 .NAMETOOLONG => return error.NameTooLong,3414 .MFILE => return error.ProcessFdQuotaExceeded,
2923 .NFILE => return error.SystemFdQuotaExceeded,3415 .NAMETOOLONG => return error.NameTooLong,
2924 .NODEV => return error.NoDevice,3416 .NFILE => return error.SystemFdQuotaExceeded,
2925 .NOENT => return error.FileNotFound,3417 .NODEV => return error.NoDevice,
2926 .SRCH => return error.FileNotFound, // Linux when opening procfs files.3418 .NOENT => return error.FileNotFound,
2927 .NOMEM => return error.SystemResources,3419 .SRCH => return error.FileNotFound, // Linux when opening procfs files.
2928 .NOSPC => return error.NoSpaceLeft,3420 .NOMEM => return error.SystemResources,
2929 .NOTDIR => return error.NotDir,3421 .NOSPC => return error.NoSpaceLeft,
2930 .PERM => return error.PermissionDenied,3422 .NOTDIR => return error.NotDir,
2931 .EXIST => return error.PathAlreadyExists,3423 .PERM => return error.PermissionDenied,
2932 .BUSY => return error.DeviceBusy,3424 .EXIST => return error.PathAlreadyExists,
2933 .OPNOTSUPP => return error.FileLocksUnsupported,3425 .BUSY => return error.DeviceBusy,
2934 .AGAIN => return error.WouldBlock,3426 .OPNOTSUPP => return error.FileLocksUnsupported,
2935 .TXTBSY => return error.FileBusy,3427 .AGAIN => return error.WouldBlock,
2936 .NXIO => return error.NoDevice,3428 .TXTBSY => return error.FileBusy,
2937 .ILSEQ => return error.BadPathName,3429 .NXIO => return error.NoDevice,
2938 else => |err| return posix.unexpectedErrno(err),3430 .ILSEQ => return error.BadPathName,
2939 }3431 else => |err| return posix.unexpectedErrno(err),
2940 },3432 }
3433 },
3434 }
2941 }3435 }
2942 };3436 };
2943 errdefer posix.close(fd);3437 errdefer posix.close(fd);
...@@ -2961,19 +3455,19 @@ fn dirOpenFilePosix(...@@ -2961,19 +3455,19 @@ fn dirOpenFilePosix(
2961 .shared => posix.LOCK.SH | lock_nonblocking,3455 .shared => posix.LOCK.SH | lock_nonblocking,
2962 .exclusive => posix.LOCK.EX | lock_nonblocking,3456 .exclusive => posix.LOCK.EX | lock_nonblocking,
2963 };3457 };
2964 try current_thread.beginSyscall();3458 const syscall: Syscall = try .start();
2965 while (true) {3459 while (true) {
2966 switch (posix.errno(posix.system.flock(fd, lock_flags))) {3460 switch (posix.errno(posix.system.flock(fd, lock_flags))) {
2967 .SUCCESS => {3461 .SUCCESS => {
2968 current_thread.endSyscall();3462 syscall.finish();
2969 break;3463 break;
2970 },3464 },
2971 .INTR => {3465 .INTR => {
2972 try current_thread.checkCancel();3466 try syscall.checkCancel();
2973 continue;3467 continue;
2974 },3468 },
2975 else => |e| {3469 else => |e| {
2976 current_thread.endSyscall();3470 syscall.finish();
2977 switch (e) {3471 switch (e) {
2978 .BADF => |err| return errnoBug(err), // File descriptor used after closed.3472 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
2979 .INVAL => |err| return errnoBug(err), // invalid parameters3473 .INVAL => |err| return errnoBug(err), // invalid parameters
...@@ -2988,40 +3482,42 @@ fn dirOpenFilePosix(...@@ -2988,40 +3482,42 @@ fn dirOpenFilePosix(
2988 }3482 }
29893483
2990 if (have_flock_open_flags and flags.lock_nonblocking) {3484 if (have_flock_open_flags and flags.lock_nonblocking) {
2991 try current_thread.beginSyscall();3485 var fl_flags: usize = fl: {
2992 var fl_flags: usize = while (true) {3486 const syscall: Syscall = try .start();
2993 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));3487 while (true) {
2994 switch (posix.errno(rc)) {3488 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
2995 .SUCCESS => {3489 switch (posix.errno(rc)) {
2996 current_thread.endSyscall();3490 .SUCCESS => {
2997 break @intCast(rc);3491 syscall.finish();
2998 },3492 break :fl @intCast(rc);
2999 .INTR => {3493 },
3000 try current_thread.checkCancel();3494 .INTR => {
3001 continue;3495 try syscall.checkCancel();
3002 },3496 continue;
3003 else => |err| {3497 },
3004 current_thread.endSyscall();3498 else => |err| {
3005 return posix.unexpectedErrno(err);3499 syscall.finish();
3006 },3500 return posix.unexpectedErrno(err);
3501 },
3502 }
3007 }3503 }
3008 };3504 };
30093505
3010 fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));3506 fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));
30113507
3012 try current_thread.beginSyscall();3508 const syscall: Syscall = try .start();
3013 while (true) {3509 while (true) {
3014 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {3510 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {
3015 .SUCCESS => {3511 .SUCCESS => {
3016 current_thread.endSyscall();3512 syscall.finish();
3017 break;3513 break;
3018 },3514 },
3019 .INTR => {3515 .INTR => {
3020 try current_thread.checkCancel();3516 try syscall.checkCancel();
3021 continue;3517 continue;
3022 },3518 },
3023 else => |err| {3519 else => |err| {
3024 current_thread.endSyscall();3520 syscall.finish();
3025 return posix.unexpectedErrno(err);3521 return posix.unexpectedErrno(err);
3026 },3522 },
3027 }3523 }
...@@ -3038,14 +3534,14 @@ fn dirOpenFileWindows(...@@ -3038,14 +3534,14 @@ fn dirOpenFileWindows(
3038 flags: File.OpenFlags,3534 flags: File.OpenFlags,
3039) File.OpenError!File {3535) File.OpenError!File {
3040 const t: *Threaded = @ptrCast(@alignCast(userdata));3536 const t: *Threaded = @ptrCast(@alignCast(userdata));
3537 _ = t;
3041 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);3538 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
3042 const sub_path_w = sub_path_w_array.span();3539 const sub_path_w = sub_path_w_array.span();
3043 const dir_handle = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;3540 const dir_handle = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
3044 return dirOpenFileWtf16(t, dir_handle, sub_path_w, flags);3541 return dirOpenFileWtf16(dir_handle, sub_path_w, flags);
3045}3542}
30463543
3047pub fn dirOpenFileWtf16(3544pub fn dirOpenFileWtf16(
3048 t: *Threaded,
3049 dir_handle: ?windows.HANDLE,3545 dir_handle: ?windows.HANDLE,
3050 sub_path_w: [:0]const u16,3546 sub_path_w: [:0]const u16,
3051 flags: File.OpenFlags,3547 flags: File.OpenFlags,
...@@ -3054,7 +3550,6 @@ pub fn dirOpenFileWtf16(...@@ -3054,7 +3550,6 @@ pub fn dirOpenFileWtf16(
3054 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;3550 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
3055 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;3551 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;
3056 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;3552 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
3057 const current_thread = Thread.getCurrent(t);
3058 const w = windows;3553 const w = windows;
30593554
3060 var nt_name: w.UNICODE_STRING = .{3555 var nt_name: w.UNICODE_STRING = .{
...@@ -3076,11 +3571,10 @@ pub fn dirOpenFileWtf16(...@@ -3076,11 +3571,10 @@ pub fn dirOpenFileWtf16(
3076 const max_attempts = 13;3571 const max_attempts = 13;
3077 var attempt: u5 = 0;3572 var attempt: u5 = 0;
30783573
3574 var syscall: Syscall = try .start();
3079 const handle = while (true) {3575 const handle = while (true) {
3080 try current_thread.checkCancel();
3081
3082 var result: w.HANDLE = undefined;3576 var result: w.HANDLE = undefined;
3083 const rc = w.ntdll.NtCreateFile(3577 switch (w.ntdll.NtCreateFile(
3084 &result,3578 &result,
3085 .{3579 .{
3086 .STANDARD = .{ .SYNCHRONIZE = true },3580 .STANDARD = .{ .SYNCHRONIZE = true },
...@@ -3102,49 +3596,59 @@ pub fn dirOpenFileWtf16(...@@ -3102,49 +3596,59 @@ pub fn dirOpenFileWtf16(
3102 },3596 },
3103 null,3597 null,
3104 0,3598 0,
3105 );3599 )) {
3106 switch (rc) {3600 .SUCCESS => {
3107 .SUCCESS => break result,3601 syscall.finish();
3108 .OBJECT_NAME_INVALID => return error.BadPathName,3602 break result;
3109 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,3603 },
3110 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,3604 .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
3111 .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found3605 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
3112 .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't3606 .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
3113 .NO_MEDIA_IN_DEVICE => return error.NoDevice,3607 .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
3114 .INVALID_PARAMETER => |err| return w.statusBug(err),3608 .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
3609 .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
3610 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
3611 .CANCELLED => {
3612 try syscall.checkCancel();
3613 continue;
3614 },
3115 .SHARING_VIOLATION => {3615 .SHARING_VIOLATION => {
3116 // This occurs if the file attempting to be opened is a running3616 // This occurs if the file attempting to be opened is a running
3117 // executable. However, there's a kernel bug: the error may be3617 // executable. However, there's a kernel bug: the error may be
3118 // incorrectly returned for an indeterminate amount of time3618 // incorrectly returned for an indeterminate amount of time
3119 // after an executable file is closed. Here we work around the3619 // after an executable file is closed. Here we work around the
3120 // kernel bug with retry attempts.3620 // kernel bug with retry attempts.
3621 syscall.finish();
3121 if (max_attempts - attempt == 0) return error.SharingViolation;3622 if (max_attempts - attempt == 0) return error.SharingViolation;
3122 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);3623 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
3123 attempt += 1;3624 attempt += 1;
3625 syscall = try .start();
3124 continue;3626 continue;
3125 },3627 },
3126 .ACCESS_DENIED => return error.AccessDenied,3628 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
3127 .PIPE_BUSY => return error.PipeBusy,3629 .PIPE_BUSY => return syscall.fail(error.PipeBusy),
3128 .PIPE_NOT_AVAILABLE => return error.NoDevice,3630 .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
3129 .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),3631 .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
3130 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,3632 .OBJECT_NAME_COLLISION => return syscall.fail(error.PathAlreadyExists),
3131 .FILE_IS_A_DIRECTORY => return error.IsDir,3633 .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
3132 .NOT_A_DIRECTORY => return error.NotDir,3634 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
3133 .USER_MAPPED_FILE => return error.AccessDenied,3635 .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
3134 .INVALID_HANDLE => |err| return w.statusBug(err),3636 .INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
3135 .DELETE_PENDING => {3637 .DELETE_PENDING => {
3136 // This error means that there *was* a file in this location on3638 // This error means that there *was* a file in this location on
3137 // the file system, but it was deleted. However, the OS is not3639 // the file system, but it was deleted. However, the OS is not
3138 // finished with the deletion operation, and so this CreateFile3640 // finished with the deletion operation, and so this CreateFile
3139 // call has failed. Here, we simulate the kernel bug being3641 // call has failed. Here, we simulate the kernel bug being
3140 // fixed by sleeping and retrying until the error goes away.3642 // fixed by sleeping and retrying until the error goes away.
3643 syscall.finish();
3141 if (max_attempts - attempt == 0) return error.SharingViolation;3644 if (max_attempts - attempt == 0) return error.SharingViolation;
3142 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);3645 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
3143 attempt += 1;3646 attempt += 1;
3647 syscall = try .start();
3144 continue;3648 continue;
3145 },3649 },
3146 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,3650 .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
3147 else => return w.unexpectedStatus(rc),3651 else => |rc| return syscall.unexpectedNtstatus(rc),
3148 }3652 }
3149 };3653 };
3150 errdefer w.CloseHandle(handle);3654 errdefer w.CloseHandle(handle);
...@@ -3154,7 +3658,8 @@ pub fn dirOpenFileWtf16(...@@ -3154,7 +3658,8 @@ pub fn dirOpenFileWtf16(
3154 .shared => false,3658 .shared => false,
3155 .exclusive => true,3659 .exclusive => true,
3156 };3660 };
3157 const status = w.ntdll.NtLockFile(3661 syscall = try .start();
3662 while (true) switch (w.ntdll.NtLockFile(
3158 handle,3663 handle,
3159 null,3664 null,
3160 null,3665 null,
...@@ -3165,14 +3670,13 @@ pub fn dirOpenFileWtf16(...@@ -3165,14 +3670,13 @@ pub fn dirOpenFileWtf16(
3165 null,3670 null,
3166 @intFromBool(flags.lock_nonblocking),3671 @intFromBool(flags.lock_nonblocking),
3167 @intFromBool(exclusive),3672 @intFromBool(exclusive),
3168 );3673 )) {
3169 switch (status) {3674 .SUCCESS => break syscall.finish(),
3170 .SUCCESS => {},3675 .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
3171 .INSUFFICIENT_RESOURCES => return error.SystemResources,3676 .LOCK_NOT_GRANTED => return syscall.fail(error.WouldBlock),
3172 .LOCK_NOT_GRANTED => return error.WouldBlock,3677 .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
3173 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer3678 else => |status| return syscall.unexpectedNtstatus(status),
3174 else => return windows.unexpectedStatus(status),3679 };
3175 }
3176 return .{ .handle = handle };3680 return .{ .handle = handle };
3177}3681}
31783682
...@@ -3184,7 +3688,6 @@ fn dirOpenFileWasi(...@@ -3184,7 +3688,6 @@ fn dirOpenFileWasi(
3184) File.OpenError!File {3688) File.OpenError!File {
3185 if (builtin.link_libc) return dirOpenFilePosix(userdata, dir, sub_path, flags);3689 if (builtin.link_libc) return dirOpenFilePosix(userdata, dir, sub_path, flags);
3186 const t: *Threaded = @ptrCast(@alignCast(userdata));3690 const t: *Threaded = @ptrCast(@alignCast(userdata));
3187 const current_thread = Thread.getCurrent(t);
3188 const wasi = std.os.wasi;3691 const wasi = std.os.wasi;
3189 var base: std.os.wasi.rights_t = .{};3692 var base: std.os.wasi.rights_t = .{};
3190 // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or FD_WRITE3693 // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or FD_WRITE
...@@ -3214,19 +3717,19 @@ fn dirOpenFileWasi(...@@ -3214,19 +3717,19 @@ fn dirOpenFileWasi(
3214 const inheriting: wasi.rights_t = .{};3717 const inheriting: wasi.rights_t = .{};
3215 const fdflags: wasi.fdflags_t = .{};3718 const fdflags: wasi.fdflags_t = .{};
3216 var fd: posix.fd_t = undefined;3719 var fd: posix.fd_t = undefined;
3217 try current_thread.beginSyscall();3720 const syscall: Syscall = try .start();
3218 while (true) {3721 while (true) {
3219 switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {3722 switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {
3220 .SUCCESS => {3723 .SUCCESS => {
3221 current_thread.endSyscall();3724 syscall.finish();
3222 break;3725 break;
3223 },3726 },
3224 .INTR => {3727 .INTR => {
3225 try current_thread.checkCancel();3728 try syscall.checkCancel();
3226 continue;3729 continue;
3227 },3730 },
3228 else => |e| {3731 else => |e| {
3229 current_thread.endSyscall();3732 syscall.finish();
3230 switch (e) {3733 switch (e) {
3231 .FAULT => |err| return errnoBug(err),3734 .FAULT => |err| return errnoBug(err),
3232 .BADF => |err| return errnoBug(err), // File descriptor used after closed.3735 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -3283,14 +3786,13 @@ fn dirOpenDirPosix(...@@ -3283,14 +3786,13 @@ fn dirOpenDirPosix(
3283 options: Dir.OpenOptions,3786 options: Dir.OpenOptions,
3284) Dir.OpenError!Dir {3787) Dir.OpenError!Dir {
3285 const t: *Threaded = @ptrCast(@alignCast(userdata));3788 const t: *Threaded = @ptrCast(@alignCast(userdata));
3789 _ = t;
32863790
3287 if (is_windows) {3791 if (is_windows) {
3288 const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);3792 const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
3289 return dirOpenDirWindows(t, dir, sub_path_w.span(), options);3793 return dirOpenDirWindows(dir, sub_path_w.span(), options);
3290 }3794 }
32913795
3292 const current_thread = Thread.getCurrent(t);
3293
3294 var path_buffer: [posix.PATH_MAX]u8 = undefined;3796 var path_buffer: [posix.PATH_MAX]u8 = undefined;
3295 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);3797 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
32963798
...@@ -3311,20 +3813,20 @@ fn dirOpenDirPosix(...@@ -3311,20 +3813,20 @@ fn dirOpenDirPosix(
3311 if (@hasField(posix.O, "PATH") and !options.iterate)3813 if (@hasField(posix.O, "PATH") and !options.iterate)
3312 flags.PATH = true;3814 flags.PATH = true;
33133815
3314 try current_thread.beginSyscall();3816 const syscall: Syscall = try .start();
3315 while (true) {3817 while (true) {
3316 const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0));3818 const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0));
3317 switch (posix.errno(rc)) {3819 switch (posix.errno(rc)) {
3318 .SUCCESS => {3820 .SUCCESS => {
3319 current_thread.endSyscall();3821 syscall.finish();
3320 return .{ .handle = @intCast(rc) };3822 return .{ .handle = @intCast(rc) };
3321 },3823 },
3322 .INTR => {3824 .INTR => {
3323 try current_thread.checkCancel();3825 try syscall.checkCancel();
3324 continue;3826 continue;
3325 },3827 },
3326 else => |e| {3828 else => |e| {
3327 current_thread.endSyscall();3829 syscall.finish();
3328 switch (e) {3830 switch (e) {
3329 .FAULT => |err| return errnoBug(err),3831 .FAULT => |err| return errnoBug(err),
3330 .INVAL => return error.BadPathName,3832 .INVAL => return error.BadPathName,
...@@ -3356,27 +3858,27 @@ fn dirOpenDirHaiku(...@@ -3356,27 +3858,27 @@ fn dirOpenDirHaiku(
3356 options: Dir.OpenOptions,3858 options: Dir.OpenOptions,
3357) Dir.OpenError!Dir {3859) Dir.OpenError!Dir {
3358 const t: *Threaded = @ptrCast(@alignCast(userdata));3860 const t: *Threaded = @ptrCast(@alignCast(userdata));
3359 const current_thread = Thread.getCurrent(t);3861 _ = t;
33603862
3361 var path_buffer: [posix.PATH_MAX]u8 = undefined;3863 var path_buffer: [posix.PATH_MAX]u8 = undefined;
3362 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);3864 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
33633865
3364 _ = options;3866 _ = options;
33653867
3366 try current_thread.beginSyscall();3868 const syscall: Syscall = try .start();
3367 while (true) {3869 while (true) {
3368 const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix);3870 const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix);
3369 if (rc >= 0) {3871 if (rc >= 0) {
3370 current_thread.endSyscall();3872 syscall.finish();
3371 return .{ .handle = rc };3873 return .{ .handle = rc };
3372 }3874 }
3373 switch (@as(posix.E, @enumFromInt(rc))) {3875 switch (@as(posix.E, @enumFromInt(rc))) {
3374 .INTR => {3876 .INTR => {
3375 try current_thread.checkCancel();3877 try syscall.checkCancel();
3376 continue;3878 continue;
3377 },3879 },
3378 else => |e| {3880 else => |e| {
3379 current_thread.endSyscall();3881 syscall.finish();
3380 switch (e) {3882 switch (e) {
3381 .FAULT => |err| return errnoBug(err),3883 .FAULT => |err| return errnoBug(err),
3382 .INVAL => |err| return errnoBug(err),3884 .INVAL => |err| return errnoBug(err),
...@@ -3400,12 +3902,10 @@ fn dirOpenDirHaiku(...@@ -3400,12 +3902,10 @@ fn dirOpenDirHaiku(
3400}3902}
34013903
3402pub fn dirOpenDirWindows(3904pub fn dirOpenDirWindows(
3403 t: *Io.Threaded,
3404 dir: Dir,3905 dir: Dir,
3405 sub_path_w: [:0]const u16,3906 sub_path_w: [:0]const u16,
3406 options: Dir.OpenOptions,3907 options: Dir.OpenOptions,
3407) Dir.OpenError!Dir {3908) Dir.OpenError!Dir {
3408 const current_thread = Thread.getCurrent(t);
3409 const w = windows;3909 const w = windows;
34103910
3411 const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);3911 const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
...@@ -3416,8 +3916,9 @@ pub fn dirOpenDirWindows(...@@ -3416,8 +3916,9 @@ pub fn dirOpenDirWindows(
3416 };3916 };
3417 var io_status_block: w.IO_STATUS_BLOCK = undefined;3917 var io_status_block: w.IO_STATUS_BLOCK = undefined;
3418 var result: Dir = .{ .handle = undefined };3918 var result: Dir = .{ .handle = undefined };
3419 try current_thread.checkCancel();3919
3420 const rc = w.ntdll.NtCreateFile(3920 const syscall: Syscall = try .start();
3921 while (true) switch (w.ntdll.NtCreateFile(
3421 &result.handle,3922 &result.handle,
3422 // TODO remove some of these flags if options.access_sub_paths is false3923 // TODO remove some of these flags if options.access_sub_paths is false
3423 .{3924 .{
...@@ -3453,21 +3954,26 @@ pub fn dirOpenDirWindows(...@@ -3453,21 +3954,26 @@ pub fn dirOpenDirWindows(
3453 },3954 },
3454 null,3955 null,
3455 0,3956 0,
3456 );3957 )) {
34573958 .SUCCESS => {
3458 switch (rc) {3959 syscall.finish();
3459 .SUCCESS => return result,3960 return result;
3460 .OBJECT_NAME_INVALID => return error.BadPathName,3961 },
3461 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,3962 .CANCELLED => {
3963 try syscall.checkCancel();
3964 continue;
3965 },
3966 .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
3967 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
3462 .OBJECT_NAME_COLLISION => |err| return w.statusBug(err),3968 .OBJECT_NAME_COLLISION => |err| return w.statusBug(err),
3463 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,3969 .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
3464 .NOT_A_DIRECTORY => return error.NotDir,3970 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
3465 // This can happen if the directory has 'List folder contents' permission set to 'Deny'3971 // This can happen if the directory has 'List folder contents' permission set to 'Deny'
3466 // and the directory is trying to be opened for iteration.3972 // and the directory is trying to be opened for iteration.
3467 .ACCESS_DENIED => return error.AccessDenied,3973 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
3468 .INVALID_PARAMETER => |err| return w.statusBug(err),3974 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
3469 else => return w.unexpectedStatus(rc),3975 else => |rc| return syscall.unexpectedNtstatus(rc),
3470 }3976 };
3471}3977}
34723978
3473fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {3979fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
...@@ -3490,7 +3996,7 @@ const dirRead = switch (native_os) {...@@ -3490,7 +3996,7 @@ const dirRead = switch (native_os) {
3490fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {3996fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
3491 const linux = std.os.linux;3997 const linux = std.os.linux;
3492 const t: *Threaded = @ptrCast(@alignCast(userdata));3998 const t: *Threaded = @ptrCast(@alignCast(userdata));
3493 const current_thread = Thread.getCurrent(t);3999 _ = t;
3494 var buffer_index: usize = 0;4000 var buffer_index: usize = 0;
3495 while (buffer.len - buffer_index != 0) {4001 while (buffer.len - buffer_index != 0) {
3496 if (dr.end - dr.index == 0) {4002 if (dr.end - dr.index == 0) {
...@@ -3498,26 +4004,26 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir...@@ -3498,26 +4004,26 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
3498 // buffered data.4004 // buffered data.
3499 if (buffer_index != 0) break;4005 if (buffer_index != 0) break;
3500 if (dr.state == .reset) {4006 if (dr.state == .reset) {
3501 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {4007 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
3502 error.Unseekable => return error.Unexpected,4008 error.Unseekable => return error.Unexpected,
3503 else => |e| return e,4009 else => |e| return e,
3504 };4010 };
3505 dr.state = .reading;4011 dr.state = .reading;
3506 }4012 }
3507 try current_thread.beginSyscall();4013 const syscall: Syscall = try .start();
3508 const n = while (true) {4014 const n = while (true) {
3509 const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);4015 const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
3510 switch (linux.errno(rc)) {4016 switch (linux.errno(rc)) {
3511 .SUCCESS => {4017 .SUCCESS => {
3512 current_thread.endSyscall();4018 syscall.finish();
3513 break rc;4019 break rc;
3514 },4020 },
3515 .INTR => {4021 .INTR => {
3516 try current_thread.checkCancel();4022 try syscall.checkCancel();
3517 continue;4023 continue;
3518 },4024 },
3519 else => |e| {4025 else => |e| {
3520 current_thread.endSyscall();4026 syscall.finish();
3521 switch (e) {4027 switch (e) {
3522 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.4028 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
3523 .FAULT => |err| return errnoBug(err),4029 .FAULT => |err| return errnoBug(err),
...@@ -3587,7 +4093,7 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir...@@ -3587,7 +4093,7 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
35874093
3588fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {4094fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
3589 const t: *Threaded = @ptrCast(@alignCast(userdata));4095 const t: *Threaded = @ptrCast(@alignCast(userdata));
3590 const current_thread = Thread.getCurrent(t);4096 _ = t;
3591 const Header = extern struct {4097 const Header = extern struct {
3592 seek: i64,4098 seek: i64,
3593 };4099 };
...@@ -3606,27 +4112,27 @@ fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Di...@@ -3606,27 +4112,27 @@ fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Di
3606 // buffered data.4112 // buffered data.
3607 if (buffer_index != 0) break;4113 if (buffer_index != 0) break;
3608 if (dr.state == .reset) {4114 if (dr.state == .reset) {
3609 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {4115 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
3610 error.Unseekable => return error.Unexpected,4116 error.Unseekable => return error.Unexpected,
3611 else => |e| return e,4117 else => |e| return e,
3612 };4118 };
3613 dr.state = .reading;4119 dr.state = .reading;
3614 }4120 }
3615 const dents_buffer = dr.buffer[header_end..];4121 const dents_buffer = dr.buffer[header_end..];
3616 try current_thread.beginSyscall();4122 const syscall: Syscall = try .start();
3617 const n: usize = while (true) {4123 const n: usize = while (true) {
3618 const rc = posix.system.getdirentries(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, &header.seek);4124 const rc = posix.system.getdirentries(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, &header.seek);
3619 switch (posix.errno(rc)) {4125 switch (posix.errno(rc)) {
3620 .SUCCESS => {4126 .SUCCESS => {
3621 current_thread.endSyscall();4127 syscall.finish();
3622 break @intCast(rc);4128 break @intCast(rc);
3623 },4129 },
3624 .INTR => {4130 .INTR => {
3625 try current_thread.checkCancel();4131 try syscall.checkCancel();
3626 continue;4132 continue;
3627 },4133 },
3628 else => |e| {4134 else => |e| {
3629 current_thread.endSyscall();4135 syscall.finish();
3630 switch (e) {4136 switch (e) {
3631 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.4137 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
3632 .FAULT => |err| return errnoBug(err),4138 .FAULT => |err| return errnoBug(err),
...@@ -3675,7 +4181,7 @@ fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Di...@@ -3675,7 +4181,7 @@ fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Di
36754181
3676fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {4182fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
3677 const t: *Threaded = @ptrCast(@alignCast(userdata));4183 const t: *Threaded = @ptrCast(@alignCast(userdata));
3678 const current_thread = Thread.getCurrent(t);4184 _ = t;
3679 var buffer_index: usize = 0;4185 var buffer_index: usize = 0;
3680 while (buffer.len - buffer_index != 0) {4186 while (buffer.len - buffer_index != 0) {
3681 if (dr.end - dr.index == 0) {4187 if (dr.end - dr.index == 0) {
...@@ -3683,26 +4189,26 @@ fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.R...@@ -3683,26 +4189,26 @@ fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.R
3683 // buffered data.4189 // buffered data.
3684 if (buffer_index != 0) break;4190 if (buffer_index != 0) break;
3685 if (dr.state == .reset) {4191 if (dr.state == .reset) {
3686 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {4192 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
3687 error.Unseekable => return error.Unexpected,4193 error.Unseekable => return error.Unexpected,
3688 else => |e| return e,4194 else => |e| return e,
3689 };4195 };
3690 dr.state = .reading;4196 dr.state = .reading;
3691 }4197 }
3692 try current_thread.beginSyscall();4198 const syscall: Syscall = try .start();
3693 const n: usize = while (true) {4199 const n: usize = while (true) {
3694 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);4200 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
3695 switch (posix.errno(rc)) {4201 switch (posix.errno(rc)) {
3696 .SUCCESS => {4202 .SUCCESS => {
3697 current_thread.endSyscall();4203 syscall.finish();
3698 break @intCast(rc);4204 break @intCast(rc);
3699 },4205 },
3700 .INTR => {4206 .INTR => {
3701 try current_thread.checkCancel();4207 try syscall.checkCancel();
3702 continue;4208 continue;
3703 },4209 },
3704 else => |e| {4210 else => |e| {
3705 current_thread.endSyscall();4211 syscall.finish();
3706 switch (e) {4212 switch (e) {
3707 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability4213 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability
3708 .FAULT => |err| return errnoBug(err),4214 .FAULT => |err| return errnoBug(err),
...@@ -3769,7 +4275,7 @@ fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.R...@@ -3769,7 +4275,7 @@ fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.R
37694275
3770fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {4276fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
3771 const t: *Threaded = @ptrCast(@alignCast(userdata));4277 const t: *Threaded = @ptrCast(@alignCast(userdata));
3772 const current_thread = Thread.getCurrent(t);4278 _ = t;
3773 var buffer_index: usize = 0;4279 var buffer_index: usize = 0;
3774 while (buffer.len - buffer_index != 0) {4280 while (buffer.len - buffer_index != 0) {
3775 if (dr.end - dr.index == 0) {4281 if (dr.end - dr.index == 0) {
...@@ -3777,26 +4283,26 @@ fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D...@@ -3777,26 +4283,26 @@ fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D
3777 // buffered data.4283 // buffered data.
3778 if (buffer_index != 0) break;4284 if (buffer_index != 0) break;
3779 if (dr.state == .reset) {4285 if (dr.state == .reset) {
3780 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {4286 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
3781 error.Unseekable => return error.Unexpected,4287 error.Unseekable => return error.Unexpected,
3782 else => |e| return e,4288 else => |e| return e,
3783 };4289 };
3784 dr.state = .reading;4290 dr.state = .reading;
3785 }4291 }
3786 try current_thread.beginSyscall();4292 const syscall: Syscall = try .start();
3787 const n: usize = while (true) {4293 const n: usize = while (true) {
3788 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);4294 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
3789 switch (posix.errno(rc)) {4295 switch (posix.errno(rc)) {
3790 .SUCCESS => {4296 .SUCCESS => {
3791 current_thread.endSyscall();4297 syscall.finish();
3792 break rc;4298 break rc;
3793 },4299 },
3794 .INTR => {4300 .INTR => {
3795 try current_thread.checkCancel();4301 try syscall.checkCancel();
3796 continue;4302 continue;
3797 },4303 },
3798 else => |e| {4304 else => |e| {
3799 current_thread.endSyscall();4305 syscall.finish();
3800 switch (e) {4306 switch (e) {
3801 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability4307 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability
3802 .FAULT => |err| return errnoBug(err),4308 .FAULT => |err| return errnoBug(err),
...@@ -3822,7 +4328,7 @@ fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D...@@ -3822,7 +4328,7 @@ fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D
3822 if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;4328 if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
38234329
3824 // illumos dirent doesn't expose type, so we have to call stat to get it.4330 // illumos dirent doesn't expose type, so we have to call stat to get it.
3825 const stat = try posixStatFile(current_thread, dr.dir.handle, name, posix.AT.SYMLINK_NOFOLLOW);4331 const stat = try posixStatFile(dr.dir.handle, name, posix.AT.SYMLINK_NOFOLLOW);
38264332
3827 buffer[buffer_index] = .{4333 buffer[buffer_index] = .{
3828 .name = name,4334 .name = name,
...@@ -3843,7 +4349,7 @@ fn dirReadHaiku(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir...@@ -3843,7 +4349,7 @@ fn dirReadHaiku(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
38434349
3844fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {4350fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
3845 const t: *Threaded = @ptrCast(@alignCast(userdata));4351 const t: *Threaded = @ptrCast(@alignCast(userdata));
3846 const current_thread = Thread.getCurrent(t);4352 _ = t;
3847 const w = windows;4353 const w = windows;
38484354
3849 // We want to be able to use the `dr.buffer` for both the NtQueryDirectoryFile call (which4355 // We want to be able to use the `dr.buffer` for both the NtQueryDirectoryFile call (which
...@@ -3907,9 +4413,9 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D...@@ -3907,9 +4413,9 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D
3907 // buffered data.4413 // buffered data.
3908 if (buffer_index != 0) break;4414 if (buffer_index != 0) break;
39094415
3910 try current_thread.checkCancel();
3911 var io_status_block: w.IO_STATUS_BLOCK = undefined;4416 var io_status_block: w.IO_STATUS_BLOCK = undefined;
3912 const rc = w.ntdll.NtQueryDirectoryFile(4417 const syscall: Syscall = try .start();
4418 const rc = while (true) switch (w.ntdll.NtQueryDirectoryFile(
3913 dr.dir.handle,4419 dr.dir.handle,
3914 null,4420 null,
3915 null,4421 null,
...@@ -3921,7 +4427,16 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D...@@ -3921,7 +4427,16 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D
3921 w.FALSE,4427 w.FALSE,
3922 null,4428 null,
3923 @intFromBool(dr.state == .reset),4429 @intFromBool(dr.state == .reset),
3924 );4430 )) {
4431 .CANCELLED => {
4432 try syscall.checkCancel();
4433 continue;
4434 },
4435 else => |rc| {
4436 syscall.finish();
4437 break rc;
4438 },
4439 };
3925 dr.state = .reading;4440 dr.state = .reading;
3926 if (io_status_block.Information == 0) {4441 if (io_status_block.Information == 0) {
3927 dr.state = .finished;4442 dr.state = .finished;
...@@ -3993,7 +4508,7 @@ fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir....@@ -3993,7 +4508,7 @@ fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.
3993 // complexity here.4508 // complexity here.
3994 const wasi = std.os.wasi;4509 const wasi = std.os.wasi;
3995 const t: *Threaded = @ptrCast(@alignCast(userdata));4510 const t: *Threaded = @ptrCast(@alignCast(userdata));
3996 const current_thread = Thread.getCurrent(t);4511 _ = t;
3997 const Header = extern struct {4512 const Header = extern struct {
3998 cookie: u64,4513 cookie: u64,
3999 };4514 };
...@@ -4019,19 +4534,19 @@ fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir....@@ -4019,19 +4534,19 @@ fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.
4019 }4534 }
4020 const dents_buffer = dr.buffer[header_end..];4535 const dents_buffer = dr.buffer[header_end..];
4021 var n: usize = undefined;4536 var n: usize = undefined;
4022 try current_thread.beginSyscall();4537 const syscall: Syscall = try .start();
4023 while (true) {4538 while (true) {
4024 switch (wasi.fd_readdir(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, header.cookie, &n)) {4539 switch (wasi.fd_readdir(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, header.cookie, &n)) {
4025 .SUCCESS => {4540 .SUCCESS => {
4026 current_thread.endSyscall();4541 syscall.finish();
4027 break;4542 break;
4028 },4543 },
4029 .INTR => {4544 .INTR => {
4030 try current_thread.checkCancel();4545 try syscall.checkCancel();
4031 continue;4546 continue;
4032 },4547 },
4033 else => |e| {4548 else => |e| {
4034 current_thread.endSyscall();4549 syscall.finish();
4035 switch (e) {4550 switch (e) {
4036 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.4551 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
4037 .FAULT => |err| return errnoBug(err),4552 .FAULT => |err| return errnoBug(err),
...@@ -4107,34 +4622,42 @@ const dirRealPathFile = switch (native_os) {...@@ -4107,34 +4622,42 @@ const dirRealPathFile = switch (native_os) {
41074622
4108fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize {4623fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize {
4109 const t: *Threaded = @ptrCast(@alignCast(userdata));4624 const t: *Threaded = @ptrCast(@alignCast(userdata));
4110 const current_thread = Thread.getCurrent(t);4625 _ = t;
4111
4112 try current_thread.checkCancel();
41134626
4114 var path_name_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);4627 var path_name_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
41154628
4116 const h_file = blk: {4629 const h_file = handle: {
4117 const res = windows.OpenFile(path_name_w.span(), .{4630 const syscall: Syscall = try .start();
4118 .dir = dir.handle,4631 while (true) {
4119 .access_mask = .{4632 if (windows.OpenFile(path_name_w.span(), .{
4120 .GENERIC = .{ .READ = true },4633 .dir = dir.handle,
4121 .STANDARD = .{ .SYNCHRONIZE = true },4634 .access_mask = .{
4122 },4635 .GENERIC = .{ .READ = true },
4123 .creation = .OPEN,4636 .STANDARD = .{ .SYNCHRONIZE = true },
4124 .filter = .any,4637 },
4125 }) catch |err| switch (err) {4638 .creation = .OPEN,
4126 error.WouldBlock => unreachable,4639 .filter = .any,
4127 else => |e| return e,4640 })) |handle| {
4128 };4641 syscall.finish();
4129 break :blk res;4642 break :handle handle;
4643 } else |err| switch (err) {
4644 error.WouldBlock => unreachable,
4645 error.OperationCanceled => {
4646 try syscall.checkCancel();
4647 continue;
4648 },
4649 else => |e| return syscall.fail(e),
4650 }
4651 }
4130 };4652 };
4131 defer windows.CloseHandle(h_file);4653 defer windows.CloseHandle(h_file);
4132 return realPathWindows(current_thread, h_file, out_buffer);4654 return realPathWindows(h_file, out_buffer);
4133}4655}
41344656
4135fn realPathWindows(current_thread: *Thread, h_file: windows.HANDLE, out_buffer: []u8) File.RealPathError!usize {4657fn realPathWindows(h_file: windows.HANDLE, out_buffer: []u8) File.RealPathError!usize {
4136 _ = current_thread; // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks
4137 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;4658 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
4659 // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks
4660 try Thread.checkCancel();
4138 const wide_slice = try windows.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);4661 const wide_slice = try windows.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
41394662
4140 const len = std.unicode.calcWtf8Len(wide_slice);4663 const len = std.unicode.calcWtf8Len(wide_slice);
...@@ -4148,26 +4671,26 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o...@@ -4148,26 +4671,26 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o
4148 if (native_os == .wasi) return error.OperationUnsupported;4671 if (native_os == .wasi) return error.OperationUnsupported;
41494672
4150 const t: *Threaded = @ptrCast(@alignCast(userdata));4673 const t: *Threaded = @ptrCast(@alignCast(userdata));
4151 const current_thread = Thread.getCurrent(t);4674 _ = t;
41524675
4153 var path_buffer: [posix.PATH_MAX]u8 = undefined;4676 var path_buffer: [posix.PATH_MAX]u8 = undefined;
4154 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);4677 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
41554678
4156 if (builtin.link_libc and dir.handle == posix.AT.FDCWD) {4679 if (builtin.link_libc and dir.handle == posix.AT.FDCWD) {
4157 if (out_buffer.len < posix.PATH_MAX) return error.NameTooLong;4680 if (out_buffer.len < posix.PATH_MAX) return error.NameTooLong;
4158 try current_thread.beginSyscall();4681 const syscall: Syscall = try .start();
4159 while (true) {4682 while (true) {
4160 if (std.c.realpath(sub_path_posix, out_buffer.ptr)) |redundant_pointer| {4683 if (std.c.realpath(sub_path_posix, out_buffer.ptr)) |redundant_pointer| {
4161 current_thread.endSyscall();4684 syscall.finish();
4162 assert(redundant_pointer == out_buffer.ptr);4685 assert(redundant_pointer == out_buffer.ptr);
4163 return std.mem.indexOfScalar(u8, out_buffer, 0) orelse out_buffer.len;4686 return std.mem.indexOfScalar(u8, out_buffer, 0) orelse out_buffer.len;
4164 }4687 }
4165 const err: posix.E = @enumFromInt(std.c._errno().*);4688 const err: posix.E = @enumFromInt(std.c._errno().*);
4166 if (err == .INTR) {4689 if (err == .INTR) {
4167 try current_thread.checkCancel();4690 try syscall.checkCancel();
4168 continue;4691 continue;
4169 }4692 }
4170 current_thread.endSyscall();4693 syscall.finish();
4171 switch (err) {4694 switch (err) {
4172 .INVAL => return errnoBug(err),4695 .INVAL => return errnoBug(err),
4173 .BADF => return errnoBug(err),4696 .BADF => return errnoBug(err),
...@@ -4191,20 +4714,20 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o...@@ -4191,20 +4714,20 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o
41914714
4192 const mode: posix.mode_t = 0;4715 const mode: posix.mode_t = 0;
41934716
4194 try current_thread.beginSyscall();4717 const syscall: Syscall = try .start();
4195 const fd: posix.fd_t = while (true) {4718 const fd: posix.fd_t = while (true) {
4196 const rc = openat_sym(dir.handle, sub_path_posix, flags, mode);4719 const rc = openat_sym(dir.handle, sub_path_posix, flags, mode);
4197 switch (posix.errno(rc)) {4720 switch (posix.errno(rc)) {
4198 .SUCCESS => {4721 .SUCCESS => {
4199 current_thread.endSyscall();4722 syscall.finish();
4200 break @intCast(rc);4723 break @intCast(rc);
4201 },4724 },
4202 .INTR => {4725 .INTR => {
4203 try current_thread.checkCancel();4726 try syscall.checkCancel();
4204 continue;4727 continue;
4205 },4728 },
4206 else => |e| {4729 else => |e| {
4207 current_thread.endSyscall();4730 syscall.finish();
4208 switch (e) {4731 switch (e) {
4209 .FAULT => |err| return errnoBug(err),4732 .FAULT => |err| return errnoBug(err),
4210 .INVAL => return error.BadPathName,4733 .INVAL => return error.BadPathName,
...@@ -4234,7 +4757,7 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o...@@ -4234,7 +4757,7 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o
4234 }4757 }
4235 };4758 };
4236 defer posix.close(fd);4759 defer posix.close(fd);
4237 return realPathPosix(current_thread, fd, out_buffer);4760 return realPathPosix(fd, out_buffer);
4238}4761}
42394762
4240const dirRealPath = switch (native_os) {4763const dirRealPath = switch (native_os) {
...@@ -4245,14 +4768,14 @@ const dirRealPath = switch (native_os) {...@@ -4245,14 +4768,14 @@ const dirRealPath = switch (native_os) {
4245fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {4768fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {
4246 if (native_os == .wasi) return error.OperationUnsupported;4769 if (native_os == .wasi) return error.OperationUnsupported;
4247 const t: *Threaded = @ptrCast(@alignCast(userdata));4770 const t: *Threaded = @ptrCast(@alignCast(userdata));
4248 const current_thread = Thread.getCurrent(t);4771 _ = t;
4249 return realPathPosix(current_thread, dir.handle, out_buffer);4772 return realPathPosix(dir.handle, out_buffer);
4250}4773}
42514774
4252fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {4775fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {
4253 const t: *Threaded = @ptrCast(@alignCast(userdata));4776 const t: *Threaded = @ptrCast(@alignCast(userdata));
4254 const current_thread = Thread.getCurrent(t);4777 _ = t;
4255 return realPathWindows(current_thread, dir.handle, out_buffer);4778 return realPathWindows(dir.handle, out_buffer);
4256}4779}
42574780
4258const fileRealPath = switch (native_os) {4781const fileRealPath = switch (native_os) {
...@@ -4263,35 +4786,35 @@ const fileRealPath = switch (native_os) {...@@ -4263,35 +4786,35 @@ const fileRealPath = switch (native_os) {
4263fn fileRealPathWindows(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {4786fn fileRealPathWindows(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
4264 if (native_os == .wasi) return error.OperationUnsupported;4787 if (native_os == .wasi) return error.OperationUnsupported;
4265 const t: *Threaded = @ptrCast(@alignCast(userdata));4788 const t: *Threaded = @ptrCast(@alignCast(userdata));
4266 const current_thread = Thread.getCurrent(t);4789 _ = t;
4267 return realPathWindows(current_thread, file.handle, out_buffer);4790 return realPathWindows(file.handle, out_buffer);
4268}4791}
42694792
4270fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {4793fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
4271 if (native_os == .wasi) return error.OperationUnsupported;4794 if (native_os == .wasi) return error.OperationUnsupported;
4272 const t: *Threaded = @ptrCast(@alignCast(userdata));4795 const t: *Threaded = @ptrCast(@alignCast(userdata));
4273 const current_thread = Thread.getCurrent(t);4796 _ = t;
4274 return realPathPosix(current_thread, file.handle, out_buffer);4797 return realPathPosix(file.handle, out_buffer);
4275}4798}
42764799
4277fn realPathPosix(current_thread: *Thread, fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize {4800fn realPathPosix(fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize {
4278 switch (native_os) {4801 switch (native_os) {
4279 .netbsd, .dragonfly, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {4802 .netbsd, .dragonfly, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
4280 var sufficient_buffer: [posix.PATH_MAX]u8 = undefined;4803 var sufficient_buffer: [posix.PATH_MAX]u8 = undefined;
4281 @memset(&sufficient_buffer, 0);4804 @memset(&sufficient_buffer, 0);
4282 try current_thread.beginSyscall();4805 const syscall: Syscall = try .start();
4283 while (true) {4806 while (true) {
4284 switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, &sufficient_buffer))) {4807 switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, &sufficient_buffer))) {
4285 .SUCCESS => {4808 .SUCCESS => {
4286 current_thread.endSyscall();4809 syscall.finish();
4287 break;4810 break;
4288 },4811 },
4289 .INTR => {4812 .INTR => {
4290 try current_thread.checkCancel();4813 try syscall.checkCancel();
4291 continue;4814 continue;
4292 },4815 },
4293 else => |e| {4816 else => |e| {
4294 current_thread.endSyscall();4817 syscall.finish();
4295 switch (e) {4818 switch (e) {
4296 .ACCES => return error.AccessDenied,4819 .ACCES => return error.AccessDenied,
4297 .BADF => return error.FileNotFound,4820 .BADF => return error.FileNotFound,
...@@ -4313,21 +4836,21 @@ fn realPathPosix(current_thread: *Thread, fd: posix.fd_t, out_buffer: []u8) File...@@ -4313,21 +4836,21 @@ fn realPathPosix(current_thread: *Thread, fd: posix.fd_t, out_buffer: []u8) File
4313 var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;4836 var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
4314 const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}";4837 const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}";
4315 const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, template, .{fd}, 0) catch unreachable;4838 const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, template, .{fd}, 0) catch unreachable;
4316 try current_thread.beginSyscall();4839 const syscall: Syscall = try .start();
4317 while (true) {4840 while (true) {
4318 const rc = posix.system.readlink(proc_path, out_buffer.ptr, out_buffer.len);4841 const rc = posix.system.readlink(proc_path, out_buffer.ptr, out_buffer.len);
4319 switch (posix.errno(rc)) {4842 switch (posix.errno(rc)) {
4320 .SUCCESS => {4843 .SUCCESS => {
4321 current_thread.endSyscall();4844 syscall.finish();
4322 const len: usize = @bitCast(rc);4845 const len: usize = @bitCast(rc);
4323 return len;4846 return len;
4324 },4847 },
4325 .INTR => {4848 .INTR => {
4326 try current_thread.checkCancel();4849 try syscall.checkCancel();
4327 continue;4850 continue;
4328 },4851 },
4329 else => |e| {4852 else => |e| {
4330 current_thread.endSyscall();4853 syscall.finish();
4331 switch (e) {4854 switch (e) {
4332 .ACCES => return error.AccessDenied,4855 .ACCES => return error.AccessDenied,
4333 .FAULT => |err| return errnoBug(err),4856 .FAULT => |err| return errnoBug(err),
...@@ -4347,23 +4870,23 @@ fn realPathPosix(current_thread: *Thread, fd: posix.fd_t, out_buffer: []u8) File...@@ -4347,23 +4870,23 @@ fn realPathPosix(current_thread: *Thread, fd: posix.fd_t, out_buffer: []u8) File
4347 .freebsd => {4870 .freebsd => {
4348 var k_file: std.c.kinfo_file = undefined;4871 var k_file: std.c.kinfo_file = undefined;
4349 k_file.structsize = std.c.KINFO_FILE_SIZE;4872 k_file.structsize = std.c.KINFO_FILE_SIZE;
4350 try current_thread.beginSyscall();4873 const syscall: Syscall = try .start();
4351 while (true) {4874 while (true) {
4352 switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&k_file)))) {4875 switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&k_file)))) {
4353 .SUCCESS => {4876 .SUCCESS => {
4354 current_thread.endSyscall();4877 syscall.finish();
4355 break;4878 break;
4356 },4879 },
4357 .INTR => {4880 .INTR => {
4358 try current_thread.checkCancel();4881 try syscall.checkCancel();
4359 continue;4882 continue;
4360 },4883 },
4361 .BADF => {4884 .BADF => {
4362 current_thread.endSyscall();4885 syscall.finish();
4363 return error.FileNotFound;4886 return error.FileNotFound;
4364 },4887 },
4365 else => |err| {4888 else => |err| {
4366 current_thread.endSyscall();4889 syscall.finish();
4367 return posix.unexpectedErrno(err);4890 return posix.unexpectedErrno(err);
4368 },4891 },
4369 }4892 }
...@@ -4394,21 +4917,21 @@ fn dirDeleteFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) D...@@ -4394,21 +4917,21 @@ fn dirDeleteFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) D
4394fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {4917fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
4395 if (builtin.link_libc) return dirDeleteFilePosix(userdata, dir, sub_path);4918 if (builtin.link_libc) return dirDeleteFilePosix(userdata, dir, sub_path);
4396 const t: *Threaded = @ptrCast(@alignCast(userdata));4919 const t: *Threaded = @ptrCast(@alignCast(userdata));
4397 const current_thread = Thread.getCurrent(t);4920 _ = t;
4398 try current_thread.beginSyscall();4921 const syscall: Syscall = try .start();
4399 while (true) {4922 while (true) {
4400 const res = std.os.wasi.path_unlink_file(dir.handle, sub_path.ptr, sub_path.len);4923 const res = std.os.wasi.path_unlink_file(dir.handle, sub_path.ptr, sub_path.len);
4401 switch (res) {4924 switch (res) {
4402 .SUCCESS => {4925 .SUCCESS => {
4403 current_thread.endSyscall();4926 syscall.finish();
4404 return;4927 return;
4405 },4928 },
4406 .INTR => {4929 .INTR => {
4407 try current_thread.checkCancel();4930 try syscall.checkCancel();
4408 continue;4931 continue;
4409 },4932 },
4410 else => |e| {4933 else => |e| {
4411 current_thread.endSyscall();4934 syscall.finish();
4412 switch (e) {4935 switch (e) {
4413 .ACCES => return error.AccessDenied,4936 .ACCES => return error.AccessDenied,
4414 .PERM => return error.PermissionDenied,4937 .PERM => return error.PermissionDenied,
...@@ -4435,20 +4958,20 @@ fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir....@@ -4435,20 +4958,20 @@ fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.
44354958
4436fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {4959fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
4437 const t: *Threaded = @ptrCast(@alignCast(userdata));4960 const t: *Threaded = @ptrCast(@alignCast(userdata));
4438 const current_thread = Thread.getCurrent(t);4961 _ = t;
44394962
4440 var path_buffer: [posix.PATH_MAX]u8 = undefined;4963 var path_buffer: [posix.PATH_MAX]u8 = undefined;
4441 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);4964 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
44424965
4443 try current_thread.beginSyscall();4966 const syscall: Syscall = try .start();
4444 while (true) {4967 while (true) {
4445 switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, 0))) {4968 switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, 0))) {
4446 .SUCCESS => {4969 .SUCCESS => {
4447 current_thread.endSyscall();4970 syscall.finish();
4448 return;4971 return;
4449 },4972 },
4450 .INTR => {4973 .INTR => {
4451 try current_thread.checkCancel();4974 try syscall.checkCancel();
4452 continue;4975 continue;
4453 },4976 },
4454 // Some systems return permission errors when trying to delete a4977 // Some systems return permission errors when trying to delete a
...@@ -4460,15 +4983,15 @@ fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir...@@ -4460,15 +4983,15 @@ fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir
4460 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them).4983 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them).
4461 var st = std.mem.zeroes(posix.Stat);4984 var st = std.mem.zeroes(posix.Stat);
4462 while (true) {4985 while (true) {
4463 try current_thread.checkCancel();4986 try syscall.checkCancel();
4464 switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &st, posix.AT.SYMLINK_NOFOLLOW))) {4987 switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &st, posix.AT.SYMLINK_NOFOLLOW))) {
4465 .SUCCESS => {4988 .SUCCESS => {
4466 current_thread.endSyscall();4989 syscall.finish();
4467 break;4990 break;
4468 },4991 },
4469 .INTR => continue,4992 .INTR => continue,
4470 else => {4993 else => {
4471 current_thread.endSyscall();4994 syscall.finish();
4472 return error.PermissionDenied;4995 return error.PermissionDenied;
4473 },4996 },
4474 }4997 }
...@@ -4480,12 +5003,12 @@ fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir...@@ -4480,12 +5003,12 @@ fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir
4480 return error.PermissionDenied;5003 return error.PermissionDenied;
4481 },5004 },
4482 else => {5005 else => {
4483 current_thread.endSyscall();5006 syscall.finish();
4484 return error.PermissionDenied;5007 return error.PermissionDenied;
4485 },5008 },
4486 },5009 },
4487 else => |e| {5010 else => |e| {
4488 current_thread.endSyscall();5011 syscall.finish();
4489 switch (e) {5012 switch (e) {
4490 .ACCES => return error.AccessDenied,5013 .ACCES => return error.AccessDenied,
4491 .BUSY => return error.FileBusy,5014 .BUSY => return error.FileBusy,
...@@ -4525,74 +5048,74 @@ fn dirDeleteDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Di...@@ -4525,74 +5048,74 @@ fn dirDeleteDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Di
45255048
4526fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) (Dir.DeleteDirError || Dir.DeleteFileError)!void {5049fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) (Dir.DeleteDirError || Dir.DeleteFileError)!void {
4527 const t: *Threaded = @ptrCast(@alignCast(userdata));5050 const t: *Threaded = @ptrCast(@alignCast(userdata));
4528 const current_thread = Thread.getCurrent(t);5051 _ = t;
4529 const w = windows;5052 const w = windows;
45305053
4531 try current_thread.checkCancel();
4532
4533 const sub_path_w_buf = try w.sliceToPrefixedFileW(dir.handle, sub_path);5054 const sub_path_w_buf = try w.sliceToPrefixedFileW(dir.handle, sub_path);
4534 const sub_path_w = sub_path_w_buf.span();5055 const sub_path_w = sub_path_w_buf.span();
45355056
4536 const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));5057 const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
4537 var nt_name: w.UNICODE_STRING = .{5058 var nt_name: w.UNICODE_STRING = .{
4538 .Length = path_len_bytes,5059 .Length = path_len_bytes,
4539 .MaximumLength = path_len_bytes,5060 .MaximumLength = path_len_bytes,
4540 // The Windows API makes this mutable, but it will not mutate here.5061 // The Windows API makes this mutable, but it will not mutate here.
4541 .Buffer = @constCast(sub_path_w.ptr),5062 .Buffer = @constCast(sub_path_w.ptr),
4542 };5063 };
45435064
4544 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {5065 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
4545 // Windows does not recognize this, but it does work with empty string.5066 // Windows does not recognize this, but it does work with empty string.
4546 nt_name.Length = 0;5067 nt_name.Length = 0;
4547 }5068 }
4548 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {5069 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
4549 // Can't remove the parent directory with an open handle.5070 // Can't remove the parent directory with an open handle.
4550 return error.FileBusy;5071 return error.FileBusy;
4551 }5072 }
45525073
4553 var io_status_block: w.IO_STATUS_BLOCK = undefined;5074 var io_status_block: w.IO_STATUS_BLOCK = undefined;
4554 var tmp_handle: w.HANDLE = undefined;5075 var tmp_handle: w.HANDLE = undefined;
4555 var rc = w.ntdll.NtCreateFile(5076 {
4556 &tmp_handle,5077 const syscall: Syscall = try .start();
4557 .{ .STANDARD = .{5078 while (true) switch (w.ntdll.NtCreateFile(
4558 .RIGHTS = .{ .DELETE = true },5079 &tmp_handle,
4559 .SYNCHRONIZE = true,5080 .{ .STANDARD = .{
4560 } },5081 .RIGHTS = .{ .DELETE = true },
4561 &.{5082 .SYNCHRONIZE = true,
4562 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),5083 } },
4563 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,5084 &.{
4564 .Attributes = .{},5085 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
4565 .ObjectName = &nt_name,5086 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
4566 .SecurityDescriptor = null,5087 .Attributes = .{},
4567 .SecurityQualityOfService = null,5088 .ObjectName = &nt_name,
4568 },5089 .SecurityDescriptor = null,
4569 &io_status_block,5090 .SecurityQualityOfService = null,
4570 null,5091 },
4571 .{},5092 &io_status_block,
4572 .VALID_FLAGS,5093 null,
4573 .OPEN,5094 .{},
4574 .{5095 .VALID_FLAGS,
4575 .DIRECTORY_FILE = remove_dir,5096 .OPEN,
4576 .NON_DIRECTORY_FILE = !remove_dir,5097 .{
4577 .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead?5098 .DIRECTORY_FILE = remove_dir,
4578 },5099 .NON_DIRECTORY_FILE = !remove_dir,
4579 null,5100 .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead?
4580 0,5101 },
4581 );5102 null,
4582 switch (rc) {5103 0,
4583 .SUCCESS => {},5104 )) {
4584 .OBJECT_NAME_INVALID => |err| return w.statusBug(err),5105 .SUCCESS => break syscall.finish(),
4585 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,5106 .OBJECT_NAME_INVALID => |err| return syscall.ntstatusBug(err),
4586 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,5107 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
4587 .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found5108 .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
4588 .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't5109 .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
4589 .INVALID_PARAMETER => |err| return w.statusBug(err),5110 .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
4590 .FILE_IS_A_DIRECTORY => return error.IsDir,5111 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
4591 .NOT_A_DIRECTORY => return error.NotDir,5112 .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
4592 .SHARING_VIOLATION => return error.FileBusy,5113 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
4593 .ACCESS_DENIED => return error.AccessDenied,5114 .SHARING_VIOLATION => return syscall.fail(error.FileBusy),
4594 .DELETE_PENDING => return,5115 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
4595 else => return w.unexpectedStatus(rc),5116 .DELETE_PENDING => return syscall.finish(),
5117 else => |rc| return syscall.unexpectedNtstatus(rc),
5118 };
4596 }5119 }
4597 defer w.CloseHandle(tmp_handle);5120 defer w.CloseHandle(tmp_handle);
45985121
...@@ -4607,9 +5130,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov...@@ -4607,9 +5130,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
4607 //5130 //
4608 // The strategy here is just to try using FileDispositionInformationEx and fall back to5131 // The strategy here is just to try using FileDispositionInformationEx and fall back to
4609 // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.5132 // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
4610 const need_fallback = need_fallback: {5133 const rc = rc: {
4611 try current_thread.checkCancel();
4612
4613 // Deletion with posix semantics if the filesystem supports it.5134 // Deletion with posix semantics if the filesystem supports it.
4614 const info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{5135 const info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
4615 .DELETE = true,5136 .DELETE = true,
...@@ -4617,29 +5138,32 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov...@@ -4617,29 +5138,32 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
4617 .IGNORE_READONLY_ATTRIBUTE = true,5138 .IGNORE_READONLY_ATTRIBUTE = true,
4618 } };5139 } };
46195140
4620 rc = w.ntdll.NtSetInformationFile(5141 const syscall: Syscall = try .start();
5142 while (true) switch (w.ntdll.NtSetInformationFile(
4621 tmp_handle,5143 tmp_handle,
4622 &io_status_block,5144 &io_status_block,
4623 &info,5145 &info,
4624 @sizeOf(w.FILE.DISPOSITION.INFORMATION.EX),5146 @sizeOf(w.FILE.DISPOSITION.INFORMATION.EX),
4625 .DispositionEx,5147 .DispositionEx,
4626 );5148 )) {
4627 switch (rc) {5149 .CANCELLED => {
4628 .SUCCESS => return,5150 try syscall.checkCancel();
5151 continue;
5152 },
4629 // The filesystem does not support FileDispositionInformationEx5153 // The filesystem does not support FileDispositionInformationEx
4630 .INVALID_PARAMETER,5154 .INVALID_PARAMETER,
4631 // The operating system does not support FileDispositionInformationEx5155 // The operating system does not support FileDispositionInformationEx
4632 .INVALID_INFO_CLASS,5156 .INVALID_INFO_CLASS,
4633 // The operating system does not support one of the flags5157 // The operating system does not support one of the flags
4634 .NOT_SUPPORTED,5158 .NOT_SUPPORTED,
4635 => break :need_fallback true,5159 => break, // use fallback path below; `syscall` still active
4636 // For all other statuses, fall down to the switch below to handle them.
4637 else => break :need_fallback false,
4638 }
4639 };
46405160
4641 if (need_fallback) {5161 // For all other statuses, fall down to the switch below to handle them.
4642 try current_thread.checkCancel();5162 else => |rc| {
5163 syscall.finish();
5164 break :rc rc;
5165 },
5166 };
46435167
4644 // Deletion with file pending semantics, which requires waiting or moving5168 // Deletion with file pending semantics, which requires waiting or moving
4645 // files to get them removed (from here).5169 // files to get them removed (from here).
...@@ -4647,14 +5171,23 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov...@@ -4647,14 +5171,23 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
4647 .DeleteFile = w.TRUE,5171 .DeleteFile = w.TRUE,
4648 };5172 };
46495173
4650 rc = w.ntdll.NtSetInformationFile(5174 while (true) switch (w.ntdll.NtSetInformationFile(
4651 tmp_handle,5175 tmp_handle,
4652 &io_status_block,5176 &io_status_block,
4653 &file_dispo,5177 &file_dispo,
4654 @sizeOf(w.FILE.DISPOSITION.INFORMATION),5178 @sizeOf(w.FILE.DISPOSITION.INFORMATION),
4655 .Disposition,5179 .Disposition,
4656 );5180 )) {
4657 }5181 .CANCELLED => {
5182 try syscall.checkCancel();
5183 continue;
5184 },
5185 else => |rc| {
5186 syscall.finish();
5187 break :rc rc;
5188 },
5189 };
5190 };
4658 switch (rc) {5191 switch (rc) {
4659 .SUCCESS => {},5192 .SUCCESS => {},
4660 .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,5193 .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
...@@ -4670,22 +5203,22 @@ fn dirDeleteDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.D...@@ -4670,22 +5203,22 @@ fn dirDeleteDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.D
4670 if (builtin.link_libc) return dirDeleteDirPosix(userdata, dir, sub_path);5203 if (builtin.link_libc) return dirDeleteDirPosix(userdata, dir, sub_path);
46715204
4672 const t: *Threaded = @ptrCast(@alignCast(userdata));5205 const t: *Threaded = @ptrCast(@alignCast(userdata));
4673 const current_thread = Thread.getCurrent(t);5206 _ = t;
46745207
4675 try current_thread.beginSyscall();5208 const syscall: Syscall = try .start();
4676 while (true) {5209 while (true) {
4677 const res = std.os.wasi.path_remove_directory(dir.handle, sub_path.ptr, sub_path.len);5210 const res = std.os.wasi.path_remove_directory(dir.handle, sub_path.ptr, sub_path.len);
4678 switch (res) {5211 switch (res) {
4679 .SUCCESS => {5212 .SUCCESS => {
4680 current_thread.endSyscall();5213 syscall.finish();
4681 return;5214 return;
4682 },5215 },
4683 .INTR => {5216 .INTR => {
4684 try current_thread.checkCancel();5217 try syscall.checkCancel();
4685 continue;5218 continue;
4686 },5219 },
4687 else => |e| {5220 else => |e| {
4688 current_thread.endSyscall();5221 syscall.finish();
4689 switch (e) {5222 switch (e) {
4690 .ACCES => return error.AccessDenied,5223 .ACCES => return error.AccessDenied,
4691 .PERM => return error.PermissionDenied,5224 .PERM => return error.PermissionDenied,
...@@ -4712,24 +5245,24 @@ fn dirDeleteDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.D...@@ -4712,24 +5245,24 @@ fn dirDeleteDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.D
47125245
4713fn dirDeleteDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {5246fn dirDeleteDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
4714 const t: *Threaded = @ptrCast(@alignCast(userdata));5247 const t: *Threaded = @ptrCast(@alignCast(userdata));
4715 const current_thread = Thread.getCurrent(t);5248 _ = t;
47165249
4717 var path_buffer: [posix.PATH_MAX]u8 = undefined;5250 var path_buffer: [posix.PATH_MAX]u8 = undefined;
4718 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);5251 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
47195252
4720 try current_thread.beginSyscall();5253 const syscall: Syscall = try .start();
4721 while (true) {5254 while (true) {
4722 switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, posix.AT.REMOVEDIR))) {5255 switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, posix.AT.REMOVEDIR))) {
4723 .SUCCESS => {5256 .SUCCESS => {
4724 current_thread.endSyscall();5257 syscall.finish();
4725 return;5258 return;
4726 },5259 },
4727 .INTR => {5260 .INTR => {
4728 try current_thread.checkCancel();5261 try syscall.checkCancel();
4729 continue;5262 continue;
4730 },5263 },
4731 else => |e| {5264 else => |e| {
4732 current_thread.endSyscall();5265 syscall.finish();
4733 switch (e) {5266 switch (e) {
4734 .ACCES => return error.AccessDenied,5267 .ACCES => return error.AccessDenied,
4735 .PERM => return error.PermissionDenied,5268 .PERM => return error.PermissionDenied,
...@@ -4770,7 +5303,7 @@ fn dirRenameWindows(...@@ -4770,7 +5303,7 @@ fn dirRenameWindows(
4770) Dir.RenameError!void {5303) Dir.RenameError!void {
4771 const w = windows;5304 const w = windows;
4772 const t: *Threaded = @ptrCast(@alignCast(userdata));5305 const t: *Threaded = @ptrCast(@alignCast(userdata));
4773 const current_thread = Thread.getCurrent(t);5306 _ = t;
47745307
4775 const old_path_w_buf = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path);5308 const old_path_w_buf = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path);
4776 const old_path_w = old_path_w_buf.span();5309 const old_path_w = old_path_w_buf.span();
...@@ -4778,23 +5311,33 @@ fn dirRenameWindows(...@@ -4778,23 +5311,33 @@ fn dirRenameWindows(
4778 const new_path_w = new_path_w_buf.span();5311 const new_path_w = new_path_w_buf.span();
4779 const replace_if_exists = true;5312 const replace_if_exists = true;
47805313
4781 try current_thread.checkCancel();5314 const src_fd = src_fd: {
47825315 const syscall: Syscall = try .start();
4783 const src_fd = w.OpenFile(old_path_w, .{5316 while (true) {
4784 .dir = old_dir.handle,5317 if (w.OpenFile(old_path_w, .{
4785 .access_mask = .{5318 .dir = old_dir.handle,
4786 .GENERIC = .{ .WRITE = true },5319 .access_mask = .{
4787 .STANDARD = .{5320 .GENERIC = .{ .WRITE = true },
4788 .RIGHTS = .{ .DELETE = true },5321 .STANDARD = .{
4789 .SYNCHRONIZE = true,5322 .RIGHTS = .{ .DELETE = true },
4790 },5323 .SYNCHRONIZE = true,
4791 },5324 },
4792 .creation = .OPEN,5325 },
4793 .filter = .any, // This function is supposed to rename both files and directories.5326 .creation = .OPEN,
4794 .follow_symlinks = false,5327 .filter = .any, // This function is supposed to rename both files and directories.
4795 }) catch |err| switch (err) {5328 .follow_symlinks = false,
4796 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.5329 })) |handle| {
4797 else => |e| return e,5330 syscall.finish();
5331 break :src_fd handle;
5332 } else |err| switch (err) {
5333 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
5334 error.OperationCanceled => {
5335 try syscall.checkCancel();
5336 continue;
5337 },
5338 else => |e| return e,
5339 }
5340 }
4798 };5341 };
4799 defer w.CloseHandle(src_fd);5342 defer w.CloseHandle(src_fd);
48005343
...@@ -4887,18 +5430,18 @@ fn dirRenameWasi(...@@ -4887,18 +5430,18 @@ fn dirRenameWasi(
4887 if (builtin.link_libc) return dirRenamePosix(userdata, old_dir, old_sub_path, new_dir, new_sub_path);5430 if (builtin.link_libc) return dirRenamePosix(userdata, old_dir, old_sub_path, new_dir, new_sub_path);
48885431
4889 const t: *Threaded = @ptrCast(@alignCast(userdata));5432 const t: *Threaded = @ptrCast(@alignCast(userdata));
4890 const current_thread = Thread.getCurrent(t);5433 _ = t;
48915434
4892 try current_thread.beginSyscall();5435 const syscall: Syscall = try .start();
4893 while (true) {5436 while (true) {
4894 switch (std.os.wasi.path_rename(old_dir.handle, old_sub_path.ptr, old_sub_path.len, new_dir.handle, new_sub_path.ptr, new_sub_path.len)) {5437 switch (std.os.wasi.path_rename(old_dir.handle, old_sub_path.ptr, old_sub_path.len, new_dir.handle, new_sub_path.ptr, new_sub_path.len)) {
4895 .SUCCESS => return current_thread.endSyscall(),5438 .SUCCESS => return syscall.finish(),
4896 .INTR => {5439 .INTR => {
4897 try current_thread.checkCancel();5440 try syscall.checkCancel();
4898 continue;5441 continue;
4899 },5442 },
4900 else => |e| {5443 else => |e| {
4901 current_thread.endSyscall();5444 syscall.finish();
4902 switch (e) {5445 switch (e) {
4903 .ACCES => return error.AccessDenied,5446 .ACCES => return error.AccessDenied,
4904 .PERM => return error.PermissionDenied,5447 .PERM => return error.PermissionDenied,
...@@ -4935,7 +5478,7 @@ fn dirRenamePosix(...@@ -4935,7 +5478,7 @@ fn dirRenamePosix(
4935 new_sub_path: []const u8,5478 new_sub_path: []const u8,
4936) Dir.RenameError!void {5479) Dir.RenameError!void {
4937 const t: *Threaded = @ptrCast(@alignCast(userdata));5480 const t: *Threaded = @ptrCast(@alignCast(userdata));
4938 const current_thread = Thread.getCurrent(t);5481 _ = t;
49395482
4940 var old_path_buffer: [posix.PATH_MAX]u8 = undefined;5483 var old_path_buffer: [posix.PATH_MAX]u8 = undefined;
4941 var new_path_buffer: [posix.PATH_MAX]u8 = undefined;5484 var new_path_buffer: [posix.PATH_MAX]u8 = undefined;
...@@ -4943,16 +5486,16 @@ fn dirRenamePosix(...@@ -4943,16 +5486,16 @@ fn dirRenamePosix(
4943 const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);5486 const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
4944 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);5487 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
49455488
4946 try current_thread.beginSyscall();5489 const syscall: Syscall = try .start();
4947 while (true) {5490 while (true) {
4948 switch (posix.errno(posix.system.renameat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix))) {5491 switch (posix.errno(posix.system.renameat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix))) {
4949 .SUCCESS => return current_thread.endSyscall(),5492 .SUCCESS => return syscall.finish(),
4950 .INTR => {5493 .INTR => {
4951 try current_thread.checkCancel();5494 try syscall.checkCancel();
4952 continue;5495 continue;
4953 },5496 },
4954 else => |e| {5497 else => |e| {
4955 current_thread.endSyscall();5498 syscall.finish();
4956 switch (e) {5499 switch (e) {
4957 .ACCES => return error.AccessDenied,5500 .ACCES => return error.AccessDenied,
4958 .PERM => return error.PermissionDenied,5501 .PERM => return error.PermissionDenied,
...@@ -4994,11 +5537,9 @@ fn dirSymLinkWindows(...@@ -4994,11 +5537,9 @@ fn dirSymLinkWindows(
4994 flags: Dir.SymLinkFlags,5537 flags: Dir.SymLinkFlags,
4995) Dir.SymLinkError!void {5538) Dir.SymLinkError!void {
4996 const t: *Threaded = @ptrCast(@alignCast(userdata));5539 const t: *Threaded = @ptrCast(@alignCast(userdata));
4997 const current_thread = Thread.getCurrent(t);5540 _ = t;
4998 const w = windows;5541 const w = windows;
49995542
5000 try current_thread.checkCancel();
5001
5002 // Target path does not use sliceToPrefixedFileW because certain paths5543 // Target path does not use sliceToPrefixedFileW because certain paths
5003 // are handled differently when creating a symlink than they would be5544 // are handled differently when creating a symlink than they would be
5004 // when converting to an NT namespaced path. CreateSymbolicLink in5545 // when converting to an NT namespaced path. CreateSymbolicLink in
...@@ -5028,22 +5569,34 @@ fn dirSymLinkWindows(...@@ -5028,22 +5569,34 @@ fn dirSymLinkWindows(
5028 Flags: w.ULONG,5569 Flags: w.ULONG,
5029 };5570 };
50305571
5031 const symlink_handle = w.OpenFile(sym_link_path_w.span(), .{5572 const symlink_handle = handle: {
5032 .access_mask = .{5573 const syscall: Syscall = try .start();
5033 .GENERIC = .{ .READ = true, .WRITE = true },5574 while (true) {
5034 .STANDARD = .{ .SYNCHRONIZE = true },5575 if (w.OpenFile(sym_link_path_w.span(), .{
5035 },5576 .access_mask = .{
5036 .dir = dir.handle,5577 .GENERIC = .{ .READ = true, .WRITE = true },
5037 .creation = .CREATE,5578 .STANDARD = .{ .SYNCHRONIZE = true },
5038 .filter = if (flags.is_directory) .dir_only else .non_directory_only,5579 },
5039 }) catch |err| switch (err) {5580 .dir = dir.handle,
5040 error.IsDir => return error.PathAlreadyExists,5581 .creation = .CREATE,
5041 error.NotDir => return error.Unexpected,5582 .filter = if (flags.is_directory) .dir_only else .non_directory_only,
5042 error.WouldBlock => return error.Unexpected,5583 })) |handle| {
5043 error.PipeBusy => return error.Unexpected,5584 syscall.finish();
5044 error.NoDevice => return error.Unexpected,5585 break :handle handle;
5045 error.AntivirusInterference => return error.Unexpected,5586 } else |err| switch (err) {
5046 else => |e| return e,5587 error.IsDir => return syscall.fail(error.PathAlreadyExists),
5588 error.NotDir => return syscall.fail(error.Unexpected),
5589 error.WouldBlock => return syscall.fail(error.Unexpected),
5590 error.PipeBusy => return syscall.fail(error.Unexpected),
5591 error.NoDevice => return syscall.fail(error.Unexpected),
5592 error.AntivirusInterference => return syscall.fail(error.Unexpected),
5593 error.OperationCanceled => {
5594 try syscall.checkCancel();
5595 continue;
5596 },
5597 else => |e| return e,
5598 }
5599 }
5047 };5600 };
5048 defer w.CloseHandle(symlink_handle);5601 defer w.CloseHandle(symlink_handle);
50495602
...@@ -5121,18 +5674,18 @@ fn dirSymLinkWasi(...@@ -5121,18 +5674,18 @@ fn dirSymLinkWasi(
5121 if (builtin.link_libc) return dirSymLinkPosix(userdata, dir, target_path, sym_link_path, flags);5674 if (builtin.link_libc) return dirSymLinkPosix(userdata, dir, target_path, sym_link_path, flags);
51225675
5123 const t: *Threaded = @ptrCast(@alignCast(userdata));5676 const t: *Threaded = @ptrCast(@alignCast(userdata));
5124 const current_thread = Thread.getCurrent(t);5677 _ = t;
51255678
5126 try current_thread.beginSyscall();5679 const syscall: Syscall = try .start();
5127 while (true) {5680 while (true) {
5128 switch (std.os.wasi.path_symlink(target_path.ptr, target_path.len, dir.handle, sym_link_path.ptr, sym_link_path.len)) {5681 switch (std.os.wasi.path_symlink(target_path.ptr, target_path.len, dir.handle, sym_link_path.ptr, sym_link_path.len)) {
5129 .SUCCESS => return current_thread.endSyscall(),5682 .SUCCESS => return syscall.finish(),
5130 .INTR => {5683 .INTR => {
5131 try current_thread.checkCancel();5684 try syscall.checkCancel();
5132 continue;5685 continue;
5133 },5686 },
5134 else => |e| {5687 else => |e| {
5135 current_thread.endSyscall();5688 syscall.finish();
5136 switch (e) {5689 switch (e) {
5137 .FAULT => |err| return errnoBug(err),5690 .FAULT => |err| return errnoBug(err),
5138 .INVAL => |err| return errnoBug(err),5691 .INVAL => |err| return errnoBug(err),
...@@ -5167,7 +5720,7 @@ fn dirSymLinkPosix(...@@ -5167,7 +5720,7 @@ fn dirSymLinkPosix(
5167) Dir.SymLinkError!void {5720) Dir.SymLinkError!void {
5168 _ = flags;5721 _ = flags;
5169 const t: *Threaded = @ptrCast(@alignCast(userdata));5722 const t: *Threaded = @ptrCast(@alignCast(userdata));
5170 const current_thread = Thread.getCurrent(t);5723 _ = t;
51715724
5172 var target_path_buffer: [posix.PATH_MAX]u8 = undefined;5725 var target_path_buffer: [posix.PATH_MAX]u8 = undefined;
5173 var sym_link_path_buffer: [posix.PATH_MAX]u8 = undefined;5726 var sym_link_path_buffer: [posix.PATH_MAX]u8 = undefined;
...@@ -5175,16 +5728,16 @@ fn dirSymLinkPosix(...@@ -5175,16 +5728,16 @@ fn dirSymLinkPosix(
5175 const target_path_posix = try pathToPosix(target_path, &target_path_buffer);5728 const target_path_posix = try pathToPosix(target_path, &target_path_buffer);
5176 const sym_link_path_posix = try pathToPosix(sym_link_path, &sym_link_path_buffer);5729 const sym_link_path_posix = try pathToPosix(sym_link_path, &sym_link_path_buffer);
51775730
5178 try current_thread.beginSyscall();5731 const syscall: Syscall = try .start();
5179 while (true) {5732 while (true) {
5180 switch (posix.errno(posix.system.symlinkat(target_path_posix, dir.handle, sym_link_path_posix))) {5733 switch (posix.errno(posix.system.symlinkat(target_path_posix, dir.handle, sym_link_path_posix))) {
5181 .SUCCESS => return current_thread.endSyscall(),5734 .SUCCESS => return syscall.finish(),
5182 .INTR => {5735 .INTR => {
5183 try current_thread.checkCancel();5736 try syscall.checkCancel();
5184 continue;5737 continue;
5185 },5738 },
5186 else => |e| {5739 else => |e| {
5187 current_thread.endSyscall();5740 syscall.finish();
5188 switch (e) {5741 switch (e) {
5189 .FAULT => |err| return errnoBug(err),5742 .FAULT => |err| return errnoBug(err),
5190 .INVAL => |err| return errnoBug(err),5743 .INVAL => |err| return errnoBug(err),
...@@ -5216,14 +5769,24 @@ const dirReadLink = switch (native_os) {...@@ -5216,14 +5769,24 @@ const dirReadLink = switch (native_os) {
52165769
5217fn dirReadLinkWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {5770fn dirReadLinkWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
5218 const t: *Threaded = @ptrCast(@alignCast(userdata));5771 const t: *Threaded = @ptrCast(@alignCast(userdata));
5219 const current_thread = Thread.getCurrent(t);5772 _ = t;
5220 const w = windows;5773 const w = windows;
52215774
5222 try current_thread.checkCancel();
5223
5224 var sub_path_w_buf = try windows.sliceToPrefixedFileW(dir.handle, sub_path);5775 var sub_path_w_buf = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
52255776
5226 const result_w = try w.ReadLink(dir.handle, sub_path_w_buf.span(), &sub_path_w_buf.data);5777 const syscall: Syscall = try .start();
5778 const result_w = while (true) {
5779 if (w.ReadLink(dir.handle, sub_path_w_buf.span(), &sub_path_w_buf.data)) |res| {
5780 syscall.finish();
5781 break res;
5782 } else |err| switch (err) {
5783 error.OperationCanceled => {
5784 try syscall.checkCancel();
5785 continue;
5786 },
5787 else => |e| return syscall.fail(e),
5788 }
5789 };
52275790
5228 const len = std.unicode.calcWtf8Len(result_w);5791 const len = std.unicode.calcWtf8Len(result_w);
5229 if (len > buffer.len) return error.NameTooLong;5792 if (len > buffer.len) return error.NameTooLong;
...@@ -5235,22 +5798,22 @@ fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer...@@ -5235,22 +5798,22 @@ fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer
5235 if (builtin.link_libc) return dirReadLinkPosix(userdata, dir, sub_path, buffer);5798 if (builtin.link_libc) return dirReadLinkPosix(userdata, dir, sub_path, buffer);
52365799
5237 const t: *Threaded = @ptrCast(@alignCast(userdata));5800 const t: *Threaded = @ptrCast(@alignCast(userdata));
5238 const current_thread = Thread.getCurrent(t);5801 _ = t;
52395802
5240 var n: usize = undefined;5803 var n: usize = undefined;
5241 try current_thread.beginSyscall();5804 const syscall: Syscall = try .start();
5242 while (true) {5805 while (true) {
5243 switch (std.os.wasi.path_readlink(dir.handle, sub_path.ptr, sub_path.len, buffer.ptr, buffer.len, &n)) {5806 switch (std.os.wasi.path_readlink(dir.handle, sub_path.ptr, sub_path.len, buffer.ptr, buffer.len, &n)) {
5244 .SUCCESS => {5807 .SUCCESS => {
5245 current_thread.endSyscall();5808 syscall.finish();
5246 return n;5809 return n;
5247 },5810 },
5248 .INTR => {5811 .INTR => {
5249 try current_thread.checkCancel();5812 try syscall.checkCancel();
5250 continue;5813 continue;
5251 },5814 },
5252 else => |e| {5815 else => |e| {
5253 current_thread.endSyscall();5816 syscall.finish();
5254 switch (e) {5817 switch (e) {
5255 .ACCES => return error.AccessDenied,5818 .ACCES => return error.AccessDenied,
5256 .FAULT => |err| return errnoBug(err),5819 .FAULT => |err| return errnoBug(err),
...@@ -5272,26 +5835,26 @@ fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer...@@ -5272,26 +5835,26 @@ fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer
52725835
5273fn dirReadLinkPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {5836fn dirReadLinkPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
5274 const t: *Threaded = @ptrCast(@alignCast(userdata));5837 const t: *Threaded = @ptrCast(@alignCast(userdata));
5275 const current_thread = Thread.getCurrent(t);5838 _ = t;
52765839
5277 var sub_path_buffer: [posix.PATH_MAX]u8 = undefined;5840 var sub_path_buffer: [posix.PATH_MAX]u8 = undefined;
5278 const sub_path_posix = try pathToPosix(sub_path, &sub_path_buffer);5841 const sub_path_posix = try pathToPosix(sub_path, &sub_path_buffer);
52795842
5280 try current_thread.beginSyscall();5843 const syscall: Syscall = try .start();
5281 while (true) {5844 while (true) {
5282 const rc = posix.system.readlinkat(dir.handle, sub_path_posix, buffer.ptr, buffer.len);5845 const rc = posix.system.readlinkat(dir.handle, sub_path_posix, buffer.ptr, buffer.len);
5283 switch (posix.errno(rc)) {5846 switch (posix.errno(rc)) {
5284 .SUCCESS => {5847 .SUCCESS => {
5285 current_thread.endSyscall();5848 syscall.finish();
5286 const len: usize = @bitCast(rc);5849 const len: usize = @bitCast(rc);
5287 return len;5850 return len;
5288 },5851 },
5289 .INTR => {5852 .INTR => {
5290 try current_thread.checkCancel();5853 try syscall.checkCancel();
5291 continue;5854 continue;
5292 },5855 },
5293 else => |e| {5856 else => |e| {
5294 current_thread.endSyscall();5857 syscall.finish();
5295 switch (e) {5858 switch (e) {
5296 .ACCES => return error.AccessDenied,5859 .ACCES => return error.AccessDenied,
5297 .FAULT => |err| return errnoBug(err),5860 .FAULT => |err| return errnoBug(err),
...@@ -5326,8 +5889,8 @@ fn dirSetPermissionsWindows(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Pe...@@ -5326,8 +5889,8 @@ fn dirSetPermissionsWindows(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Pe
5326fn dirSetPermissionsPosix(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {5889fn dirSetPermissionsPosix(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {
5327 if (@sizeOf(Dir.Permissions) == 0) return;5890 if (@sizeOf(Dir.Permissions) == 0) return;
5328 const t: *Threaded = @ptrCast(@alignCast(userdata));5891 const t: *Threaded = @ptrCast(@alignCast(userdata));
5329 const current_thread = Thread.getCurrent(t);5892 _ = t;
5330 return setPermissionsPosix(current_thread, dir.handle, permissions.toMode());5893 return setPermissionsPosix(dir.handle, permissions.toMode());
5331}5894}
53325895
5333fn dirSetFilePermissions(5896fn dirSetFilePermissions(
...@@ -5340,7 +5903,6 @@ fn dirSetFilePermissions(...@@ -5340,7 +5903,6 @@ fn dirSetFilePermissions(
5340 if (@sizeOf(Dir.Permissions) == 0) return;5903 if (@sizeOf(Dir.Permissions) == 0) return;
5341 if (is_windows) @panic("TODO implement dirSetFilePermissions windows");5904 if (is_windows) @panic("TODO implement dirSetFilePermissions windows");
5342 const t: *Threaded = @ptrCast(@alignCast(userdata));5905 const t: *Threaded = @ptrCast(@alignCast(userdata));
5343 const current_thread = Thread.getCurrent(t);
53445906
5345 var path_buffer: [posix.PATH_MAX]u8 = undefined;5907 var path_buffer: [posix.PATH_MAX]u8 = undefined;
5346 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);5908 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
...@@ -5348,12 +5910,11 @@ fn dirSetFilePermissions(...@@ -5348,12 +5910,11 @@ fn dirSetFilePermissions(
5348 const mode = permissions.toMode();5910 const mode = permissions.toMode();
5349 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;5911 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
53505912
5351 return posixFchmodat(t, current_thread, dir.handle, sub_path_posix, mode, flags);5913 return posixFchmodat(t, dir.handle, sub_path_posix, mode, flags);
5352}5914}
53535915
5354fn posixFchmodat(5916fn posixFchmodat(
5355 t: *Threaded,5917 t: *Threaded,
5356 current_thread: *Thread,
5357 dir_fd: posix.fd_t,5918 dir_fd: posix.fd_t,
5358 path: [*:0]const u8,5919 path: [*:0]const u8,
5359 mode: posix.mode_t,5920 mode: posix.mode_t,
...@@ -5362,20 +5923,20 @@ fn posixFchmodat(...@@ -5362,20 +5923,20 @@ fn posixFchmodat(
5362 // No special handling for linux is needed if we can use the libc fallback5923 // No special handling for linux is needed if we can use the libc fallback
5363 // or `flags` is empty. Glibc only added the fallback in 2.32.5924 // or `flags` is empty. Glibc only added the fallback in 2.32.
5364 if (have_fchmodat_flags or flags == 0) {5925 if (have_fchmodat_flags or flags == 0) {
5365 try current_thread.beginSyscall();5926 const syscall: Syscall = try .start();
5366 while (true) {5927 while (true) {
5367 const rc = if (have_fchmodat_flags or builtin.link_libc)5928 const rc = if (have_fchmodat_flags or builtin.link_libc)
5368 posix.system.fchmodat(dir_fd, path, mode, flags)5929 posix.system.fchmodat(dir_fd, path, mode, flags)
5369 else5930 else
5370 posix.system.fchmodat(dir_fd, path, mode);5931 posix.system.fchmodat(dir_fd, path, mode);
5371 switch (posix.errno(rc)) {5932 switch (posix.errno(rc)) {
5372 .SUCCESS => return current_thread.endSyscall(),5933 .SUCCESS => return syscall.finish(),
5373 .INTR => {5934 .INTR => {
5374 try current_thread.checkCancel();5935 try syscall.checkCancel();
5375 continue;5936 continue;
5376 },5937 },
5377 else => |e| {5938 else => |e| {
5378 current_thread.endSyscall();5939 syscall.finish();
5379 switch (e) {5940 switch (e) {
5380 .BADF => |err| return errnoBug(err),5941 .BADF => |err| return errnoBug(err),
5381 .FAULT => |err| return errnoBug(err),5942 .FAULT => |err| return errnoBug(err),
...@@ -5400,20 +5961,20 @@ fn posixFchmodat(...@@ -5400,20 +5961,20 @@ fn posixFchmodat(
5400 }5961 }
54015962
5402 if (@atomicLoad(UseFchmodat2, &t.use_fchmodat2, .monotonic) == .disabled)5963 if (@atomicLoad(UseFchmodat2, &t.use_fchmodat2, .monotonic) == .disabled)
5403 return fchmodatFallback(current_thread, dir_fd, path, mode);5964 return fchmodatFallback(dir_fd, path, mode);
54045965
5405 comptime assert(native_os == .linux);5966 comptime assert(native_os == .linux);
54065967
5407 try current_thread.beginSyscall();5968 const syscall: Syscall = try .start();
5408 while (true) {5969 while (true) {
5409 switch (std.os.linux.errno(std.os.linux.fchmodat2(dir_fd, path, mode, flags))) {5970 switch (std.os.linux.errno(std.os.linux.fchmodat2(dir_fd, path, mode, flags))) {
5410 .SUCCESS => return current_thread.endSyscall(),5971 .SUCCESS => return syscall.finish(),
5411 .INTR => {5972 .INTR => {
5412 try current_thread.checkCancel();5973 try syscall.checkCancel();
5413 continue;5974 continue;
5414 },5975 },
5415 else => |e| {5976 else => |e| {
5416 current_thread.endSyscall();5977 syscall.finish();
5417 switch (e) {5978 switch (e) {
5418 .BADF => |err| return errnoBug(err),5979 .BADF => |err| return errnoBug(err),
5419 .FAULT => |err| return errnoBug(err),5980 .FAULT => |err| return errnoBug(err),
...@@ -5429,7 +5990,7 @@ fn posixFchmodat(...@@ -5429,7 +5990,7 @@ fn posixFchmodat(
5429 .ROFS => return error.ReadOnlyFileSystem,5990 .ROFS => return error.ReadOnlyFileSystem,
5430 .NOSYS => {5991 .NOSYS => {
5431 @atomicStore(UseFchmodat2, &t.use_fchmodat2, .disabled, .monotonic);5992 @atomicStore(UseFchmodat2, &t.use_fchmodat2, .disabled, .monotonic);
5432 return fchmodatFallback(current_thread, dir_fd, path, mode);5993 return fchmodatFallback(dir_fd, path, mode);
5433 },5994 },
5434 else => |err| return posix.unexpectedErrno(err),5995 else => |err| return posix.unexpectedErrno(err),
5435 }5996 }
...@@ -5439,7 +6000,6 @@ fn posixFchmodat(...@@ -5439,7 +6000,6 @@ fn posixFchmodat(
5439}6000}
54406001
5441fn fchmodatFallback(6002fn fchmodatFallback(
5442 current_thread: *Thread,
5443 dir_fd: posix.fd_t,6003 dir_fd: posix.fd_t,
5444 path: [*:0]const u8,6004 path: [*:0]const u8,
5445 mode: posix.mode_t,6005 mode: posix.mode_t,
...@@ -5457,64 +6017,68 @@ fn fchmodatFallback(...@@ -5457,64 +6017,68 @@ fn fchmodatFallback(
5457 // 2. Stat the fd and check if it isn't a symbolic link.6017 // 2. Stat the fd and check if it isn't a symbolic link.
5458 // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`.6018 // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`.
5459 // 4. Pass the procfs path to `chmod` with the `mode`.6019 // 4. Pass the procfs path to `chmod` with the `mode`.
5460 try current_thread.beginSyscall();6020 const path_fd: posix.fd_t = fd: {
5461 const path_fd: posix.fd_t = while (true) {6021 const syscall: Syscall = try .start();
5462 const rc = posix.system.openat(dir_fd, path, .{6022 while (true) {
5463 .PATH = true,6023 const rc = posix.system.openat(dir_fd, path, .{
5464 .NOFOLLOW = true,6024 .PATH = true,
5465 .CLOEXEC = true,6025 .NOFOLLOW = true,
5466 }, @as(posix.mode_t, 0));6026 .CLOEXEC = true,
5467 switch (posix.errno(rc)) {6027 }, @as(posix.mode_t, 0));
5468 .SUCCESS => {6028 switch (posix.errno(rc)) {
5469 current_thread.endSyscall();6029 .SUCCESS => {
5470 break @intCast(rc);6030 syscall.finish();
5471 },6031 break :fd @intCast(rc);
5472 .INTR => {6032 },
5473 try current_thread.checkCancel();6033 .INTR => {
5474 continue;6034 try syscall.checkCancel();
5475 },6035 continue;
5476 else => |e| {6036 },
5477 current_thread.endSyscall();6037 else => |e| {
5478 switch (e) {6038 syscall.finish();
5479 .FAULT => |err| return errnoBug(err),6039 switch (e) {
5480 .INVAL => |err| return errnoBug(err),6040 .FAULT => |err| return errnoBug(err),
5481 .ACCES => return error.AccessDenied,6041 .INVAL => |err| return errnoBug(err),
5482 .PERM => return error.PermissionDenied,6042 .ACCES => return error.AccessDenied,
5483 .LOOP => return error.SymLinkLoop,6043 .PERM => return error.PermissionDenied,
5484 .MFILE => return error.ProcessFdQuotaExceeded,6044 .LOOP => return error.SymLinkLoop,
5485 .NAMETOOLONG => return error.NameTooLong,6045 .MFILE => return error.ProcessFdQuotaExceeded,
5486 .NFILE => return error.SystemFdQuotaExceeded,6046 .NAMETOOLONG => return error.NameTooLong,
5487 .NOENT => return error.FileNotFound,6047 .NFILE => return error.SystemFdQuotaExceeded,
5488 .NOMEM => return error.SystemResources,6048 .NOENT => return error.FileNotFound,
5489 else => |err| return posix.unexpectedErrno(err),6049 .NOMEM => return error.SystemResources,
5490 }6050 else => |err| return posix.unexpectedErrno(err),
5491 },6051 }
6052 },
6053 }
5492 }6054 }
5493 };6055 };
5494 defer posix.close(path_fd);6056 defer posix.close(path_fd);
54956057
5496 try current_thread.beginSyscall();6058 const path_mode = mode: {
5497 const path_mode = while (true) {6059 const syscall: Syscall = try .start();
5498 var statx = std.mem.zeroes(std.os.linux.Statx);6060 while (true) {
5499 switch (sys.errno(sys.statx(path_fd, "", posix.AT.EMPTY_PATH, .{ .TYPE = true }, &statx))) {6061 var statx = std.mem.zeroes(std.os.linux.Statx);
5500 .SUCCESS => {6062 switch (sys.errno(sys.statx(path_fd, "", posix.AT.EMPTY_PATH, .{ .TYPE = true }, &statx))) {
5501 current_thread.endSyscall();6063 .SUCCESS => {
5502 if (!statx.mask.TYPE) return error.Unexpected;6064 syscall.finish();
5503 break statx.mode;6065 if (!statx.mask.TYPE) return error.Unexpected;
5504 },6066 break :mode statx.mode;
5505 .INTR => {6067 },
5506 try current_thread.checkCancel();6068 .INTR => {
5507 continue;6069 try syscall.checkCancel();
5508 },6070 continue;
5509 else => |e| {6071 },
5510 current_thread.endSyscall();6072 else => |e| {
5511 switch (e) {6073 syscall.finish();
5512 .ACCES => return error.AccessDenied,6074 switch (e) {
5513 .LOOP => return error.SymLinkLoop,6075 .ACCES => return error.AccessDenied,
5514 .NOMEM => return error.SystemResources,6076 .LOOP => return error.SymLinkLoop,
5515 else => |err| return posix.unexpectedErrno(err),6077 .NOMEM => return error.SystemResources,
5516 }6078 else => |err| return posix.unexpectedErrno(err),
5517 },6079 }
6080 },
6081 }
5518 }6082 }
5519 };6083 };
55206084
...@@ -5524,16 +6088,16 @@ fn fchmodatFallback(...@@ -5524,16 +6088,16 @@ fn fchmodatFallback(
55246088
5525 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;6089 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
5526 const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, "/proc/self/fd/{d}", .{path_fd}, 0) catch unreachable;6090 const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, "/proc/self/fd/{d}", .{path_fd}, 0) catch unreachable;
5527 try current_thread.beginSyscall();6091 const syscall: Syscall = try .start();
5528 while (true) {6092 while (true) {
5529 switch (posix.errno(posix.system.chmod(proc_path, mode))) {6093 switch (posix.errno(posix.system.chmod(proc_path, mode))) {
5530 .SUCCESS => return current_thread.endSyscall(),6094 .SUCCESS => return syscall.finish(),
5531 .INTR => {6095 .INTR => {
5532 try current_thread.checkCancel();6096 try syscall.checkCancel();
5533 continue;6097 continue;
5534 },6098 },
5535 else => |e| {6099 else => |e| {
5536 current_thread.endSyscall();6100 syscall.finish();
5537 switch (e) {6101 switch (e) {
5538 .NOENT => return error.OperationUnsupported, // procfs not mounted.6102 .NOENT => return error.OperationUnsupported, // procfs not mounted.
5539 .BADF => |err| return errnoBug(err),6103 .BADF => |err| return errnoBug(err),
...@@ -5569,24 +6133,24 @@ fn dirSetOwnerUnsupported(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, gro...@@ -5569,24 +6133,24 @@ fn dirSetOwnerUnsupported(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, gro
5569fn dirSetOwnerPosix(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, group: ?File.Gid) Dir.SetOwnerError!void {6133fn dirSetOwnerPosix(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, group: ?File.Gid) Dir.SetOwnerError!void {
5570 if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.6134 if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.
5571 const t: *Threaded = @ptrCast(@alignCast(userdata));6135 const t: *Threaded = @ptrCast(@alignCast(userdata));
5572 const current_thread = Thread.getCurrent(t);6136 _ = t;
5573 const uid = owner orelse ~@as(posix.uid_t, 0);6137 const uid = owner orelse ~@as(posix.uid_t, 0);
5574 const gid = group orelse ~@as(posix.gid_t, 0);6138 const gid = group orelse ~@as(posix.gid_t, 0);
5575 return posixFchown(current_thread, dir.handle, uid, gid);6139 return posixFchown(dir.handle, uid, gid);
5576}6140}
55776141
5578fn posixFchown(current_thread: *Thread, fd: posix.fd_t, uid: posix.uid_t, gid: posix.gid_t) File.SetOwnerError!void {6142fn posixFchown(fd: posix.fd_t, uid: posix.uid_t, gid: posix.gid_t) File.SetOwnerError!void {
5579 comptime assert(have_fchown);6143 comptime assert(have_fchown);
5580 try current_thread.beginSyscall();6144 const syscall: Syscall = try .start();
5581 while (true) {6145 while (true) {
5582 switch (posix.errno(posix.system.fchown(fd, uid, gid))) {6146 switch (posix.errno(posix.system.fchown(fd, uid, gid))) {
5583 .SUCCESS => return current_thread.endSyscall(),6147 .SUCCESS => return syscall.finish(),
5584 .INTR => {6148 .INTR => {
5585 try current_thread.checkCancel();6149 try syscall.checkCancel();
5586 continue;6150 continue;
5587 },6151 },
5588 else => |e| {6152 else => |e| {
5589 current_thread.endSyscall();6153 syscall.finish();
5590 switch (e) {6154 switch (e) {
5591 .BADF => |err| return errnoBug(err), // likely fd refers to directory opened without `Dir.OpenOptions.iterate`6155 .BADF => |err| return errnoBug(err), // likely fd refers to directory opened without `Dir.OpenOptions.iterate`
5592 .FAULT => |err| return errnoBug(err),6156 .FAULT => |err| return errnoBug(err),
...@@ -5616,12 +6180,11 @@ fn dirSetFileOwner(...@@ -5616,12 +6180,11 @@ fn dirSetFileOwner(
5616) Dir.SetFileOwnerError!void {6180) Dir.SetFileOwnerError!void {
5617 if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.6181 if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.
5618 const t: *Threaded = @ptrCast(@alignCast(userdata));6182 const t: *Threaded = @ptrCast(@alignCast(userdata));
5619 const current_thread = Thread.getCurrent(t);6183 _ = t;
56206184
5621 var path_buffer: [posix.PATH_MAX]u8 = undefined;6185 var path_buffer: [posix.PATH_MAX]u8 = undefined;
5622 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);6186 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
56236187
5624 _ = current_thread;
5625 _ = dir;6188 _ = dir;
5626 _ = sub_path_posix;6189 _ = sub_path_posix;
5627 _ = owner;6190 _ = owner;
...@@ -5638,35 +6201,43 @@ const fileSync = switch (native_os) {...@@ -5638,35 +6201,43 @@ const fileSync = switch (native_os) {
56386201
5639fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void {6202fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void {
5640 const t: *Threaded = @ptrCast(@alignCast(userdata));6203 const t: *Threaded = @ptrCast(@alignCast(userdata));
5641 const current_thread = Thread.getCurrent(t);6204 _ = t;
5642
5643 try current_thread.checkCancel();
5644
5645 if (windows.kernel32.FlushFileBuffers(file.handle) != 0)
5646 return;
56476205
5648 switch (windows.GetLastError()) {6206 const syscall: Syscall = try .start();
5649 .SUCCESS => return,6207 while (true) {
5650 .INVALID_HANDLE => unreachable,6208 if (windows.kernel32.FlushFileBuffers(file.handle) != 0) {
5651 .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time6209 return syscall.finish();
5652 .UNEXP_NET_ERR => return error.InputOutput,6210 }
5653 else => |err| return windows.unexpectedError(err),6211 switch (windows.GetLastError()) {
6212 .SUCCESS => unreachable, // `FlushFileBuffers` returned nonzero
6213 .INVALID_HANDLE => unreachable,
6214 .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time
6215 .UNEXP_NET_ERR => return syscall.fail(error.InputOutput),
6216 .OPERATION_ABORTED => {
6217 try syscall.checkCancel();
6218 continue;
6219 },
6220 else => |err| {
6221 syscall.finish();
6222 return windows.unexpectedError(err);
6223 },
6224 }
5654 }6225 }
5655}6226}
56566227
5657fn fileSyncPosix(userdata: ?*anyopaque, file: File) File.SyncError!void {6228fn fileSyncPosix(userdata: ?*anyopaque, file: File) File.SyncError!void {
5658 const t: *Threaded = @ptrCast(@alignCast(userdata));6229 const t: *Threaded = @ptrCast(@alignCast(userdata));
5659 const current_thread = Thread.getCurrent(t);6230 _ = t;
5660 try current_thread.beginSyscall();6231 const syscall: Syscall = try .start();
5661 while (true) {6232 while (true) {
5662 switch (posix.errno(posix.system.fsync(file.handle))) {6233 switch (posix.errno(posix.system.fsync(file.handle))) {
5663 .SUCCESS => return current_thread.endSyscall(),6234 .SUCCESS => return syscall.finish(),
5664 .INTR => {6235 .INTR => {
5665 try current_thread.checkCancel();6236 try syscall.checkCancel();
5666 continue;6237 continue;
5667 },6238 },
5668 else => |e| {6239 else => |e| {
5669 current_thread.endSyscall();6240 syscall.finish();
5670 switch (e) {6241 switch (e) {
5671 .BADF => |err| return errnoBug(err),6242 .BADF => |err| return errnoBug(err),
5672 .INVAL => |err| return errnoBug(err),6243 .INVAL => |err| return errnoBug(err),
...@@ -5683,17 +6254,17 @@ fn fileSyncPosix(userdata: ?*anyopaque, file: File) File.SyncError!void {...@@ -5683,17 +6254,17 @@ fn fileSyncPosix(userdata: ?*anyopaque, file: File) File.SyncError!void {
56836254
5684fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {6255fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
5685 const t: *Threaded = @ptrCast(@alignCast(userdata));6256 const t: *Threaded = @ptrCast(@alignCast(userdata));
5686 const current_thread = Thread.getCurrent(t);6257 _ = t;
5687 try current_thread.beginSyscall();6258 const syscall: Syscall = try .start();
5688 while (true) {6259 while (true) {
5689 switch (std.os.wasi.fd_sync(file.handle)) {6260 switch (std.os.wasi.fd_sync(file.handle)) {
5690 .SUCCESS => return current_thread.endSyscall(),6261 .SUCCESS => return syscall.finish(),
5691 .INTR => {6262 .INTR => {
5692 try current_thread.checkCancel();6263 try syscall.checkCancel();
5693 continue;6264 continue;
5694 },6265 },
5695 else => |e| {6266 else => |e| {
5696 current_thread.endSyscall();6267 syscall.finish();
5697 switch (e) {6268 switch (e) {
5698 .BADF => |err| return errnoBug(err),6269 .BADF => |err| return errnoBug(err),
5699 .INVAL => |err| return errnoBug(err),6270 .INVAL => |err| return errnoBug(err),
...@@ -5710,33 +6281,46 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {...@@ -5710,33 +6281,46 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
57106281
5711fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {6282fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
5712 const t: *Threaded = @ptrCast(@alignCast(userdata));6283 const t: *Threaded = @ptrCast(@alignCast(userdata));
5713 const current_thread = Thread.getCurrent(t);6284 _ = t;
5714 return isTty(current_thread, file);6285 return isTty(file);
5715}6286}
57166287
5717fn isTty(current_thread: *Thread, file: File) Io.Cancelable!bool {6288fn isTty(file: File) Io.Cancelable!bool {
5718 if (is_windows) {6289 if (is_windows) {
5719 if (try isCygwinPty(current_thread, file)) return true;6290 if (try isCygwinPty(file)) return true;
5720 try current_thread.checkCancel();
5721 var out: windows.DWORD = undefined;6291 var out: windows.DWORD = undefined;
5722 return windows.kernel32.GetConsoleMode(file.handle, &out) != 0;6292 const syscall: Syscall = try .start();
6293 while (windows.kernel32.GetConsoleMode(file.handle, &out) == 0) {
6294 switch (windows.GetLastError()) {
6295 .OPERATION_ABORTED => {
6296 try syscall.checkCancel();
6297 continue;
6298 },
6299 else => {
6300 syscall.finish();
6301 return false;
6302 },
6303 }
6304 }
6305 syscall.finish();
6306 return true;
5723 }6307 }
57246308
5725 if (builtin.link_libc) {6309 if (builtin.link_libc) {
5726 try current_thread.beginSyscall();6310 const syscall: Syscall = try .start();
5727 while (true) {6311 while (true) {
5728 const rc = posix.system.isatty(file.handle);6312 const rc = posix.system.isatty(file.handle);
5729 switch (posix.errno(rc - 1)) {6313 switch (posix.errno(rc - 1)) {
5730 .SUCCESS => {6314 .SUCCESS => {
5731 current_thread.endSyscall();6315 syscall.finish();
5732 return true;6316 return true;
5733 },6317 },
5734 .INTR => {6318 .INTR => {
5735 try current_thread.checkCancel();6319 try syscall.checkCancel();
5736 continue;6320 continue;
5737 },6321 },
5738 else => {6322 else => {
5739 current_thread.endSyscall();6323 syscall.finish();
5740 return false;6324 return false;
5741 },6325 },
5742 }6326 }
...@@ -5760,22 +6344,22 @@ fn isTty(current_thread: *Thread, file: File) Io.Cancelable!bool {...@@ -5760,22 +6344,22 @@ fn isTty(current_thread: *Thread, file: File) Io.Cancelable!bool {
57606344
5761 if (native_os == .linux) {6345 if (native_os == .linux) {
5762 const linux = std.os.linux;6346 const linux = std.os.linux;
5763 try current_thread.beginSyscall();6347 const syscall: Syscall = try .start();
5764 while (true) {6348 while (true) {
5765 var wsz: posix.winsize = undefined;6349 var wsz: posix.winsize = undefined;
5766 const fd: usize = @bitCast(@as(isize, file.handle));6350 const fd: usize = @bitCast(@as(isize, file.handle));
5767 const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz));6351 const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz));
5768 switch (linux.errno(rc)) {6352 switch (linux.errno(rc)) {
5769 .SUCCESS => {6353 .SUCCESS => {
5770 current_thread.endSyscall();6354 syscall.finish();
5771 return true;6355 return true;
5772 },6356 },
5773 .INTR => {6357 .INTR => {
5774 try current_thread.checkCancel();6358 try syscall.checkCancel();
5775 continue;6359 continue;
5776 },6360 },
5777 else => {6361 else => {
5778 current_thread.endSyscall();6362 syscall.finish();
5779 return false;6363 return false;
5780 },6364 },
5781 }6365 }
...@@ -5787,53 +6371,99 @@ fn isTty(current_thread: *Thread, file: File) Io.Cancelable!bool {...@@ -5787,53 +6371,99 @@ fn isTty(current_thread: *Thread, file: File) Io.Cancelable!bool {
57876371
5788fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {6372fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
5789 const t: *Threaded = @ptrCast(@alignCast(userdata));6373 const t: *Threaded = @ptrCast(@alignCast(userdata));
5790 const current_thread = Thread.getCurrent(t);6374 _ = t;
57916375
5792 if (is_windows) {6376 if (!is_windows) {
5793 try current_thread.checkCancel();6377 if (try supportsAnsiEscapeCodes(file)) return;
6378 return error.NotTerminalDevice;
6379 }
57946380
5795 // For Windows Terminal, VT Sequences processing is enabled by default.6381 // For Windows Terminal, VT Sequences processing is enabled by default.
5796 var original_console_mode: windows.DWORD = 0;6382 var original_console_mode: windows.DWORD = 0;
5797 if (windows.kernel32.GetConsoleMode(file.handle, &original_console_mode) != 0) {
5798 if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
57996383
5800 // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.6384 {
5801 // https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/6385 const syscall: Syscall = try .start();
5802 //6386 while (windows.kernel32.GetConsoleMode(file.handle, &original_console_mode) == 0) {
5803 // Note: In Microsoft's example for enabling virtual terminal processing, it6387 switch (windows.GetLastError()) {
5804 // shows attempting to enable `DISABLE_NEWLINE_AUTO_RETURN` as well:6388 .OPERATION_ABORTED => {
5805 // https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing6389 try syscall.checkCancel();
5806 // This is avoided because in the old Windows Console, that flag causes \n (as opposed to \r\n)6390 continue;
5807 // to behave unexpectedly (the cursor moves down 1 row but remains on the same column).6391 },
5808 // Additionally, the default console mode in Windows Terminal does not have6392 else => {
5809 // `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`6393 syscall.finish();
5810 // we end up matching the mode of Windows Terminal.6394 if (try isCygwinPty(file)) return;
5811 const requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;6395 return error.NotTerminalDevice;
5812 const console_mode = original_console_mode | requested_console_modes;6396 },
5813 try current_thread.checkCancel();6397 }
5814 if (windows.kernel32.SetConsoleMode(file.handle, console_mode) != 0) return;6398 }
5815 }6399 syscall.finish();
5816 if (try isCygwinPty(current_thread, file)) return;6400 }
5817 } else {6401
5818 if (try supportsAnsiEscapeCodes(current_thread, file)) return;6402 if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
6403
6404 // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
6405 // https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/
6406 //
6407 // Note: In Microsoft's example for enabling virtual terminal processing, it
6408 // shows attempting to enable `DISABLE_NEWLINE_AUTO_RETURN` as well:
6409 // https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
6410 // This is avoided because in the old Windows Console, that flag causes \n (as opposed to \r\n)
6411 // to behave unexpectedly (the cursor moves down 1 row but remains on the same column).
6412 // Additionally, the default console mode in Windows Terminal does not have
6413 // `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`
6414 // we end up matching the mode of Windows Terminal.
6415 const requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
6416 const console_mode = original_console_mode | requested_console_modes;
6417
6418 {
6419 const syscall: Syscall = try .start();
6420 while (windows.kernel32.SetConsoleMode(file.handle, console_mode) == 0) {
6421 switch (windows.GetLastError()) {
6422 .OPERATION_ABORTED => {
6423 try syscall.checkCancel();
6424 continue;
6425 },
6426 else => {
6427 syscall.finish();
6428 if (try isCygwinPty(file)) return;
6429 return error.NotTerminalDevice;
6430 },
6431 }
6432 }
6433 syscall.finish();
5819 }6434 }
5820 return error.NotTerminalDevice;
5821}6435}
58226436
5823fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {6437fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
5824 const t: *Threaded = @ptrCast(@alignCast(userdata));6438 const t: *Threaded = @ptrCast(@alignCast(userdata));
5825 const current_thread = Thread.getCurrent(t);6439 _ = t;
5826 return supportsAnsiEscapeCodes(current_thread, file);6440 return supportsAnsiEscapeCodes(file);
5827}6441}
58286442
5829fn supportsAnsiEscapeCodes(current_thread: *Thread, file: File) Io.Cancelable!bool {6443fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
5830 if (is_windows) {6444 if (is_windows) {
5831 try current_thread.checkCancel();
5832 var console_mode: windows.DWORD = 0;6445 var console_mode: windows.DWORD = 0;
5833 if (windows.kernel32.GetConsoleMode(file.handle, &console_mode) != 0) {6446
5834 if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true;6447 const syscall: Syscall = try .start();
6448 while (windows.kernel32.GetConsoleMode(file.handle, &console_mode) == 0) {
6449 switch (windows.GetLastError()) {
6450 .OPERATION_ABORTED => {
6451 try syscall.checkCancel();
6452 continue;
6453 },
6454 else => {
6455 syscall.finish();
6456 break;
6457 },
6458 }
6459 } else {
6460 syscall.finish();
6461 if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) {
6462 return true;
6463 }
5835 }6464 }
5836 return isCygwinPty(current_thread, file);6465
6466 return isCygwinPty(file);
5837 }6467 }
58386468
5839 if (native_os == .wasi) {6469 if (native_os == .wasi) {
...@@ -5843,12 +6473,12 @@ fn supportsAnsiEscapeCodes(current_thread: *Thread, file: File) Io.Cancelable!bo...@@ -5843,12 +6473,12 @@ fn supportsAnsiEscapeCodes(current_thread: *Thread, file: File) Io.Cancelable!bo
5843 return false;6473 return false;
5844 }6474 }
58456475
5846 if (try isTty(current_thread, file)) return true;6476 if (try isTty(file)) return true;
58476477
5848 return false;6478 return false;
5849}6479}
58506480
5851fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {6481fn isCygwinPty(file: File) Io.Cancelable!bool {
5852 if (!is_windows) return false;6482 if (!is_windows) return false;
58536483
5854 const handle = file.handle;6484 const handle = file.handle;
...@@ -5863,20 +6493,26 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {...@@ -5863,20 +6493,26 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {
5863 // This allows us to avoid the more costly NtQueryInformationFile call6493 // This allows us to avoid the more costly NtQueryInformationFile call
5864 // for handles that aren't named pipes.6494 // for handles that aren't named pipes.
5865 {6495 {
5866 try current_thread.checkCancel();
5867 var io_status: windows.IO_STATUS_BLOCK = undefined;6496 var io_status: windows.IO_STATUS_BLOCK = undefined;
5868 var device_info: windows.FILE.FS_DEVICE_INFORMATION = undefined;6497 var device_info: windows.FILE.FS_DEVICE_INFORMATION = undefined;
5869 const rc = windows.ntdll.NtQueryVolumeInformationFile(6498 const syscall: Syscall = try .start();
6499 while (true) switch (windows.ntdll.NtQueryVolumeInformationFile(
5870 handle,6500 handle,
5871 &io_status,6501 &io_status,
5872 &device_info,6502 &device_info,
5873 @sizeOf(windows.FILE.FS_DEVICE_INFORMATION),6503 @sizeOf(windows.FILE.FS_DEVICE_INFORMATION),
5874 .Device,6504 .Device,
5875 );6505 )) {
5876 switch (rc) {6506 .SUCCESS => break syscall.finish(),
5877 .SUCCESS => {},6507 .CANCELLED => {
5878 else => return false,6508 try syscall.checkCancel();
5879 }6509 continue;
6510 },
6511 else => {
6512 syscall.finish();
6513 return false;
6514 },
6515 };
5880 if (device_info.DeviceType.FileDevice != .NAMED_PIPE) return false;6516 if (device_info.DeviceType.FileDevice != .NAMED_PIPE) return false;
5881 }6517 }
58826518
...@@ -5891,19 +6527,25 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {...@@ -5891,19 +6527,25 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {
5891 var name_info_bytes align(@alignOf(windows.FILE.NAME_INFORMATION)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);6527 var name_info_bytes align(@alignOf(windows.FILE.NAME_INFORMATION)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
58926528
5893 var io_status_block: windows.IO_STATUS_BLOCK = undefined;6529 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
5894 try current_thread.checkCancel();6530 const syscall: Syscall = try .start();
5895 const rc = windows.ntdll.NtQueryInformationFile(6531 while (true) switch (windows.ntdll.NtQueryInformationFile(
5896 handle,6532 handle,
5897 &io_status_block,6533 &io_status_block,
5898 &name_info_bytes,6534 &name_info_bytes,
5899 @intCast(name_info_bytes.len),6535 @intCast(name_info_bytes.len),
5900 .Name,6536 .Name,
5901 );6537 )) {
5902 switch (rc) {6538 .SUCCESS => break syscall.finish(),
5903 .SUCCESS => {},6539 .CANCELLED => {
6540 try syscall.checkCancel();
6541 continue;
6542 },
5904 .INVALID_PARAMETER => unreachable,6543 .INVALID_PARAMETER => unreachable,
5905 else => return false,6544 else => {
5906 }6545 syscall.finish();
6546 return false;
6547 },
6548 };
59076549
5908 const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);6550 const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);
5909 const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];6551 const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
...@@ -5916,47 +6558,49 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {...@@ -5916,47 +6558,49 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {
59166558
5917fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthError!void {6559fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthError!void {
5918 const t: *Threaded = @ptrCast(@alignCast(userdata));6560 const t: *Threaded = @ptrCast(@alignCast(userdata));
5919 const current_thread = Thread.getCurrent(t);6561 _ = t;
59206562
5921 const signed_len: i64 = @bitCast(length);6563 const signed_len: i64 = @bitCast(length);
5922 if (signed_len < 0) return error.FileTooBig; // Avoid ambiguous EINVAL errors.6564 if (signed_len < 0) return error.FileTooBig; // Avoid ambiguous EINVAL errors.
59236565
5924 if (is_windows) {6566 if (is_windows) {
5925 try current_thread.checkCancel();
5926
5927 var io_status_block: windows.IO_STATUS_BLOCK = undefined;6567 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
5928 const eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{6568 const eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{
5929 .EndOfFile = signed_len,6569 .EndOfFile = signed_len,
5930 };6570 };
59316571
5932 const status = windows.ntdll.NtSetInformationFile(6572 const syscall: Syscall = try .start();
6573 while (true) switch (windows.ntdll.NtSetInformationFile(
5933 file.handle,6574 file.handle,
5934 &io_status_block,6575 &io_status_block,
5935 &eof_info,6576 &eof_info,
5936 @sizeOf(windows.FILE.END_OF_FILE_INFORMATION),6577 @sizeOf(windows.FILE.END_OF_FILE_INFORMATION),
5937 .EndOfFile,6578 .EndOfFile,
5938 );6579 )) {
5939 switch (status) {6580 .SUCCESS => return syscall.finish(),
5940 .SUCCESS => return,6581 .CANCELLED => {
5941 .INVALID_HANDLE => |err| return windows.statusBug(err), // Handle not open for writing.6582 try syscall.checkCancel();
5942 .ACCESS_DENIED => return error.AccessDenied,6583 continue;
5943 .USER_MAPPED_FILE => return error.AccessDenied,6584 },
5944 .INVALID_PARAMETER => return error.FileTooBig,6585 .INVALID_HANDLE => |err| return syscall.ntstatusBug(err), // Handle not open for writing.
5945 else => return windows.unexpectedStatus(status),6586 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
5946 }6587 .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
6588 .INVALID_PARAMETER => return syscall.fail(error.FileTooBig),
6589 else => |status| return syscall.unexpectedNtstatus(status),
6590 };
5947 }6591 }
59486592
5949 if (native_os == .wasi and !builtin.link_libc) {6593 if (native_os == .wasi and !builtin.link_libc) {
5950 try current_thread.beginSyscall();6594 const syscall: Syscall = try .start();
5951 while (true) {6595 while (true) {
5952 switch (std.os.wasi.fd_filestat_set_size(file.handle, length)) {6596 switch (std.os.wasi.fd_filestat_set_size(file.handle, length)) {
5953 .SUCCESS => return current_thread.endSyscall(),6597 .SUCCESS => return syscall.finish(),
5954 .INTR => {6598 .INTR => {
5955 try current_thread.checkCancel();6599 try syscall.checkCancel();
5956 continue;6600 continue;
5957 },6601 },
5958 else => |e| {6602 else => |e| {
5959 current_thread.endSyscall();6603 syscall.finish();
5960 switch (e) {6604 switch (e) {
5961 .FBIG => return error.FileTooBig,6605 .FBIG => return error.FileTooBig,
5962 .IO => return error.InputOutput,6606 .IO => return error.InputOutput,
...@@ -5972,16 +6616,16 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE...@@ -5972,16 +6616,16 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE
5972 }6616 }
5973 }6617 }
59746618
5975 try current_thread.beginSyscall();6619 const syscall: Syscall = try .start();
5976 while (true) {6620 while (true) {
5977 switch (posix.errno(ftruncate_sym(file.handle, signed_len))) {6621 switch (posix.errno(ftruncate_sym(file.handle, signed_len))) {
5978 .SUCCESS => return current_thread.endSyscall(),6622 .SUCCESS => return syscall.finish(),
5979 .INTR => {6623 .INTR => {
5980 try current_thread.checkCancel();6624 try syscall.checkCancel();
5981 continue;6625 continue;
5982 },6626 },
5983 else => |e| {6627 else => |e| {
5984 current_thread.endSyscall();6628 syscall.finish();
5985 switch (e) {6629 switch (e) {
5986 .FBIG => return error.FileTooBig,6630 .FBIG => return error.FileTooBig,
5987 .IO => return error.InputOutput,6631 .IO => return error.InputOutput,
...@@ -5999,19 +6643,18 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE...@@ -5999,19 +6643,18 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE
5999fn fileSetOwner(userdata: ?*anyopaque, file: File, owner: ?File.Uid, group: ?File.Gid) File.SetOwnerError!void {6643fn fileSetOwner(userdata: ?*anyopaque, file: File, owner: ?File.Uid, group: ?File.Gid) File.SetOwnerError!void {
6000 if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.6644 if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.
6001 const t: *Threaded = @ptrCast(@alignCast(userdata));6645 const t: *Threaded = @ptrCast(@alignCast(userdata));
6002 const current_thread = Thread.getCurrent(t);6646 _ = t;
6003 const uid = owner orelse ~@as(posix.uid_t, 0);6647 const uid = owner orelse ~@as(posix.uid_t, 0);
6004 const gid = group orelse ~@as(posix.gid_t, 0);6648 const gid = group orelse ~@as(posix.gid_t, 0);
6005 return posixFchown(current_thread, file.handle, uid, gid);6649 return posixFchown(file.handle, uid, gid);
6006}6650}
60076651
6008fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permissions) File.SetPermissionsError!void {6652fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permissions) File.SetPermissionsError!void {
6009 if (@sizeOf(File.Permissions) == 0) return;6653 if (@sizeOf(File.Permissions) == 0) return;
6010 const t: *Threaded = @ptrCast(@alignCast(userdata));6654 const t: *Threaded = @ptrCast(@alignCast(userdata));
6011 const current_thread = Thread.getCurrent(t);6655 _ = t;
6012 switch (native_os) {6656 switch (native_os) {
6013 .windows => {6657 .windows => {
6014 try current_thread.checkCancel();
6015 var io_status_block: windows.IO_STATUS_BLOCK = undefined;6658 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6016 const info: windows.FILE.BASIC_INFORMATION = .{6659 const info: windows.FILE.BASIC_INFORMATION = .{
6017 .CreationTime = 0,6660 .CreationTime = 0,
...@@ -6020,37 +6663,41 @@ fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permi...@@ -6020,37 +6663,41 @@ fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permi
6020 .ChangeTime = 0,6663 .ChangeTime = 0,
6021 .FileAttributes = permissions.toAttributes(),6664 .FileAttributes = permissions.toAttributes(),
6022 };6665 };
6023 const status = windows.ntdll.NtSetInformationFile(6666 const syscall: Syscall = try .start();
6667 while (true) switch (windows.ntdll.NtSetInformationFile(
6024 file.handle,6668 file.handle,
6025 &io_status_block,6669 &io_status_block,
6026 &info,6670 &info,
6027 @sizeOf(windows.FILE.BASIC_INFORMATION),6671 @sizeOf(windows.FILE.BASIC_INFORMATION),
6028 .Basic,6672 .Basic,
6029 );6673 )) {
6030 switch (status) {6674 .SUCCESS => return syscall.finish(),
6031 .SUCCESS => return,6675 .CANCELLED => {
6032 .INVALID_HANDLE => |err| return windows.statusBug(err),6676 try syscall.checkCancel();
6033 .ACCESS_DENIED => return error.AccessDenied,6677 continue;
6034 else => return windows.unexpectedStatus(status),6678 },
6035 }6679 .INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
6680 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
6681 else => |status| return syscall.unexpectedNtstatus(status),
6682 };
6036 },6683 },
6037 .wasi => return error.Unexpected, // Unsupported OS.6684 .wasi => return error.Unexpected, // Unsupported OS.
6038 else => return setPermissionsPosix(current_thread, file.handle, permissions.toMode()),6685 else => return setPermissionsPosix(file.handle, permissions.toMode()),
6039 }6686 }
6040}6687}
60416688
6042fn setPermissionsPosix(current_thread: *Thread, fd: posix.fd_t, mode: posix.mode_t) File.SetPermissionsError!void {6689fn setPermissionsPosix(fd: posix.fd_t, mode: posix.mode_t) File.SetPermissionsError!void {
6043 comptime assert(have_fchmod);6690 comptime assert(have_fchmod);
6044 try current_thread.beginSyscall();6691 const syscall: Syscall = try .start();
6045 while (true) {6692 while (true) {
6046 switch (posix.errno(posix.system.fchmod(fd, mode))) {6693 switch (posix.errno(posix.system.fchmod(fd, mode))) {
6047 .SUCCESS => return current_thread.endSyscall(),6694 .SUCCESS => return syscall.finish(),
6048 .INTR => {6695 .INTR => {
6049 try current_thread.checkCancel();6696 try syscall.checkCancel();
6050 continue;6697 continue;
6051 },6698 },
6052 else => |e| {6699 else => |e| {
6053 current_thread.endSyscall();6700 syscall.finish();
6054 switch (e) {6701 switch (e) {
6055 .BADF => |err| return errnoBug(err),6702 .BADF => |err| return errnoBug(err),
6056 .FAULT => |err| return errnoBug(err),6703 .FAULT => |err| return errnoBug(err),
...@@ -6077,7 +6724,7 @@ fn dirSetTimestamps(...@@ -6077,7 +6724,7 @@ fn dirSetTimestamps(
6077 options: Dir.SetTimestampsOptions,6724 options: Dir.SetTimestampsOptions,
6078) Dir.SetTimestampsError!void {6725) Dir.SetTimestampsError!void {
6079 const t: *Threaded = @ptrCast(@alignCast(userdata));6726 const t: *Threaded = @ptrCast(@alignCast(userdata));
6080 const current_thread = Thread.getCurrent(t);6727 _ = t;
60816728
6082 if (is_windows) {6729 if (is_windows) {
6083 @panic("TODO implement dirSetTimestamps windows");6730 @panic("TODO implement dirSetTimestamps windows");
...@@ -6101,20 +6748,20 @@ fn dirSetTimestamps(...@@ -6101,20 +6748,20 @@ fn dirSetTimestamps(
6101 var path_buffer: [posix.PATH_MAX]u8 = undefined;6748 var path_buffer: [posix.PATH_MAX]u8 = undefined;
6102 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);6749 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
61036750
6104 try current_thread.beginSyscall();6751 const syscall: Syscall = try .start();
6105 while (true) switch (posix.errno(posix.system.utimensat(dir.handle, sub_path_posix, times, flags))) {6752 while (true) switch (posix.errno(posix.system.utimensat(dir.handle, sub_path_posix, times, flags))) {
6106 .SUCCESS => return current_thread.endSyscall(),6753 .SUCCESS => return syscall.finish(),
6107 .INTR => {6754 .INTR => {
6108 try current_thread.checkCancel();6755 try syscall.checkCancel();
6109 continue;6756 continue;
6110 },6757 },
6111 .BADF => |err| return current_thread.endSyscallErrnoBug(err), // always a race condition6758 .BADF => |err| return syscall.errnoBug(err), // always a race condition
6112 .FAULT => |err| return current_thread.endSyscallErrnoBug(err),6759 .FAULT => |err| return syscall.errnoBug(err),
6113 .INVAL => |err| return current_thread.endSyscallErrnoBug(err),6760 .INVAL => |err| return syscall.errnoBug(err),
6114 .ACCES => return current_thread.endSyscallError(error.AccessDenied),6761 .ACCES => return syscall.fail(error.AccessDenied),
6115 .PERM => return current_thread.endSyscallError(error.PermissionDenied),6762 .PERM => return syscall.fail(error.PermissionDenied),
6116 .ROFS => return current_thread.endSyscallError(error.ReadOnlyFileSystem),6763 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
6117 else => |err| return current_thread.endSyscallUnexpectedErrno(err),6764 else => |err| return syscall.unexpectedErrno(err),
6118 };6765 };
6119}6766}
61206767
...@@ -6124,11 +6771,9 @@ fn fileSetTimestamps(...@@ -6124,11 +6771,9 @@ fn fileSetTimestamps(
6124 options: File.SetTimestampsOptions,6771 options: File.SetTimestampsOptions,
6125) File.SetTimestampsError!void {6772) File.SetTimestampsError!void {
6126 const t: *Threaded = @ptrCast(@alignCast(userdata));6773 const t: *Threaded = @ptrCast(@alignCast(userdata));
6127 const current_thread = Thread.getCurrent(t);6774 _ = t;
61286775
6129 if (is_windows) {6776 if (is_windows) {
6130 try current_thread.checkCancel();
6131
6132 var access_time_buffer: windows.FILETIME = undefined;6777 var access_time_buffer: windows.FILETIME = undefined;
6133 var modify_time_buffer: windows.FILETIME = undefined;6778 var modify_time_buffer: windows.FILETIME = undefined;
6134 var system_time_buffer: windows.LARGE_INTEGER = undefined;6779 var system_time_buffer: windows.LARGE_INTEGER = undefined;
...@@ -6156,13 +6801,22 @@ fn fileSetTimestamps(...@@ -6156,13 +6801,22 @@ fn fileSetTimestamps(
6156 };6801 };
61576802
6158 // https://github.com/ziglang/zig/issues/18406803 // https://github.com/ziglang/zig/issues/1840
6159 const rc = windows.kernel32.SetFileTime(file.handle, null, access_ptr, modify_ptr);6804 const syscall: Syscall = try .start();
6160 if (rc == 0) {6805 while (true) {
6161 switch (windows.GetLastError()) {6806 switch (windows.kernel32.SetFileTime(file.handle, null, access_ptr, modify_ptr)) {
6162 else => |err| return windows.unexpectedError(err),6807 0 => switch (windows.GetLastError()) {
6808 .OPERATION_ABORTED => {
6809 try syscall.checkCancel();
6810 continue;
6811 },
6812 else => |err| {
6813 syscall.finish();
6814 return windows.unexpectedError(err);
6815 },
6816 },
6817 else => return syscall.finish(),
6163 }6818 }
6164 }6819 }
6165 return;
6166 }6820 }
61676821
6168 if (native_os == .wasi and !builtin.link_libc) {6822 if (native_os == .wasi and !builtin.link_libc) {
...@@ -6188,20 +6842,20 @@ fn fileSetTimestamps(...@@ -6188,20 +6842,20 @@ fn fileSetTimestamps(
6188 },6842 },
6189 }6843 }
61906844
6191 try current_thread.beginSyscall();6845 const syscall: Syscall = try .start();
6192 while (true) switch (std.os.wasi.fd_filestat_set_times(file.handle, atime, mtime, flags)) {6846 while (true) switch (std.os.wasi.fd_filestat_set_times(file.handle, atime, mtime, flags)) {
6193 .SUCCESS => return current_thread.endSyscall(),6847 .SUCCESS => return syscall.finish(),
6194 .INTR => {6848 .INTR => {
6195 try current_thread.checkCancel();6849 try syscall.checkCancel();
6196 continue;6850 continue;
6197 },6851 },
6198 .BADF => |err| return current_thread.endSyscallErrnoBug(err), // File descriptor use-after-free.6852 .BADF => |err| return syscall.errnoBug(err), // File descriptor use-after-free.
6199 .FAULT => |err| return current_thread.endSyscallErrnoBug(err),6853 .FAULT => |err| return syscall.errnoBug(err),
6200 .INVAL => |err| return current_thread.endSyscallErrnoBug(err),6854 .INVAL => |err| return syscall.errnoBug(err),
6201 .ACCES => return current_thread.endSyscallError(error.AccessDenied),6855 .ACCES => return syscall.fail(error.AccessDenied),
6202 .PERM => return current_thread.endSyscallError(error.PermissionDenied),6856 .PERM => return syscall.fail(error.PermissionDenied),
6203 .ROFS => return current_thread.endSyscallError(error.ReadOnlyFileSystem),6857 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
6204 else => |err| return current_thread.endSyscallUnexpectedErrno(err),6858 else => |err| return syscall.unexpectedErrno(err),
6205 };6859 };
6206 }6860 }
62076861
...@@ -6214,20 +6868,20 @@ fn fileSetTimestamps(...@@ -6214,20 +6868,20 @@ fn fileSetTimestamps(
6214 break :p &times_buffer;6868 break :p &times_buffer;
6215 };6869 };
62166870
6217 try current_thread.beginSyscall();6871 const syscall: Syscall = try .start();
6218 while (true) switch (posix.errno(posix.system.futimens(file.handle, times))) {6872 while (true) switch (posix.errno(posix.system.futimens(file.handle, times))) {
6219 .SUCCESS => return current_thread.endSyscall(),6873 .SUCCESS => return syscall.finish(),
6220 .INTR => {6874 .INTR => {
6221 try current_thread.checkCancel();6875 try syscall.checkCancel();
6222 continue;6876 continue;
6223 },6877 },
6224 .BADF => |err| return current_thread.endSyscallErrnoBug(err), // always a race condition6878 .BADF => |err| return syscall.errnoBug(err), // always a race condition
6225 .FAULT => |err| return current_thread.endSyscallErrnoBug(err),6879 .FAULT => |err| return syscall.errnoBug(err),
6226 .INVAL => |err| return current_thread.endSyscallErrnoBug(err),6880 .INVAL => |err| return syscall.errnoBug(err),
6227 .ACCES => return current_thread.endSyscallError(error.AccessDenied),6881 .ACCES => return syscall.fail(error.AccessDenied),
6228 .PERM => return current_thread.endSyscallError(error.PermissionDenied),6882 .PERM => return syscall.fail(error.PermissionDenied),
6229 .ROFS => return current_thread.endSyscallError(error.ReadOnlyFileSystem),6883 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
6230 else => |err| return current_thread.endSyscallUnexpectedErrno(err),6884 else => |err| return syscall.unexpectedErrno(err),
6231 };6885 };
6232}6886}
62336887
...@@ -6237,34 +6891,33 @@ const windows_lock_range_len: windows.LARGE_INTEGER = 1;...@@ -6237,34 +6891,33 @@ const windows_lock_range_len: windows.LARGE_INTEGER = 1;
6237fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!void {6891fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!void {
6238 if (native_os == .wasi) return error.FileLocksUnsupported;6892 if (native_os == .wasi) return error.FileLocksUnsupported;
6239 const t: *Threaded = @ptrCast(@alignCast(userdata));6893 const t: *Threaded = @ptrCast(@alignCast(userdata));
6240 const current_thread = Thread.getCurrent(t);6894 _ = t;
62416895
6242 if (is_windows) {6896 if (is_windows) {
6243 const exclusive = switch (lock) {6897 const exclusive = switch (lock) {
6244 .none => {6898 .none => {
6245 // To match the non-Windows behavior, unlock6899 // To match the non-Windows behavior, unlock
6246 var io_status_block: windows.IO_STATUS_BLOCK = undefined;6900 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6247 const status = windows.ntdll.NtUnlockFile(6901 while (true) switch (windows.ntdll.NtUnlockFile(
6248 file.handle,6902 file.handle,
6249 &io_status_block,6903 &io_status_block,
6250 &windows_lock_range_off,6904 &windows_lock_range_off,
6251 &windows_lock_range_len,6905 &windows_lock_range_len,
6252 0,6906 0,
6253 );6907 )) {
6254 switch (status) {6908 .SUCCESS => return,
6255 .SUCCESS => {},6909 .CANCELLED => continue,
6256 .RANGE_NOT_LOCKED => {},6910 .RANGE_NOT_LOCKED => return,
6257 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer6911 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
6258 else => return windows.unexpectedStatus(status),6912 else => |status| return windows.unexpectedStatus(status),
6259 }6913 };
6260 return;
6261 },6914 },
6262 .shared => false,6915 .shared => false,
6263 .exclusive => true,6916 .exclusive => true,
6264 };6917 };
6265 try current_thread.checkCancel();
6266 var io_status_block: windows.IO_STATUS_BLOCK = undefined;6918 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6267 const status = windows.ntdll.NtLockFile(6919 const syscall: Syscall = try .start();
6920 while (true) switch (windows.ntdll.NtLockFile(
6268 file.handle,6921 file.handle,
6269 null,6922 null,
6270 null,6923 null,
...@@ -6275,14 +6928,17 @@ fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!v...@@ -6275,14 +6928,17 @@ fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!v
6275 null,6928 null,
6276 windows.FALSE,6929 windows.FALSE,
6277 @intFromBool(exclusive),6930 @intFromBool(exclusive),
6278 );6931 )) {
6279 switch (status) {6932 .SUCCESS => return syscall.finish(),
6280 .SUCCESS => return,6933 .CANCELLED => {
6281 .INSUFFICIENT_RESOURCES => return error.SystemResources,6934 try syscall.checkCancel();
6282 .LOCK_NOT_GRANTED => |err| return windows.statusBug(err), // passed FailImmediately=false6935 continue;
6283 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer6936 },
6284 else => return windows.unexpectedStatus(status),6937 .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
6285 }6938 .LOCK_NOT_GRANTED => |err| return syscall.ntstatusBug(err), // passed FailImmediately=false
6939 .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
6940 else => |status| return syscall.unexpectedNtstatus(status),
6941 };
6286 }6942 }
62876943
6288 const operation: i32 = switch (lock) {6944 const operation: i32 = switch (lock) {
...@@ -6290,16 +6946,16 @@ fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!v...@@ -6290,16 +6946,16 @@ fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!v
6290 .shared => posix.LOCK.SH,6946 .shared => posix.LOCK.SH,
6291 .exclusive => posix.LOCK.EX,6947 .exclusive => posix.LOCK.EX,
6292 };6948 };
6293 try current_thread.beginSyscall();6949 const syscall: Syscall = try .start();
6294 while (true) {6950 while (true) {
6295 switch (posix.errno(posix.system.flock(file.handle, operation))) {6951 switch (posix.errno(posix.system.flock(file.handle, operation))) {
6296 .SUCCESS => return current_thread.endSyscall(),6952 .SUCCESS => return syscall.finish(),
6297 .INTR => {6953 .INTR => {
6298 try current_thread.checkCancel();6954 try syscall.checkCancel();
6299 continue;6955 continue;
6300 },6956 },
6301 else => |e| {6957 else => |e| {
6302 current_thread.endSyscall();6958 syscall.finish();
6303 switch (e) {6959 switch (e) {
6304 .BADF => |err| return errnoBug(err),6960 .BADF => |err| return errnoBug(err),
6305 .INVAL => |err| return errnoBug(err), // invalid parameters6961 .INVAL => |err| return errnoBug(err), // invalid parameters
...@@ -6316,33 +6972,33 @@ fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!v...@@ -6316,33 +6972,33 @@ fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!v
6316fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!bool {6972fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!bool {
6317 if (native_os == .wasi) return error.FileLocksUnsupported;6973 if (native_os == .wasi) return error.FileLocksUnsupported;
6318 const t: *Threaded = @ptrCast(@alignCast(userdata));6974 const t: *Threaded = @ptrCast(@alignCast(userdata));
6319 const current_thread = Thread.getCurrent(t);6975 _ = t;
63206976
6321 if (is_windows) {6977 if (is_windows) {
6322 const exclusive = switch (lock) {6978 const exclusive = switch (lock) {
6323 .none => {6979 .none => {
6324 // To match the non-Windows behavior, unlock6980 // To match the non-Windows behavior, unlock
6325 var io_status_block: windows.IO_STATUS_BLOCK = undefined;6981 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6326 const status = windows.ntdll.NtUnlockFile(6982 while (true) switch (windows.ntdll.NtUnlockFile(
6327 file.handle,6983 file.handle,
6328 &io_status_block,6984 &io_status_block,
6329 &windows_lock_range_off,6985 &windows_lock_range_off,
6330 &windows_lock_range_len,6986 &windows_lock_range_len,
6331 0,6987 0,
6332 );6988 )) {
6333 switch (status) {
6334 .SUCCESS => return true,6989 .SUCCESS => return true,
6990 .CANCELLED => continue,
6335 .RANGE_NOT_LOCKED => return false,6991 .RANGE_NOT_LOCKED => return false,
6336 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer6992 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
6337 else => return windows.unexpectedStatus(status),6993 else => |status| return windows.unexpectedStatus(status),
6338 }6994 };
6339 },6995 },
6340 .shared => false,6996 .shared => false,
6341 .exclusive => true,6997 .exclusive => true,
6342 };6998 };
6343 try current_thread.checkCancel();
6344 var io_status_block: windows.IO_STATUS_BLOCK = undefined;6999 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6345 const status = windows.ntdll.NtLockFile(7000 const syscall: Syscall = try .start();
7001 while (true) switch (windows.ntdll.NtLockFile(
6346 file.handle,7002 file.handle,
6347 null,7003 null,
6348 null,7004 null,
...@@ -6353,14 +7009,23 @@ fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockErro...@@ -6353,14 +7009,23 @@ fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockErro
6353 null,7009 null,
6354 windows.TRUE,7010 windows.TRUE,
6355 @intFromBool(exclusive),7011 @intFromBool(exclusive),
6356 );7012 )) {
6357 switch (status) {7013 .SUCCESS => {
6358 .SUCCESS => return true,7014 syscall.finish();
6359 .INSUFFICIENT_RESOURCES => return error.SystemResources,7015 return true;
6360 .LOCK_NOT_GRANTED => return false,7016 },
6361 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer7017 .LOCK_NOT_GRANTED => {
6362 else => return windows.unexpectedStatus(status),7018 syscall.finish();
6363 }7019 return false;
7020 },
7021 .CANCELLED => {
7022 try syscall.checkCancel();
7023 continue;
7024 },
7025 .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
7026 .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
7027 else => |status| return syscall.unexpectedNtstatus(status),
7028 };
6364 }7029 }
63657030
6366 const operation: i32 = switch (lock) {7031 const operation: i32 = switch (lock) {
...@@ -6368,23 +7033,23 @@ fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockErro...@@ -6368,23 +7033,23 @@ fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockErro
6368 .shared => posix.LOCK.SH | posix.LOCK.NB,7033 .shared => posix.LOCK.SH | posix.LOCK.NB,
6369 .exclusive => posix.LOCK.EX | posix.LOCK.NB,7034 .exclusive => posix.LOCK.EX | posix.LOCK.NB,
6370 };7035 };
6371 try current_thread.beginSyscall();7036 const syscall: Syscall = try .start();
6372 while (true) {7037 while (true) {
6373 switch (posix.errno(posix.system.flock(file.handle, operation))) {7038 switch (posix.errno(posix.system.flock(file.handle, operation))) {
6374 .SUCCESS => {7039 .SUCCESS => {
6375 current_thread.endSyscall();7040 syscall.finish();
6376 return true;7041 return true;
6377 },7042 },
6378 .INTR => {7043 .INTR => {
6379 try current_thread.checkCancel();7044 try syscall.checkCancel();
6380 continue;7045 continue;
6381 },7046 },
6382 .AGAIN => {7047 .AGAIN => {
6383 current_thread.endSyscall();7048 syscall.finish();
6384 return false;7049 return false;
6385 },7050 },
6386 else => |e| {7051 else => |e| {
6387 current_thread.endSyscall();7052 syscall.finish();
6388 switch (e) {7053 switch (e) {
6389 .BADF => |err| return errnoBug(err),7054 .BADF => |err| return errnoBug(err),
6390 .INVAL => |err| return errnoBug(err), // invalid parameters7055 .INVAL => |err| return errnoBug(err), // invalid parameters
...@@ -6404,20 +7069,19 @@ fn fileUnlock(userdata: ?*anyopaque, file: File) void {...@@ -6404,20 +7069,19 @@ fn fileUnlock(userdata: ?*anyopaque, file: File) void {
64047069
6405 if (is_windows) {7070 if (is_windows) {
6406 var io_status_block: windows.IO_STATUS_BLOCK = undefined;7071 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6407 const status = windows.ntdll.NtUnlockFile(7072 while (true) switch (windows.ntdll.NtUnlockFile(
6408 file.handle,7073 file.handle,
6409 &io_status_block,7074 &io_status_block,
6410 &windows_lock_range_off,7075 &windows_lock_range_off,
6411 &windows_lock_range_len,7076 &windows_lock_range_len,
6412 0,7077 0,
6413 );7078 )) {
6414 if (is_debug) switch (status) {7079 .SUCCESS => return,
6415 .SUCCESS => {},7080 .CANCELLED => continue,
6416 .RANGE_NOT_LOCKED => unreachable, // Function asserts unlocked.7081 .RANGE_NOT_LOCKED => if (is_debug) unreachable else return, // Function asserts unlocked.
6417 .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer7082 .ACCESS_VIOLATION => if (is_debug) unreachable else return, // bad io_status_block pointer
6418 else => unreachable, // Resource deallocation must succeed.7083 else => if (is_debug) unreachable else return, // Resource deallocation must succeed.
6419 };7084 };
6420 return;
6421 }7085 }
64227086
6423 while (true) {7087 while (true) {
...@@ -6437,17 +7101,17 @@ fn fileUnlock(userdata: ?*anyopaque, file: File) void {...@@ -6437,17 +7101,17 @@ fn fileUnlock(userdata: ?*anyopaque, file: File) void {
6437fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!void {7101fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!void {
6438 if (native_os == .wasi) return;7102 if (native_os == .wasi) return;
6439 const t: *Threaded = @ptrCast(@alignCast(userdata));7103 const t: *Threaded = @ptrCast(@alignCast(userdata));
6440 const current_thread = Thread.getCurrent(t);7104 _ = t;
64417105
6442 if (is_windows) {7106 if (is_windows) {
6443 try current_thread.checkCancel();
6444 // On Windows it works like a semaphore + exclusivity flag. To7107 // On Windows it works like a semaphore + exclusivity flag. To
6445 // implement this function, we first obtain another lock in shared7108 // implement this function, we first obtain another lock in shared
6446 // mode. This changes the exclusivity flag, but increments the7109 // mode. This changes the exclusivity flag, but increments the
6447 // semaphore to 2. So we follow up with an NtUnlockFile which7110 // semaphore to 2. So we follow up with an NtUnlockFile which
6448 // decrements the semaphore but does not modify the exclusivity flag.7111 // decrements the semaphore but does not modify the exclusivity flag.
6449 var io_status_block: windows.IO_STATUS_BLOCK = undefined;7112 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6450 switch (windows.ntdll.NtLockFile(7113 const syscall: Syscall = try .start();
7114 while (true) switch (windows.ntdll.NtLockFile(
6451 file.handle,7115 file.handle,
6452 null,7116 null,
6453 null,7117 null,
...@@ -6459,43 +7123,46 @@ fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!...@@ -6459,43 +7123,46 @@ fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!
6459 windows.TRUE,7123 windows.TRUE,
6460 windows.FALSE,7124 windows.FALSE,
6461 )) {7125 )) {
6462 .SUCCESS => {},7126 .SUCCESS => break syscall.finish(),
6463 .INSUFFICIENT_RESOURCES => |err| return windows.statusBug(err),7127 .CANCELLED => {
6464 .LOCK_NOT_GRANTED => |err| return windows.statusBug(err), // File was not locked in exclusive mode.7128 try syscall.checkCancel();
6465 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer7129 continue;
6466 else => |status| return windows.unexpectedStatus(status),7130 },
6467 }7131 .INSUFFICIENT_RESOURCES => |err| return syscall.ntstatusBug(err),
6468 const status = windows.ntdll.NtUnlockFile(7132 .LOCK_NOT_GRANTED => |err| return syscall.ntstatusBug(err), // File was not locked in exclusive mode.
7133 .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
7134 else => |status| return syscall.unexpectedNtstatus(status),
7135 };
7136 while (true) switch (windows.ntdll.NtUnlockFile(
6469 file.handle,7137 file.handle,
6470 &io_status_block,7138 &io_status_block,
6471 &windows_lock_range_off,7139 &windows_lock_range_off,
6472 &windows_lock_range_len,7140 &windows_lock_range_len,
6473 0,7141 0,
6474 );7142 )) {
6475 if (is_debug) switch (status) {7143 .SUCCESS => return,
6476 .SUCCESS => {},7144 .CANCELLED => continue,
6477 .RANGE_NOT_LOCKED => unreachable, // File was not locked.7145 .RANGE_NOT_LOCKED => if (is_debug) unreachable else return, // File was not locked.
6478 .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer7146 .ACCESS_VIOLATION => if (is_debug) unreachable else return, // bad io_status_block pointer
6479 else => unreachable, // Resource deallocation must succeed.7147 else => if (is_debug) unreachable else return, // Resource deallocation must succeed.
6480 };7148 };
6481 return;
6482 }7149 }
64837150
6484 const operation = posix.LOCK.SH | posix.LOCK.NB;7151 const operation = posix.LOCK.SH | posix.LOCK.NB;
64857152
6486 try current_thread.beginSyscall();7153 const syscall: Syscall = try .start();
6487 while (true) {7154 while (true) {
6488 switch (posix.errno(posix.system.flock(file.handle, operation))) {7155 switch (posix.errno(posix.system.flock(file.handle, operation))) {
6489 .SUCCESS => {7156 .SUCCESS => {
6490 current_thread.endSyscall();7157 syscall.finish();
6491 return;7158 return;
6492 },7159 },
6493 .INTR => {7160 .INTR => {
6494 try current_thread.checkCancel();7161 try syscall.checkCancel();
6495 continue;7162 continue;
6496 },7163 },
6497 else => |e| {7164 else => |e| {
6498 current_thread.endSyscall();7165 syscall.finish();
6499 switch (e) {7166 switch (e) {
6500 .AGAIN => |err| return errnoBug(err), // File was not locked in exclusive mode.7167 .AGAIN => |err| return errnoBug(err), // File was not locked in exclusive mode.
6501 .BADF => |err| return errnoBug(err),7168 .BADF => |err| return errnoBug(err),
...@@ -6517,7 +7184,7 @@ fn dirOpenDirWasi(...@@ -6517,7 +7184,7 @@ fn dirOpenDirWasi(
6517) Dir.OpenError!Dir {7184) Dir.OpenError!Dir {
6518 if (builtin.link_libc) return dirOpenDirPosix(userdata, dir, sub_path, options);7185 if (builtin.link_libc) return dirOpenDirPosix(userdata, dir, sub_path, options);
6519 const t: *Threaded = @ptrCast(@alignCast(userdata));7186 const t: *Threaded = @ptrCast(@alignCast(userdata));
6520 const current_thread = Thread.getCurrent(t);7187 _ = t;
6521 const wasi = std.os.wasi;7188 const wasi = std.os.wasi;
65227189
6523 var base: std.os.wasi.rights_t = .{7190 var base: std.os.wasi.rights_t = .{
...@@ -6547,19 +7214,19 @@ fn dirOpenDirWasi(...@@ -6547,19 +7214,19 @@ fn dirOpenDirWasi(
6547 const oflags: wasi.oflags_t = .{ .DIRECTORY = true };7214 const oflags: wasi.oflags_t = .{ .DIRECTORY = true };
6548 const fdflags: wasi.fdflags_t = .{};7215 const fdflags: wasi.fdflags_t = .{};
6549 var fd: posix.fd_t = undefined;7216 var fd: posix.fd_t = undefined;
6550 try current_thread.beginSyscall();7217 const syscall: Syscall = try .start();
6551 while (true) {7218 while (true) {
6552 switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, base, fdflags, &fd)) {7219 switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, base, fdflags, &fd)) {
6553 .SUCCESS => {7220 .SUCCESS => {
6554 current_thread.endSyscall();7221 syscall.finish();
6555 return .{ .handle = fd };7222 return .{ .handle = fd };
6556 },7223 },
6557 .INTR => {7224 .INTR => {
6558 try current_thread.checkCancel();7225 try syscall.checkCancel();
6559 continue;7226 continue;
6560 },7227 },
6561 else => |e| {7228 else => |e| {
6562 current_thread.endSyscall();7229 syscall.finish();
6563 switch (e) {7230 switch (e) {
6564 .FAULT => |err| return errnoBug(err),7231 .FAULT => |err| return errnoBug(err),
6565 .INVAL => return error.BadPathName,7232 .INVAL => return error.BadPathName,
...@@ -6594,13 +7261,13 @@ fn dirHardLink(...@@ -6594,13 +7261,13 @@ fn dirHardLink(
6594) Dir.HardLinkError!void {7261) Dir.HardLinkError!void {
6595 if (is_windows) return error.OperationUnsupported;7262 if (is_windows) return error.OperationUnsupported;
6596 const t: *Threaded = @ptrCast(@alignCast(userdata));7263 const t: *Threaded = @ptrCast(@alignCast(userdata));
6597 const current_thread = Thread.getCurrent(t);7264 _ = t;
65987265
6599 if (native_os == .wasi and !builtin.link_libc) {7266 if (native_os == .wasi and !builtin.link_libc) {
6600 const flags: std.os.wasi.lookupflags_t = .{7267 const flags: std.os.wasi.lookupflags_t = .{
6601 .SYMLINK_FOLLOW = options.follow_symlinks,7268 .SYMLINK_FOLLOW = options.follow_symlinks,
6602 };7269 };
6603 try current_thread.beginSyscall();7270 const syscall: Syscall = try .start();
6604 while (true) {7271 while (true) {
6605 switch (std.os.wasi.path_link(7272 switch (std.os.wasi.path_link(
6606 old_dir.handle,7273 old_dir.handle,
...@@ -6611,13 +7278,13 @@ fn dirHardLink(...@@ -6611,13 +7278,13 @@ fn dirHardLink(
6611 new_sub_path.ptr,7278 new_sub_path.ptr,
6612 new_sub_path.len,7279 new_sub_path.len,
6613 )) {7280 )) {
6614 .SUCCESS => return current_thread.endSyscall(),7281 .SUCCESS => return syscall.finish(),
6615 .INTR => {7282 .INTR => {
6616 try current_thread.checkCancel();7283 try syscall.checkCancel();
6617 continue;7284 continue;
6618 },7285 },
6619 else => |e| {7286 else => |e| {
6620 current_thread.endSyscall();7287 syscall.finish();
6621 switch (e) {7288 switch (e) {
6622 .ACCES => return error.AccessDenied,7289 .ACCES => return error.AccessDenied,
6623 .DQUOT => return error.DiskQuota,7290 .DQUOT => return error.DiskQuota,
...@@ -6651,7 +7318,7 @@ fn dirHardLink(...@@ -6651,7 +7318,7 @@ fn dirHardLink(
66517318
6652 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;7319 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
66537320
6654 try current_thread.beginSyscall();7321 const syscall: Syscall = try .start();
6655 while (true) {7322 while (true) {
6656 switch (posix.errno(posix.system.linkat(7323 switch (posix.errno(posix.system.linkat(
6657 old_dir.handle,7324 old_dir.handle,
...@@ -6660,13 +7327,13 @@ fn dirHardLink(...@@ -6660,13 +7327,13 @@ fn dirHardLink(
6660 new_sub_path_posix,7327 new_sub_path_posix,
6661 flags,7328 flags,
6662 ))) {7329 ))) {
6663 .SUCCESS => return current_thread.endSyscall(),7330 .SUCCESS => return syscall.finish(),
6664 .INTR => {7331 .INTR => {
6665 try current_thread.checkCancel();7332 try syscall.checkCancel();
6666 continue;7333 continue;
6667 },7334 },
6668 else => |e| {7335 else => |e| {
6669 current_thread.endSyscall();7336 syscall.finish();
6670 switch (e) {7337 switch (e) {
6671 .ACCES => return error.AccessDenied,7338 .ACCES => return error.AccessDenied,
6672 .DQUOT => return error.DiskQuota,7339 .DQUOT => return error.DiskQuota,
...@@ -6705,7 +7372,7 @@ const fileReadStreaming = switch (native_os) {...@@ -6705,7 +7372,7 @@ const fileReadStreaming = switch (native_os) {
67057372
6706fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {7373fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
6707 const t: *Threaded = @ptrCast(@alignCast(userdata));7374 const t: *Threaded = @ptrCast(@alignCast(userdata));
6708 const current_thread = Thread.getCurrent(t);7375 _ = t;
67097376
6710 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;7377 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
6711 var i: usize = 0;7378 var i: usize = 0;
...@@ -6721,20 +7388,20 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)...@@ -6721,20 +7388,20 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)
6721 assert(dest[0].len > 0);7388 assert(dest[0].len > 0);
67227389
6723 if (native_os == .wasi and !builtin.link_libc) {7390 if (native_os == .wasi and !builtin.link_libc) {
6724 try current_thread.beginSyscall();7391 const syscall: Syscall = try .start();
6725 while (true) {7392 while (true) {
6726 var nread: usize = undefined;7393 var nread: usize = undefined;
6727 switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {7394 switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {
6728 .SUCCESS => {7395 .SUCCESS => {
6729 current_thread.endSyscall();7396 syscall.finish();
6730 return nread;7397 return nread;
6731 },7398 },
6732 .INTR => {7399 .INTR => {
6733 try current_thread.checkCancel();7400 try syscall.checkCancel();
6734 continue;7401 continue;
6735 },7402 },
6736 else => |e| {7403 else => |e| {
6737 current_thread.endSyscall();7404 syscall.finish();
6738 switch (e) {7405 switch (e) {
6739 .INVAL => |err| return errnoBug(err),7406 .INVAL => |err| return errnoBug(err),
6740 .FAULT => |err| return errnoBug(err),7407 .FAULT => |err| return errnoBug(err),
...@@ -6754,20 +7421,20 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)...@@ -6754,20 +7421,20 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)
6754 }7421 }
6755 }7422 }
67567423
6757 try current_thread.beginSyscall();7424 const syscall: Syscall = try .start();
6758 while (true) {7425 while (true) {
6759 const rc = posix.system.readv(file.handle, dest.ptr, @intCast(dest.len));7426 const rc = posix.system.readv(file.handle, dest.ptr, @intCast(dest.len));
6760 switch (posix.errno(rc)) {7427 switch (posix.errno(rc)) {
6761 .SUCCESS => {7428 .SUCCESS => {
6762 current_thread.endSyscall();7429 syscall.finish();
6763 return @intCast(rc);7430 return @intCast(rc);
6764 },7431 },
6765 .INTR => {7432 .INTR => {
6766 try current_thread.checkCancel();7433 try syscall.checkCancel();
6767 continue;7434 continue;
6768 },7435 },
6769 else => |e| {7436 else => |e| {
6770 current_thread.endSyscall();7437 syscall.finish();
6771 switch (e) {7438 switch (e) {
6772 .INVAL => |err| return errnoBug(err),7439 .INVAL => |err| return errnoBug(err),
6773 .FAULT => |err| return errnoBug(err),7440 .FAULT => |err| return errnoBug(err),
...@@ -6792,7 +7459,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)...@@ -6792,7 +7459,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)
67927459
6793fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {7460fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
6794 const t: *Threaded = @ptrCast(@alignCast(userdata));7461 const t: *Threaded = @ptrCast(@alignCast(userdata));
6795 const current_thread = Thread.getCurrent(t);7462 _ = t;
67967463
6797 const DWORD = windows.DWORD;7464 const DWORD = windows.DWORD;
6798 var index: usize = 0;7465 var index: usize = 0;
...@@ -6801,28 +7468,41 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u...@@ -6801,28 +7468,41 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u
6801 const buffer = data[index];7468 const buffer = data[index];
6802 const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);7469 const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
68037470
7471 const syscall: Syscall = try .start();
6804 while (true) {7472 while (true) {
6805 try current_thread.checkCancel();
6806 var n: DWORD = undefined;7473 var n: DWORD = undefined;
6807 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) != 0)7474 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) != 0) {
7475 syscall.finish();
6808 return n;7476 return n;
7477 }
6809 switch (windows.GetLastError()) {7478 switch (windows.GetLastError()) {
6810 .IO_PENDING => |err| return windows.errorBug(err),7479 .IO_PENDING => |err| {
6811 .OPERATION_ABORTED => continue,7480 syscall.finish();
6812 .BROKEN_PIPE => return 0,7481 return windows.errorBug(err);
6813 .HANDLE_EOF => return 0,7482 },
6814 .NETNAME_DELETED => return error.ConnectionResetByPeer,7483 .OPERATION_ABORTED => {
6815 .LOCK_VIOLATION => return error.LockViolation,7484 try syscall.checkCancel();
6816 .ACCESS_DENIED => return error.AccessDenied,7485 continue;
6817 .INVALID_HANDLE => return error.NotOpenForReading,7486 },
6818 else => |err| return windows.unexpectedError(err),7487 .BROKEN_PIPE, .HANDLE_EOF => {
7488 syscall.finish();
7489 return 0;
7490 },
7491 .NETNAME_DELETED => return syscall.fail(error.ConnectionResetByPeer),
7492 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
7493 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
7494 .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),
7495 else => |err| {
7496 syscall.finish();
7497 return windows.unexpectedError(err);
7498 },
6819 }7499 }
6820 }7500 }
6821}7501}
68227502
6823fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {7503fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
6824 const t: *Threaded = @ptrCast(@alignCast(userdata));7504 const t: *Threaded = @ptrCast(@alignCast(userdata));
6825 const current_thread = Thread.getCurrent(t);7505 _ = t;
68267506
6827 if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");7507 if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");
68287508
...@@ -6840,20 +7520,20 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8...@@ -6840,20 +7520,20 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8
6840 assert(dest[0].len > 0);7520 assert(dest[0].len > 0);
68417521
6842 if (native_os == .wasi and !builtin.link_libc) {7522 if (native_os == .wasi and !builtin.link_libc) {
6843 try current_thread.beginSyscall();7523 const syscall: Syscall = try .start();
6844 while (true) {7524 while (true) {
6845 var nread: usize = undefined;7525 var nread: usize = undefined;
6846 switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) {7526 switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) {
6847 .SUCCESS => {7527 .SUCCESS => {
6848 current_thread.endSyscall();7528 syscall.finish();
6849 return nread;7529 return nread;
6850 },7530 },
6851 .INTR => {7531 .INTR => {
6852 try current_thread.checkCancel();7532 try syscall.checkCancel();
6853 continue;7533 continue;
6854 },7534 },
6855 else => |e| {7535 else => |e| {
6856 current_thread.endSyscall();7536 syscall.finish();
6857 switch (e) {7537 switch (e) {
6858 .INVAL => |err| return errnoBug(err),7538 .INVAL => |err| return errnoBug(err),
6859 .FAULT => |err| return errnoBug(err),7539 .FAULT => |err| return errnoBug(err),
...@@ -6877,20 +7557,20 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8...@@ -6877,20 +7557,20 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8
6877 }7557 }
6878 }7558 }
68797559
6880 try current_thread.beginSyscall();7560 const syscall: Syscall = try .start();
6881 while (true) {7561 while (true) {
6882 const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));7562 const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
6883 switch (posix.errno(rc)) {7563 switch (posix.errno(rc)) {
6884 .SUCCESS => {7564 .SUCCESS => {
6885 current_thread.endSyscall();7565 syscall.finish();
6886 return @bitCast(rc);7566 return @bitCast(rc);
6887 },7567 },
6888 .INTR => {7568 .INTR => {
6889 try current_thread.checkCancel();7569 try syscall.checkCancel();
6890 continue;7570 continue;
6891 },7571 },
6892 else => |e| {7572 else => |e| {
6893 current_thread.endSyscall();7573 syscall.finish();
6894 switch (e) {7574 switch (e) {
6895 .INVAL => |err| return errnoBug(err),7575 .INVAL => |err| return errnoBug(err),
6896 .FAULT => |err| return errnoBug(err),7576 .FAULT => |err| return errnoBug(err),
...@@ -6923,7 +7603,7 @@ const fileReadPositional = switch (native_os) {...@@ -6923,7 +7603,7 @@ const fileReadPositional = switch (native_os) {
69237603
6924fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {7604fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
6925 const t: *Threaded = @ptrCast(@alignCast(userdata));7605 const t: *Threaded = @ptrCast(@alignCast(userdata));
6926 const current_thread = Thread.getCurrent(t);7606 _ = t;
69277607
6928 const DWORD = windows.DWORD;7608 const DWORD = windows.DWORD;
69297609
...@@ -6945,45 +7625,58 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []...@@ -6945,45 +7625,58 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []
6945 .hEvent = null,7625 .hEvent = null,
6946 };7626 };
69477627
7628 const syscall: Syscall = try .start();
6948 while (true) {7629 while (true) {
6949 try current_thread.checkCancel();
6950 var n: DWORD = undefined;7630 var n: DWORD = undefined;
6951 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0)7631 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0) {
7632 syscall.finish();
6952 return n;7633 return n;
7634 }
6953 switch (windows.GetLastError()) {7635 switch (windows.GetLastError()) {
6954 .IO_PENDING => |err| return windows.errorBug(err),7636 .IO_PENDING => |err| {
6955 .OPERATION_ABORTED => continue,7637 syscall.finish();
6956 .BROKEN_PIPE => return 0,7638 return windows.errorBug(err);
6957 .HANDLE_EOF => return 0,7639 },
6958 .NETNAME_DELETED => return error.ConnectionResetByPeer,7640 .OPERATION_ABORTED => {
6959 .LOCK_VIOLATION => return error.LockViolation,7641 try syscall.checkCancel();
6960 .ACCESS_DENIED => return error.AccessDenied,7642 continue;
6961 .INVALID_HANDLE => return error.NotOpenForReading,7643 },
6962 else => |err| return windows.unexpectedError(err),7644 .BROKEN_PIPE, .HANDLE_EOF => {
7645 syscall.finish();
7646 return 0;
7647 },
7648 .NETNAME_DELETED => return syscall.fail(error.ConnectionResetByPeer),
7649 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
7650 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
7651 .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),
7652 else => |err| {
7653 syscall.finish();
7654 return windows.unexpectedError(err);
7655 },
6963 }7656 }
6964 }7657 }
6965}7658}
69667659
6967fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {7660fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {
6968 const t: *Threaded = @ptrCast(@alignCast(userdata));7661 const t: *Threaded = @ptrCast(@alignCast(userdata));
6969 const current_thread = Thread.getCurrent(t);7662 _ = t;
6970 const fd = file.handle;7663 const fd = file.handle;
69717664
6972 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {7665 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
6973 var result: u64 = undefined;7666 var result: u64 = undefined;
6974 try current_thread.beginSyscall();7667 const syscall: Syscall = try .start();
6975 while (true) {7668 while (true) {
6976 switch (posix.errno(posix.system.llseek(fd, @bitCast(offset), &result, posix.SEEK.CUR))) {7669 switch (posix.errno(posix.system.llseek(fd, @bitCast(offset), &result, posix.SEEK.CUR))) {
6977 .SUCCESS => {7670 .SUCCESS => {
6978 current_thread.endSyscall();7671 syscall.finish();
6979 return;7672 return;
6980 },7673 },
6981 .INTR => {7674 .INTR => {
6982 try current_thread.checkCancel();7675 try syscall.checkCancel();
6983 continue;7676 continue;
6984 },7677 },
6985 else => |e| {7678 else => |e| {
6986 current_thread.endSyscall();7679 syscall.finish();
6987 switch (e) {7680 switch (e) {
6988 .BADF => |err| return errnoBug(err), // File descriptor used after closed.7681 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
6989 .INVAL => return error.Unseekable,7682 .INVAL => return error.Unseekable,
...@@ -6998,25 +7691,43 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi...@@ -6998,25 +7691,43 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
6998 }7691 }
69997692
7000 if (native_os == .windows) {7693 if (native_os == .windows) {
7001 try current_thread.checkCancel();7694 const syscall: Syscall = try .start();
7002 return windows.SetFilePointerEx_CURRENT(fd, offset);7695 while (true) {
7696 if (windows.kernel32.SetFilePointerEx(fd, offset, null, windows.FILE_CURRENT) != 0) {
7697 return syscall.finish();
7698 }
7699 switch (windows.GetLastError()) {
7700 .OPERATION_ABORTED => {
7701 try syscall.checkCancel();
7702 continue;
7703 },
7704 .INVALID_FUNCTION => return syscall.fail(error.Unseekable),
7705 .NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
7706 .INVALID_PARAMETER => unreachable,
7707 .INVALID_HANDLE => unreachable,
7708 else => |err| {
7709 syscall.finish();
7710 return windows.unexpectedError(err);
7711 },
7712 }
7713 }
7003 }7714 }
70047715
7005 if (native_os == .wasi and !builtin.link_libc) {7716 if (native_os == .wasi and !builtin.link_libc) {
7006 var new_offset: std.os.wasi.filesize_t = undefined;7717 var new_offset: std.os.wasi.filesize_t = undefined;
7007 try current_thread.beginSyscall();7718 const syscall: Syscall = try .start();
7008 while (true) {7719 while (true) {
7009 switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) {7720 switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
7010 .SUCCESS => {7721 .SUCCESS => {
7011 current_thread.endSyscall();7722 syscall.finish();
7012 return;7723 return;
7013 },7724 },
7014 .INTR => {7725 .INTR => {
7015 try current_thread.checkCancel();7726 try syscall.checkCancel();
7016 continue;7727 continue;
7017 },7728 },
7018 else => |e| {7729 else => |e| {
7019 current_thread.endSyscall();7730 syscall.finish();
7020 switch (e) {7731 switch (e) {
7021 .BADF => |err| return errnoBug(err), // File descriptor used after closed.7732 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
7022 .INVAL => return error.Unseekable,7733 .INVAL => return error.Unseekable,
...@@ -7033,19 +7744,19 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi...@@ -7033,19 +7744,19 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
70337744
7034 if (posix.SEEK == void) return error.Unseekable;7745 if (posix.SEEK == void) return error.Unseekable;
70357746
7036 try current_thread.beginSyscall();7747 const syscall: Syscall = try .start();
7037 while (true) {7748 while (true) {
7038 switch (posix.errno(lseek_sym(fd, offset, posix.SEEK.CUR))) {7749 switch (posix.errno(lseek_sym(fd, offset, posix.SEEK.CUR))) {
7039 .SUCCESS => {7750 .SUCCESS => {
7040 current_thread.endSyscall();7751 syscall.finish();
7041 return;7752 return;
7042 },7753 },
7043 .INTR => {7754 .INTR => {
7044 try current_thread.checkCancel();7755 try syscall.checkCancel();
7045 continue;7756 continue;
7046 },7757 },
7047 else => |e| {7758 else => |e| {
7048 current_thread.endSyscall();7759 syscall.finish();
7049 switch (e) {7760 switch (e) {
7050 .BADF => |err| return errnoBug(err), // File descriptor used after closed.7761 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
7051 .INVAL => return error.Unseekable,7762 .INVAL => return error.Unseekable,
...@@ -7061,29 +7772,52 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi...@@ -7061,29 +7772,52 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
70617772
7062fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {7773fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {
7063 const t: *Threaded = @ptrCast(@alignCast(userdata));7774 const t: *Threaded = @ptrCast(@alignCast(userdata));
7064 const current_thread = Thread.getCurrent(t);7775 _ = t;
7065 const fd = file.handle;7776 const fd = file.handle;
70667777
7067 if (native_os == .windows) {7778 if (native_os == .windows) {
7068 try current_thread.checkCancel();7779 // "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
7069 return windows.SetFilePointerEx_BEGIN(fd, offset);7780 // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
7781 // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
7782 const ipos: windows.LARGE_INTEGER = @bitCast(offset);
7783
7784 const syscall: Syscall = try .start();
7785 while (true) {
7786 if (windows.kernel32.SetFilePointerEx(fd, ipos, null, windows.FILE_BEGIN) != 0) {
7787 return syscall.finish();
7788 }
7789 switch (windows.GetLastError()) {
7790 .OPERATION_ABORTED => {
7791 try syscall.checkCancel();
7792 continue;
7793 },
7794 .INVALID_FUNCTION => return syscall.fail(error.Unseekable),
7795 .NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
7796 .INVALID_PARAMETER => unreachable,
7797 .INVALID_HANDLE => unreachable,
7798 else => |err| {
7799 syscall.finish();
7800 return windows.unexpectedError(err);
7801 },
7802 }
7803 }
7070 }7804 }
70717805
7072 if (native_os == .wasi and !builtin.link_libc) {7806 if (native_os == .wasi and !builtin.link_libc) {
7073 try current_thread.beginSyscall();7807 const syscall: Syscall = try .start();
7074 while (true) {7808 while (true) {
7075 var new_offset: std.os.wasi.filesize_t = undefined;7809 var new_offset: std.os.wasi.filesize_t = undefined;
7076 switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {7810 switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
7077 .SUCCESS => {7811 .SUCCESS => {
7078 current_thread.endSyscall();7812 syscall.finish();
7079 return;7813 return;
7080 },7814 },
7081 .INTR => {7815 .INTR => {
7082 try current_thread.checkCancel();7816 try syscall.checkCancel();
7083 continue;7817 continue;
7084 },7818 },
7085 else => |e| {7819 else => |e| {
7086 current_thread.endSyscall();7820 syscall.finish();
7087 switch (e) {7821 switch (e) {
7088 .BADF => |err| return errnoBug(err), // File descriptor used after closed.7822 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
7089 .INVAL => return error.Unseekable,7823 .INVAL => return error.Unseekable,
...@@ -7100,25 +7834,25 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi...@@ -7100,25 +7834,25 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi
71007834
7101 if (posix.SEEK == void) return error.Unseekable;7835 if (posix.SEEK == void) return error.Unseekable;
71027836
7103 return posixSeekTo(current_thread, fd, offset);7837 return posixSeekTo(fd, offset);
7104}7838}
71057839
7106fn posixSeekTo(current_thread: *Thread, fd: posix.fd_t, offset: u64) File.SeekError!void {7840fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {
7107 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {7841 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
7108 try current_thread.beginSyscall();7842 const syscall: Syscall = try .start();
7109 while (true) {7843 while (true) {
7110 var result: u64 = undefined;7844 var result: u64 = undefined;
7111 switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) {7845 switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) {
7112 .SUCCESS => {7846 .SUCCESS => {
7113 current_thread.endSyscall();7847 syscall.finish();
7114 return;7848 return;
7115 },7849 },
7116 .INTR => {7850 .INTR => {
7117 try current_thread.checkCancel();7851 try syscall.checkCancel();
7118 continue;7852 continue;
7119 },7853 },
7120 else => |e| {7854 else => |e| {
7121 current_thread.endSyscall();7855 syscall.finish();
7122 switch (e) {7856 switch (e) {
7123 .BADF => |err| return errnoBug(err), // File descriptor used after closed.7857 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
7124 .INVAL => return error.Unseekable,7858 .INVAL => return error.Unseekable,
...@@ -7132,19 +7866,19 @@ fn posixSeekTo(current_thread: *Thread, fd: posix.fd_t, offset: u64) File.SeekEr...@@ -7132,19 +7866,19 @@ fn posixSeekTo(current_thread: *Thread, fd: posix.fd_t, offset: u64) File.SeekEr
7132 }7866 }
7133 }7867 }
71347868
7135 try current_thread.beginSyscall();7869 const syscall: Syscall = try .start();
7136 while (true) {7870 while (true) {
7137 switch (posix.errno(lseek_sym(fd, @bitCast(offset), posix.SEEK.SET))) {7871 switch (posix.errno(lseek_sym(fd, @bitCast(offset), posix.SEEK.SET))) {
7138 .SUCCESS => {7872 .SUCCESS => {
7139 current_thread.endSyscall();7873 syscall.finish();
7140 return;7874 return;
7141 },7875 },
7142 .INTR => {7876 .INTR => {
7143 try current_thread.checkCancel();7877 try syscall.checkCancel();
7144 continue;7878 continue;
7145 },7879 },
7146 else => |e| {7880 else => |e| {
7147 current_thread.endSyscall();7881 syscall.finish();
7148 switch (e) {7882 switch (e) {
7149 .BADF => |err| return errnoBug(err), // File descriptor used after closed.7883 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
7150 .INVAL => return error.Unseekable,7884 .INVAL => return error.Unseekable,
...@@ -7170,7 +7904,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.proce...@@ -7170,7 +7904,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.proce
7170 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;7904 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
7171 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];7905 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
7172 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);7906 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
7173 return dirOpenFileWtf16(t, null, prefixed_path_w.span(), flags);7907 return dirOpenFileWtf16(null, prefixed_path_w.span(), flags);
7174 },7908 },
7175 .driverkit,7909 .driverkit,
7176 .ios,7910 .ios,
...@@ -7234,22 +7968,21 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex...@@ -7234,22 +7968,21 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
7234 else => |e| return e,7968 else => |e| return e,
7235 },7969 },
7236 .freebsd, .dragonfly => {7970 .freebsd, .dragonfly => {
7237 const current_thread = Thread.getCurrent(t);
7238 var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };7971 var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
7239 var out_len: usize = out_buffer.len;7972 var out_len: usize = out_buffer.len;
7240 try current_thread.beginSyscall();7973 const syscall: Syscall = try .start();
7241 while (true) {7974 while (true) {
7242 switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {7975 switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
7243 .SUCCESS => {7976 .SUCCESS => {
7244 current_thread.endSyscall();7977 syscall.finish();
7245 return out_len - 1; // discard terminating NUL7978 return out_len - 1; // discard terminating NUL
7246 },7979 },
7247 .INTR => {7980 .INTR => {
7248 try current_thread.checkCancel();7981 try syscall.checkCancel();
7249 continue;7982 continue;
7250 },7983 },
7251 else => |e| {7984 else => |e| {
7252 current_thread.endSyscall();7985 syscall.finish();
7253 switch (e) {7986 switch (e) {
7254 .FAULT => |err| return errnoBug(err),7987 .FAULT => |err| return errnoBug(err),
7255 .PERM => return error.PermissionDenied,7988 .PERM => return error.PermissionDenied,
...@@ -7262,22 +7995,21 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex...@@ -7262,22 +7995,21 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
7262 }7995 }
7263 },7996 },
7264 .netbsd => {7997 .netbsd => {
7265 const current_thread = Thread.getCurrent(t);
7266 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };7998 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
7267 var out_len: usize = out_buffer.len;7999 var out_len: usize = out_buffer.len;
7268 try current_thread.beginSyscall();8000 const syscall: Syscall = try .start();
7269 while (true) {8001 while (true) {
7270 switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {8002 switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
7271 .SUCCESS => {8003 .SUCCESS => {
7272 current_thread.endSyscall();8004 syscall.finish();
7273 return out_len - 1; // discard terminating NUL8005 return out_len - 1; // discard terminating NUL
7274 },8006 },
7275 .INTR => {8007 .INTR => {
7276 try current_thread.checkCancel();8008 try syscall.checkCancel();
7277 continue;8009 continue;
7278 },8010 },
7279 else => |e| {8011 else => |e| {
7280 current_thread.endSyscall();8012 syscall.finish();
7281 switch (e) {8013 switch (e) {
7282 .FAULT => |err| return errnoBug(err),8014 .FAULT => |err| return errnoBug(err),
7283 .PERM => return error.PermissionDenied,8015 .PERM => return error.PermissionDenied,
...@@ -7295,20 +8027,19 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex...@@ -7295,20 +8027,19 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
7295 const argv0 = std.mem.span(t.argv0.value orelse return error.OperationUnsupported);8027 const argv0 = std.mem.span(t.argv0.value orelse return error.OperationUnsupported);
7296 if (std.mem.findScalar(u8, argv0, '/') != null) {8028 if (std.mem.findScalar(u8, argv0, '/') != null) {
7297 // argv[0] is a path (relative or absolute): use realpath(3) directly8029 // argv[0] is a path (relative or absolute): use realpath(3) directly
7298 const current_thread = Thread.getCurrent(t);
7299 var resolved_buf: [std.c.PATH_MAX]u8 = undefined;8030 var resolved_buf: [std.c.PATH_MAX]u8 = undefined;
7300 try current_thread.beginSyscall();8031 const syscall: Syscall = try .start();
7301 while (true) {8032 while (true) {
7302 if (std.c.realpath(argv0, &resolved_buf)) |p| {8033 if (std.c.realpath(argv0, &resolved_buf)) |p| {
7303 assert(p == &resolved_buf);8034 assert(p == &resolved_buf);
7304 break current_thread.endSyscall();8035 break syscall.finish();
7305 } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) {8036 } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) {
7306 .INTR => {8037 .INTR => {
7307 try current_thread.checkCancel();8038 try syscall.checkCancel();
7308 continue;8039 continue;
7309 },8040 },
7310 else => |e| {8041 else => |e| {
7311 current_thread.endSyscall();8042 syscall.finish();
7312 switch (e) {8043 switch (e) {
7313 .ACCES => return error.AccessDenied,8044 .ACCES => return error.AccessDenied,
7314 .INVAL => |err| return errnoBug(err), // the pathname argument is a null pointer8045 .INVAL => |err| return errnoBug(err), // the pathname argument is a null pointer
...@@ -7332,7 +8063,6 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex...@@ -7332,7 +8063,6 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
7332 // argv[0] is not empty (and not a path): search PATH8063 // argv[0] is not empty (and not a path): search PATH
7333 t.scanEnviron();8064 t.scanEnviron();
7334 const PATH = t.environ.string.PATH orelse return error.FileNotFound;8065 const PATH = t.environ.string.PATH orelse return error.FileNotFound;
7335 const current_thread = Thread.getCurrent(t);
7336 var it = std.mem.tokenizeScalar(u8, PATH, ':');8066 var it = std.mem.tokenizeScalar(u8, PATH, ':');
7337 it: while (it.next()) |dir| {8067 it: while (it.next()) |dir| {
7338 var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined;8068 var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined;
...@@ -7341,34 +8071,34 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex...@@ -7341,34 +8071,34 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
7341 }, 0) catch continue;8071 }, 0) catch continue;
73428072
7343 var resolved_buf: [std.c.PATH_MAX]u8 = undefined;8073 var resolved_buf: [std.c.PATH_MAX]u8 = undefined;
7344 try current_thread.beginSyscall();8074 const syscall: Syscall = try .start();
7345 while (true) {8075 while (true) {
7346 if (std.c.realpath(resolved_path, &resolved_buf)) |p| {8076 if (std.c.realpath(resolved_path, &resolved_buf)) |p| {
7347 assert(p == &resolved_buf);8077 assert(p == &resolved_buf);
7348 break current_thread.endSyscall();8078 break syscall.finish();
7349 } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) {8079 } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) {
7350 .INTR => {8080 .INTR => {
7351 try current_thread.checkCancel();8081 try syscall.checkCancel();
7352 continue;8082 continue;
7353 },8083 },
7354 .NAMETOOLONG => {8084 .NAMETOOLONG => {
7355 current_thread.endSyscall();8085 syscall.finish();
7356 return error.NameTooLong;8086 return error.NameTooLong;
7357 },8087 },
7358 .NOMEM => {8088 .NOMEM => {
7359 current_thread.endSyscall();8089 syscall.finish();
7360 return error.SystemResources;8090 return error.SystemResources;
7361 },8091 },
7362 .IO => {8092 .IO => {
7363 current_thread.endSyscall();8093 syscall.finish();
7364 return error.InputOutput;8094 return error.InputOutput;
7365 },8095 },
7366 .ACCES, .LOOP, .NOENT, .NOTDIR => {8096 .ACCES, .LOOP, .NOENT, .NOTDIR => {
7367 current_thread.endSyscall();8097 syscall.finish();
7368 continue :it;8098 continue :it;
7369 },8099 },
7370 else => |err| {8100 else => |err| {
7371 current_thread.endSyscall();8101 syscall.finish();
7372 return posix.unexpectedErrno(err);8102 return posix.unexpectedErrno(err);
7373 },8103 },
7374 }8104 }
...@@ -7383,8 +8113,6 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex...@@ -7383,8 +8113,6 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
7383 return error.FileNotFound;8113 return error.FileNotFound;
7384 },8114 },
7385 .windows => {8115 .windows => {
7386 const current_thread = Thread.getCurrent(t);
7387 try current_thread.checkCancel();
7388 const w = windows;8116 const w = windows;
7389 const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName;8117 const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName;
7390 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];8118 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
...@@ -7394,24 +8122,34 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex...@@ -7394,24 +8122,34 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
7394 // that the symlink points to, though, so we need to get the realpath.8122 // that the symlink points to, though, so we need to get the realpath.
7395 var path_name_w_buf = try w.wToPrefixedFileW(null, image_path_name);8123 var path_name_w_buf = try w.wToPrefixedFileW(null, image_path_name);
73968124
7397 const h_file = blk: {8125 const h_file = handle: {
7398 const res = w.OpenFile(path_name_w_buf.span(), .{8126 const syscall: Syscall = try .start();
7399 .dir = null,8127 while (true) {
7400 .access_mask = .{8128 if (w.OpenFile(path_name_w_buf.span(), .{
7401 .GENERIC = .{ .READ = true },8129 .dir = null,
7402 .STANDARD = .{ .SYNCHRONIZE = true },8130 .access_mask = .{
7403 },8131 .GENERIC = .{ .READ = true },
7404 .creation = .OPEN,8132 .STANDARD = .{ .SYNCHRONIZE = true },
7405 .filter = .any,8133 },
7406 }) catch |err| switch (err) {8134 .creation = .OPEN,
7407 error.WouldBlock => unreachable,8135 .filter = .any,
7408 else => |e| return e,8136 })) |handle| {
7409 };8137 syscall.finish();
7410 break :blk res;8138 break :handle handle;
8139 } else |err| switch (err) {
8140 error.WouldBlock => unreachable,
8141 error.OperationCanceled => {
8142 try syscall.checkCancel();
8143 continue;
8144 },
8145 else => |e| return e,
8146 }
8147 }
7411 };8148 };
7412 defer w.CloseHandle(h_file);8149 defer w.CloseHandle(h_file);
74138150
7414 // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks8151 // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks
8152 try Thread.checkCancel();
7415 const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);8153 const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);
74168154
7417 const len = std.unicode.calcWtf8Len(wide_slice);8155 const len = std.unicode.calcWtf8Len(wide_slice);
...@@ -7434,19 +8172,19 @@ fn fileWritePositional(...@@ -7434,19 +8172,19 @@ fn fileWritePositional(
7434 offset: u64,8172 offset: u64,
7435) File.WritePositionalError!usize {8173) File.WritePositionalError!usize {
7436 const t: *Threaded = @ptrCast(@alignCast(userdata));8174 const t: *Threaded = @ptrCast(@alignCast(userdata));
7437 const current_thread = Thread.getCurrent(t);8175 _ = t;
74388176
7439 if (is_windows) {8177 if (is_windows) {
7440 if (header.len != 0) {8178 if (header.len != 0) {
7441 return writeFilePositionalWindows(current_thread, file.handle, header, offset);8179 return writeFilePositionalWindows(file.handle, header, offset);
7442 }8180 }
7443 for (data[0 .. data.len - 1]) |buf| {8181 for (data[0 .. data.len - 1]) |buf| {
7444 if (buf.len == 0) continue;8182 if (buf.len == 0) continue;
7445 return writeFilePositionalWindows(current_thread, file.handle, buf, offset);8183 return writeFilePositionalWindows(file.handle, buf, offset);
7446 }8184 }
7447 const pattern = data[data.len - 1];8185 const pattern = data[data.len - 1];
7448 if (pattern.len == 0 or splat == 0) return 0;8186 if (pattern.len == 0 or splat == 0) return 0;
7449 return writeFilePositionalWindows(current_thread, file.handle, pattern, offset);8187 return writeFilePositionalWindows(file.handle, pattern, offset);
7450 }8188 }
74518189
7452 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;8190 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
...@@ -7484,19 +8222,19 @@ fn fileWritePositional(...@@ -7484,19 +8222,19 @@ fn fileWritePositional(
74848222
7485 if (native_os == .wasi and !builtin.link_libc) {8223 if (native_os == .wasi and !builtin.link_libc) {
7486 var n_written: usize = undefined;8224 var n_written: usize = undefined;
7487 try current_thread.beginSyscall();8225 const syscall: Syscall = try .start();
7488 while (true) {8226 while (true) {
7489 switch (std.os.wasi.fd_pwrite(file.handle, &iovecs, iovlen, offset, &n_written)) {8227 switch (std.os.wasi.fd_pwrite(file.handle, &iovecs, iovlen, offset, &n_written)) {
7490 .SUCCESS => {8228 .SUCCESS => {
7491 current_thread.endSyscall();8229 syscall.finish();
7492 return n_written;8230 return n_written;
7493 },8231 },
7494 .INTR => {8232 .INTR => {
7495 try current_thread.checkCancel();8233 try syscall.checkCancel();
7496 continue;8234 continue;
7497 },8235 },
7498 else => |e| {8236 else => |e| {
7499 current_thread.endSyscall();8237 syscall.finish();
7500 switch (e) {8238 switch (e) {
7501 .INVAL => |err| return errnoBug(err),8239 .INVAL => |err| return errnoBug(err),
7502 .FAULT => |err| return errnoBug(err),8240 .FAULT => |err| return errnoBug(err),
...@@ -7520,20 +8258,20 @@ fn fileWritePositional(...@@ -7520,20 +8258,20 @@ fn fileWritePositional(
7520 }8258 }
7521 }8259 }
75228260
7523 try current_thread.beginSyscall();8261 const syscall: Syscall = try .start();
7524 while (true) {8262 while (true) {
7525 const rc = pwritev_sym(file.handle, &iovecs, @intCast(iovlen), @bitCast(offset));8263 const rc = pwritev_sym(file.handle, &iovecs, @intCast(iovlen), @bitCast(offset));
7526 switch (posix.errno(rc)) {8264 switch (posix.errno(rc)) {
7527 .SUCCESS => {8265 .SUCCESS => {
7528 current_thread.endSyscall();8266 syscall.finish();
7529 return @intCast(rc);8267 return @intCast(rc);
7530 },8268 },
7531 .INTR => {8269 .INTR => {
7532 try current_thread.checkCancel();8270 try syscall.checkCancel();
7533 continue;8271 continue;
7534 },8272 },
7535 else => |e| {8273 else => |e| {
7536 current_thread.endSyscall();8274 syscall.finish();
7537 switch (e) {8275 switch (e) {
7538 .INVAL => |err| return errnoBug(err),8276 .INVAL => |err| return errnoBug(err),
7539 .FAULT => |err| return errnoBug(err),8277 .FAULT => |err| return errnoBug(err),
...@@ -7560,13 +8298,10 @@ fn fileWritePositional(...@@ -7560,13 +8298,10 @@ fn fileWritePositional(
7560}8298}
75618299
7562fn writeFilePositionalWindows(8300fn writeFilePositionalWindows(
7563 current_thread: *Thread,
7564 handle: windows.HANDLE,8301 handle: windows.HANDLE,
7565 bytes: []const u8,8302 bytes: []const u8,
7566 offset: u64,8303 offset: u64,
7567) File.WritePositionalError!usize {8304) File.WritePositionalError!usize {
7568 try current_thread.checkCancel();
7569
7570 var bytes_written: windows.DWORD = undefined;8305 var bytes_written: windows.DWORD = undefined;
7571 var overlapped: windows.OVERLAPPED = .{8306 var overlapped: windows.OVERLAPPED = .{
7572 .Internal = 0,8307 .Internal = 0,
...@@ -7580,21 +8315,31 @@ fn writeFilePositionalWindows(...@@ -7580,21 +8315,31 @@ fn writeFilePositionalWindows(
7580 .hEvent = null,8315 .hEvent = null,
7581 };8316 };
7582 const adjusted_len = std.math.lossyCast(u32, bytes.len);8317 const adjusted_len = std.math.lossyCast(u32, bytes.len);
7583 if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, &overlapped) == 0) {8318 const syscall: Syscall = try .start();
8319 while (true) {
8320 if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, &overlapped) != 0) {
8321 syscall.finish();
8322 return bytes_written;
8323 }
7584 switch (windows.GetLastError()) {8324 switch (windows.GetLastError()) {
7585 .INVALID_USER_BUFFER => return error.SystemResources,8325 .OPERATION_ABORTED => {
7586 .NOT_ENOUGH_MEMORY => return error.SystemResources,8326 try syscall.checkCancel();
7587 .OPERATION_ABORTED => return error.Canceled,8327 continue;
7588 .NOT_ENOUGH_QUOTA => return error.SystemResources,8328 },
7589 .NO_DATA => return error.BrokenPipe,8329 .INVALID_USER_BUFFER => return syscall.fail(error.SystemResources),
7590 .INVALID_HANDLE => return error.NotOpenForWriting,8330 .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources),
7591 .LOCK_VIOLATION => return error.LockViolation,8331 .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources),
7592 .ACCESS_DENIED => return error.AccessDenied,8332 .NO_DATA => return syscall.fail(error.BrokenPipe),
7593 .WORKING_SET_QUOTA => return error.SystemResources,8333 .INVALID_HANDLE => return syscall.fail(error.NotOpenForWriting),
7594 else => |err| return windows.unexpectedError(err),8334 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
8335 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
8336 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
8337 else => |err| {
8338 syscall.finish();
8339 return windows.unexpectedError(err);
8340 },
7595 }8341 }
7596 }8342 }
7597 return bytes_written;
7598}8343}
75998344
7600fn fileWriteStreaming(8345fn fileWriteStreaming(
...@@ -7605,19 +8350,19 @@ fn fileWriteStreaming(...@@ -7605,19 +8350,19 @@ fn fileWriteStreaming(
7605 splat: usize,8350 splat: usize,
7606) File.Writer.Error!usize {8351) File.Writer.Error!usize {
7607 const t: *Threaded = @ptrCast(@alignCast(userdata));8352 const t: *Threaded = @ptrCast(@alignCast(userdata));
7608 const current_thread = Thread.getCurrent(t);8353 _ = t;
76098354
7610 if (is_windows) {8355 if (is_windows) {
7611 if (header.len != 0) {8356 if (header.len != 0) {
7612 return writeFileStreamingWindows(current_thread, file.handle, header);8357 return writeFileStreamingWindows(file.handle, header);
7613 }8358 }
7614 for (data[0 .. data.len - 1]) |buf| {8359 for (data[0 .. data.len - 1]) |buf| {
7615 if (buf.len == 0) continue;8360 if (buf.len == 0) continue;
7616 return writeFileStreamingWindows(current_thread, file.handle, buf);8361 return writeFileStreamingWindows(file.handle, buf);
7617 }8362 }
7618 const pattern = data[data.len - 1];8363 const pattern = data[data.len - 1];
7619 if (pattern.len == 0 or splat == 0) return 0;8364 if (pattern.len == 0 or splat == 0) return 0;
7620 return writeFileStreamingWindows(current_thread, file.handle, pattern);8365 return writeFileStreamingWindows(file.handle, pattern);
7621 }8366 }
76228367
7623 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;8368 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
...@@ -7655,19 +8400,19 @@ fn fileWriteStreaming(...@@ -7655,19 +8400,19 @@ fn fileWriteStreaming(
76558400
7656 if (native_os == .wasi and !builtin.link_libc) {8401 if (native_os == .wasi and !builtin.link_libc) {
7657 var n_written: usize = undefined;8402 var n_written: usize = undefined;
7658 try current_thread.beginSyscall();8403 const syscall: Syscall = try .start();
7659 while (true) {8404 while (true) {
7660 switch (std.os.wasi.fd_write(file.handle, &iovecs, iovlen, &n_written)) {8405 switch (std.os.wasi.fd_write(file.handle, &iovecs, iovlen, &n_written)) {
7661 .SUCCESS => {8406 .SUCCESS => {
7662 current_thread.endSyscall();8407 syscall.finish();
7663 return n_written;8408 return n_written;
7664 },8409 },
7665 .INTR => {8410 .INTR => {
7666 try current_thread.checkCancel();8411 try syscall.checkCancel();
7667 continue;8412 continue;
7668 },8413 },
7669 else => |e| {8414 else => |e| {
7670 current_thread.endSyscall();8415 syscall.finish();
7671 switch (e) {8416 switch (e) {
7672 .INVAL => |err| return errnoBug(err),8417 .INVAL => |err| return errnoBug(err),
7673 .FAULT => |err| return errnoBug(err),8418 .FAULT => |err| return errnoBug(err),
...@@ -7688,20 +8433,20 @@ fn fileWriteStreaming(...@@ -7688,20 +8433,20 @@ fn fileWriteStreaming(
7688 }8433 }
7689 }8434 }
76908435
7691 try current_thread.beginSyscall();8436 const syscall: Syscall = try .start();
7692 while (true) {8437 while (true) {
7693 const rc = posix.system.writev(file.handle, &iovecs, @intCast(iovlen));8438 const rc = posix.system.writev(file.handle, &iovecs, @intCast(iovlen));
7694 switch (posix.errno(rc)) {8439 switch (posix.errno(rc)) {
7695 .SUCCESS => {8440 .SUCCESS => {
7696 current_thread.endSyscall();8441 syscall.finish();
7697 return @intCast(rc);8442 return @intCast(rc);
7698 },8443 },
7699 .INTR => {8444 .INTR => {
7700 try current_thread.checkCancel();8445 try syscall.checkCancel();
7701 continue;8446 continue;
7702 },8447 },
7703 else => |e| {8448 else => |e| {
7704 current_thread.endSyscall();8449 syscall.finish();
7705 switch (e) {8450 switch (e) {
7706 .INVAL => |err| return errnoBug(err),8451 .INVAL => |err| return errnoBug(err),
7707 .FAULT => |err| return errnoBug(err),8452 .FAULT => |err| return errnoBug(err),
...@@ -7724,29 +8469,36 @@ fn fileWriteStreaming(...@@ -7724,29 +8469,36 @@ fn fileWriteStreaming(
7724}8469}
77258470
7726fn writeFileStreamingWindows(8471fn writeFileStreamingWindows(
7727 current_thread: *Thread,
7728 handle: windows.HANDLE,8472 handle: windows.HANDLE,
7729 bytes: []const u8,8473 bytes: []const u8,
7730) File.Writer.Error!usize {8474) File.Writer.Error!usize {
7731 try current_thread.checkCancel();
7732
7733 var bytes_written: windows.DWORD = undefined;8475 var bytes_written: windows.DWORD = undefined;
7734 const adjusted_len = std.math.lossyCast(u32, bytes.len);8476 const adjusted_len = std.math.lossyCast(u32, bytes.len);
7735 if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, null) == 0) {8477 const syscall: Syscall = try .start();
8478 while (true) {
8479 if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, null) != 0) {
8480 syscall.finish();
8481 return bytes_written;
8482 }
7736 switch (windows.GetLastError()) {8483 switch (windows.GetLastError()) {
7737 .INVALID_USER_BUFFER => return error.SystemResources,8484 .OPERATION_ABORTED => {
7738 .NOT_ENOUGH_MEMORY => return error.SystemResources,8485 try syscall.checkCancel();
7739 .OPERATION_ABORTED => return error.Canceled,8486 continue;
7740 .NOT_ENOUGH_QUOTA => return error.SystemResources,8487 },
7741 .NO_DATA => return error.BrokenPipe,8488 .INVALID_USER_BUFFER => return syscall.fail(error.SystemResources),
7742 .INVALID_HANDLE => return error.NotOpenForWriting,8489 .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources),
7743 .LOCK_VIOLATION => return error.LockViolation,8490 .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources),
7744 .ACCESS_DENIED => return error.AccessDenied,8491 .NO_DATA => return syscall.fail(error.BrokenPipe),
7745 .WORKING_SET_QUOTA => return error.SystemResources,8492 .INVALID_HANDLE => return syscall.fail(error.NotOpenForWriting),
7746 else => |err| return windows.unexpectedError(err),8493 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
8494 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
8495 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
8496 else => |err| {
8497 syscall.finish();
8498 return windows.unexpectedError(err);
8499 },
7747 }8500 }
7748 }8501 }
7749 return bytes_written;
7750}8502}
77518503
7752fn fileWriteFileStreaming(8504fn fileWriteFileStreaming(
...@@ -7807,40 +8559,39 @@ fn fileWriteFileStreaming(...@@ -7807,40 +8559,39 @@ fn fileWriteFileStreaming(
7807 const nbytes: usize = @min(file_limit, std.math.maxInt(usize));8559 const nbytes: usize = @min(file_limit, std.math.maxInt(usize));
7808 const flags = 0;8560 const flags = 0;
78098561
7810 const current_thread = Thread.getCurrent(t);8562 const syscall: Syscall = try .start();
7811 try current_thread.beginSyscall();
7812 while (true) {8563 while (true) {
7813 switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, nbytes, hdtr, &sbytes, flags))) {8564 switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, nbytes, hdtr, &sbytes, flags))) {
7814 .SUCCESS => {8565 .SUCCESS => {
7815 current_thread.endSyscall();8566 syscall.finish();
7816 break;8567 break;
7817 },8568 },
7818 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {8569 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
7819 // Give calling code chance to observe before trying8570 // Give calling code chance to observe before trying
7820 // something else.8571 // something else.
7821 current_thread.endSyscall();8572 syscall.finish();
7822 @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);8573 @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
7823 return 0;8574 return 0;
7824 },8575 },
7825 .INTR, .BUSY => {8576 .INTR, .BUSY => {
7826 if (sbytes == 0) {8577 if (sbytes == 0) {
7827 try current_thread.checkCancel();8578 try syscall.checkCancel();
7828 continue;8579 continue;
7829 } else {8580 } else {
7830 // Even if we are being canceled, there have been side8581 // Even if we are being canceled, there have been side
7831 // effects, so it is better to report those side8582 // effects, so it is better to report those side
7832 // effects to the caller.8583 // effects to the caller.
7833 current_thread.endSyscall();8584 syscall.finish();
7834 break;8585 break;
7835 }8586 }
7836 },8587 },
7837 .AGAIN => {8588 .AGAIN => {
7838 current_thread.endSyscall();8589 syscall.finish();
7839 if (sbytes == 0) return error.WouldBlock;8590 if (sbytes == 0) return error.WouldBlock;
7840 break;8591 break;
7841 },8592 },
7842 else => |e| {8593 else => |e| {
7843 current_thread.endSyscall();8594 syscall.finish();
7844 assert(error.Unexpected == switch (e) {8595 assert(error.Unexpected == switch (e) {
7845 .NOTCONN => return error.BrokenPipe,8596 .NOTCONN => return error.BrokenPipe,
7846 .IO => return error.InputOutput,8597 .IO => return error.InputOutput,
...@@ -7893,40 +8644,39 @@ fn fileWriteFileStreaming(...@@ -7893,40 +8644,39 @@ fn fileWriteFileStreaming(
7893 const max_count = std.math.maxInt(i32); // Avoid EINVAL.8644 const max_count = std.math.maxInt(i32); // Avoid EINVAL.
7894 var len: std.c.off_t = @min(file_limit, max_count);8645 var len: std.c.off_t = @min(file_limit, max_count);
7895 const flags = 0;8646 const flags = 0;
7896 const current_thread = Thread.getCurrent(t);8647 const syscall: Syscall = try .start();
7897 try current_thread.beginSyscall();
7898 while (true) {8648 while (true) {
7899 switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &len, hdtr, flags))) {8649 switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &len, hdtr, flags))) {
7900 .SUCCESS => {8650 .SUCCESS => {
7901 current_thread.endSyscall();8651 syscall.finish();
7902 break;8652 break;
7903 },8653 },
7904 .OPNOTSUPP, .NOTSOCK, .NOSYS => {8654 .OPNOTSUPP, .NOTSOCK, .NOSYS => {
7905 // Give calling code chance to observe before trying8655 // Give calling code chance to observe before trying
7906 // something else.8656 // something else.
7907 current_thread.endSyscall();8657 syscall.finish();
7908 @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);8658 @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
7909 return 0;8659 return 0;
7910 },8660 },
7911 .INTR => {8661 .INTR => {
7912 if (len == 0) {8662 if (len == 0) {
7913 try current_thread.checkCancel();8663 try syscall.checkCancel();
7914 continue;8664 continue;
7915 } else {8665 } else {
7916 // Even if we are being canceled, there have been side8666 // Even if we are being canceled, there have been side
7917 // effects, so it is better to report those side8667 // effects, so it is better to report those side
7918 // effects to the caller.8668 // effects to the caller.
7919 current_thread.endSyscall();8669 syscall.finish();
7920 break;8670 break;
7921 }8671 }
7922 },8672 },
7923 .AGAIN => {8673 .AGAIN => {
7924 current_thread.endSyscall();8674 syscall.finish();
7925 if (len == 0) return error.WouldBlock;8675 if (len == 0) return error.WouldBlock;
7926 break;8676 break;
7927 },8677 },
7928 else => |e| {8678 else => |e| {
7929 current_thread.endSyscall();8679 syscall.finish();
7930 assert(error.Unexpected == switch (e) {8680 assert(error.Unexpected == switch (e) {
7931 .NOTCONN => return error.BrokenPipe,8681 .NOTCONN => return error.BrokenPipe,
7932 .IO => return error.InputOutput,8682 .IO => return error.InputOutput,
...@@ -7973,28 +8723,27 @@ fn fileWriteFileStreaming(...@@ -7973,28 +8723,27 @@ fn fileWriteFileStreaming(
7973 .streaming_simple, .positional_simple => break :sf,8723 .streaming_simple, .positional_simple => break :sf,
7974 .failure => return error.ReadFailed,8724 .failure => return error.ReadFailed,
7975 };8725 };
7976 const current_thread = Thread.getCurrent(t);8726 const syscall: Syscall = try .start();
7977 try current_thread.beginSyscall();
7978 const n: usize = while (true) {8727 const n: usize = while (true) {
7979 const rc = sendfile_sym(out_fd, in_fd, off_ptr, count);8728 const rc = sendfile_sym(out_fd, in_fd, off_ptr, count);
7980 switch (posix.errno(rc)) {8729 switch (posix.errno(rc)) {
7981 .SUCCESS => {8730 .SUCCESS => {
7982 current_thread.endSyscall();8731 syscall.finish();
7983 break @intCast(rc);8732 break @intCast(rc);
7984 },8733 },
7985 .NOSYS, .INVAL => {8734 .NOSYS, .INVAL => {
7986 // Give calling code chance to observe before trying8735 // Give calling code chance to observe before trying
7987 // something else.8736 // something else.
7988 current_thread.endSyscall();8737 syscall.finish();
7989 @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);8738 @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
7990 return 0;8739 return 0;
7991 },8740 },
7992 .INTR => {8741 .INTR => {
7993 try current_thread.checkCancel();8742 try syscall.checkCancel();
7994 continue;8743 continue;
7995 },8744 },
7996 else => |e| {8745 else => |e| {
7997 current_thread.endSyscall();8746 syscall.finish();
7998 assert(error.Unexpected == switch (e) {8747 assert(error.Unexpected == switch (e) {
7999 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket8748 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
8000 .AGAIN => return error.WouldBlock,8749 .AGAIN => return error.WouldBlock,
...@@ -8050,30 +8799,29 @@ fn fileWriteFileStreaming(...@@ -8050,30 +8799,29 @@ fn fileWriteFileStreaming(
8050 .streaming => null,8799 .streaming => null,
8051 .failure => return error.ReadFailed,8800 .failure => return error.ReadFailed,
8052 };8801 };
8053 const current_thread = Thread.getCurrent(t);
8054 const n: usize = switch (native_os) {8802 const n: usize = switch (native_os) {
8055 .linux => n: {8803 .linux => n: {
8056 try current_thread.beginSyscall();8804 const syscall: Syscall = try .start();
8057 while (true) {8805 while (true) {
8058 const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, null, @intFromEnum(limit), 0);8806 const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, null, @intFromEnum(limit), 0);
8059 switch (linux_copy_file_range_sys.errno(rc)) {8807 switch (linux_copy_file_range_sys.errno(rc)) {
8060 .SUCCESS => {8808 .SUCCESS => {
8061 current_thread.endSyscall();8809 syscall.finish();
8062 break :n @intCast(rc);8810 break :n @intCast(rc);
8063 },8811 },
8064 .INTR => {8812 .INTR => {
8065 try current_thread.checkCancel();8813 try syscall.checkCancel();
8066 continue;8814 continue;
8067 },8815 },
8068 .OPNOTSUPP, .INVAL, .NOSYS => {8816 .OPNOTSUPP, .INVAL, .NOSYS => {
8069 // Give calling code chance to observe before trying8817 // Give calling code chance to observe before trying
8070 // something else.8818 // something else.
8071 current_thread.endSyscall();8819 syscall.finish();
8072 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);8820 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
8073 return 0;8821 return 0;
8074 },8822 },
8075 else => |e| {8823 else => |e| {
8076 current_thread.endSyscall();8824 syscall.finish();
8077 assert(error.Unexpected == switch (e) {8825 assert(error.Unexpected == switch (e) {
8078 .FBIG => return error.FileTooBig,8826 .FBIG => return error.FileTooBig,
8079 .IO => return error.InputOutput,8827 .IO => return error.InputOutput,
...@@ -8097,27 +8845,27 @@ fn fileWriteFileStreaming(...@@ -8097,27 +8845,27 @@ fn fileWriteFileStreaming(
8097 }8845 }
8098 },8846 },
8099 .freebsd => n: {8847 .freebsd => n: {
8100 try current_thread.beginSyscall();8848 const syscall: Syscall = try .start();
8101 while (true) {8849 while (true) {
8102 const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, null, @intFromEnum(limit), 0);8850 const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, null, @intFromEnum(limit), 0);
8103 switch (std.c.errno(rc)) {8851 switch (std.c.errno(rc)) {
8104 .SUCCESS => {8852 .SUCCESS => {
8105 current_thread.endSyscall();8853 syscall.finish();
8106 break :n @intCast(rc);8854 break :n @intCast(rc);
8107 },8855 },
8108 .INTR => {8856 .INTR => {
8109 try current_thread.checkCancel();8857 try syscall.checkCancel();
8110 continue;8858 continue;
8111 },8859 },
8112 .OPNOTSUPP, .INVAL, .NOSYS => {8860 .OPNOTSUPP, .INVAL, .NOSYS => {
8113 // Give calling code chance to observe before trying8861 // Give calling code chance to observe before trying
8114 // something else.8862 // something else.
8115 current_thread.endSyscall();8863 syscall.finish();
8116 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);8864 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
8117 return 0;8865 return 0;
8118 },8866 },
8119 else => |e| {8867 else => |e| {
8120 current_thread.endSyscall();8868 syscall.finish();
8121 assert(error.Unexpected == switch (e) {8869 assert(error.Unexpected == switch (e) {
8122 .FBIG => return error.FileTooBig,8870 .FBIG => return error.FileTooBig,
8123 .IO => return error.InputOutput,8871 .IO => return error.InputOutput,
...@@ -8226,30 +8974,29 @@ fn fileWriteFilePositional(...@@ -8226,30 +8974,29 @@ fn fileWriteFilePositional(
8226 .failure => return error.ReadFailed,8974 .failure => return error.ReadFailed,
8227 };8975 };
8228 var off_out: i64 = @intCast(offset);8976 var off_out: i64 = @intCast(offset);
8229 const current_thread = Thread.getCurrent(t);
8230 const n: usize = switch (native_os) {8977 const n: usize = switch (native_os) {
8231 .linux => n: {8978 .linux => n: {
8232 try current_thread.beginSyscall();8979 const syscall: Syscall = try .start();
8233 while (true) {8980 while (true) {
8234 const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, @intFromEnum(limit), 0);8981 const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, @intFromEnum(limit), 0);
8235 switch (linux_copy_file_range_sys.errno(rc)) {8982 switch (linux_copy_file_range_sys.errno(rc)) {
8236 .SUCCESS => {8983 .SUCCESS => {
8237 current_thread.endSyscall();8984 syscall.finish();
8238 break :n @intCast(rc);8985 break :n @intCast(rc);
8239 },8986 },
8240 .INTR => {8987 .INTR => {
8241 try current_thread.checkCancel();8988 try syscall.checkCancel();
8242 continue;8989 continue;
8243 },8990 },
8244 .OPNOTSUPP, .INVAL, .NOSYS => {8991 .OPNOTSUPP, .INVAL, .NOSYS => {
8245 // Give calling code chance to observe before trying8992 // Give calling code chance to observe before trying
8246 // something else.8993 // something else.
8247 current_thread.endSyscall();8994 syscall.finish();
8248 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);8995 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
8249 return 0;8996 return 0;
8250 },8997 },
8251 else => |e| {8998 else => |e| {
8252 current_thread.endSyscall();8999 syscall.finish();
8253 assert(error.Unexpected == switch (e) {9000 assert(error.Unexpected == switch (e) {
8254 .FBIG => return error.FileTooBig,9001 .FBIG => return error.FileTooBig,
8255 .IO => return error.InputOutput,9002 .IO => return error.InputOutput,
...@@ -8274,27 +9021,27 @@ fn fileWriteFilePositional(...@@ -8274,27 +9021,27 @@ fn fileWriteFilePositional(
8274 }9021 }
8275 },9022 },
8276 .freebsd => n: {9023 .freebsd => n: {
8277 try current_thread.beginSyscall();9024 const syscall: Syscall = try .start();
8278 while (true) {9025 while (true) {
8279 const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, @intFromEnum(limit), 0);9026 const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, @intFromEnum(limit), 0);
8280 switch (std.c.errno(rc)) {9027 switch (std.c.errno(rc)) {
8281 .SUCCESS => {9028 .SUCCESS => {
8282 current_thread.endSyscall();9029 syscall.finish();
8283 break :n @intCast(rc);9030 break :n @intCast(rc);
8284 },9031 },
8285 .INTR => {9032 .INTR => {
8286 try current_thread.checkCancel();9033 try syscall.checkCancel();
8287 continue;9034 continue;
8288 },9035 },
8289 .OPNOTSUPP, .INVAL, .NOSYS => {9036 .OPNOTSUPP, .INVAL, .NOSYS => {
8290 // Give calling code chance to observe before trying9037 // Give calling code chance to observe before trying
8291 // something else.9038 // something else.
8292 current_thread.endSyscall();9039 syscall.finish();
8293 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);9040 @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
8294 return 0;9041 return 0;
8295 },9042 },
8296 else => |e| {9043 else => |e| {
8297 current_thread.endSyscall();9044 syscall.finish();
8298 assert(error.Unexpected == switch (e) {9045 assert(error.Unexpected == switch (e) {
8299 .FBIG => return error.FileTooBig,9046 .FBIG => return error.FileTooBig,
8300 .IO => return error.InputOutput,9047 .IO => return error.InputOutput,
...@@ -8334,28 +9081,27 @@ fn fileWriteFilePositional(...@@ -8334,28 +9081,27 @@ fn fileWriteFilePositional(
8334 file_reader.interface.toss(n -| header.len);9081 file_reader.interface.toss(n -| header.len);
8335 return n;9082 return n;
8336 }9083 }
8337 const current_thread = Thread.getCurrent(t);9084 const syscall: Syscall = try .start();
8338 try current_thread.beginSyscall();
8339 while (true) {9085 while (true) {
8340 const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true });9086 const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true });
8341 switch (posix.errno(rc)) {9087 switch (posix.errno(rc)) {
8342 .SUCCESS => {9088 .SUCCESS => {
8343 current_thread.endSyscall();9089 syscall.finish();
8344 break;9090 break;
8345 },9091 },
8346 .INTR => {9092 .INTR => {
8347 try current_thread.checkCancel();9093 try syscall.checkCancel();
8348 continue;9094 continue;
8349 },9095 },
8350 .OPNOTSUPP => {9096 .OPNOTSUPP => {
8351 // Give calling code chance to observe before trying9097 // Give calling code chance to observe before trying
8352 // something else.9098 // something else.
8353 current_thread.endSyscall();9099 syscall.finish();
8354 @atomicStore(UseFcopyfile, &t.use_fcopyfile, .disabled, .monotonic);9100 @atomicStore(UseFcopyfile, &t.use_fcopyfile, .disabled, .monotonic);
8355 return 0;9101 return 0;
8356 },9102 },
8357 else => |e| {9103 else => |e| {
8358 current_thread.endSyscall();9104 syscall.finish();
8359 assert(error.Unexpected == switch (e) {9105 assert(error.Unexpected == switch (e) {
8360 .NOMEM => return error.SystemResources,9106 .NOMEM => return error.SystemResources,
8361 .INVAL => |err| errnoBug(err),9107 .INVAL => |err| errnoBug(err),
...@@ -8372,9 +9118,7 @@ fn fileWriteFilePositional(...@@ -8372,9 +9118,7 @@ fn fileWriteFilePositional(
8372 return error.Unimplemented;9118 return error.Unimplemented;
8373}9119}
83749120
8375fn nowPosix(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {9121fn nowPosix(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
8376 const t: *Threaded = @ptrCast(@alignCast(userdata));
8377 _ = t;
8378 const clock_id: posix.clockid_t = clockToPosix(clock);9122 const clock_id: posix.clockid_t = clockToPosix(clock);
8379 var tp: posix.timespec = undefined;9123 var tp: posix.timespec = undefined;
8380 switch (posix.errno(posix.system.clock_gettime(clock_id, &tp))) {9124 switch (posix.errno(posix.system.clock_gettime(clock_id, &tp))) {
...@@ -8384,15 +9128,17 @@ fn nowPosix(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp...@@ -8384,15 +9128,17 @@ fn nowPosix(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp
8384 }9128 }
8385}9129}
83869130
8387const now = switch (native_os) {9131fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
8388 .windows => nowWindows,
8389 .wasi => nowWasi,
8390 else => nowPosix,
8391};
8392
8393fn nowWindows(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
8394 const t: *Threaded = @ptrCast(@alignCast(userdata));9132 const t: *Threaded = @ptrCast(@alignCast(userdata));
8395 _ = t;9133 _ = t;
9134 return switch (native_os) {
9135 .windows => nowWindows(clock),
9136 .wasi => nowWasi(clock),
9137 else => nowPosix(clock),
9138 };
9139}
9140
9141fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
8396 switch (clock) {9142 switch (clock) {
8397 .real => {9143 .real => {
8398 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds9144 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds
...@@ -8425,25 +9171,24 @@ fn nowWindows(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestam...@@ -8425,25 +9171,24 @@ fn nowWindows(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestam
8425 }9171 }
8426}9172}
84279173
8428fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {9174fn nowWasi(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
8429 const t: *Threaded = @ptrCast(@alignCast(userdata));
8430 _ = t;
8431 var ns: std.os.wasi.timestamp_t = undefined;9175 var ns: std.os.wasi.timestamp_t = undefined;
8432 const err = std.os.wasi.clock_time_get(clockToWasi(clock), 1, &ns);9176 const err = std.os.wasi.clock_time_get(clockToWasi(clock), 1, &ns);
8433 if (err != .SUCCESS) return error.Unexpected;9177 if (err != .SUCCESS) return error.Unexpected;
8434 return .fromNanoseconds(ns);9178 return .fromNanoseconds(ns);
8435}9179}
84369180
8437const sleep = switch (native_os) {9181fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
8438 .windows => sleepWindows,
8439 .wasi => sleepWasi,
8440 .linux => sleepLinux,
8441 else => sleepPosix,
8442};
8443
8444fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
8445 const t: *Threaded = @ptrCast(@alignCast(userdata));9182 const t: *Threaded = @ptrCast(@alignCast(userdata));
8446 const current_thread = Thread.getCurrent(t);9183 if (use_parking_sleep) return parking_sleep.sleep(timeout);
9184 switch (native_os) {
9185 .wasi => return sleepWasi(t, timeout),
9186 .linux => return sleepLinux(timeout),
9187 else => return sleepPosix(t, timeout),
9188 }
9189}
9190
9191fn sleepLinux(timeout: Io.Timeout) Io.SleepError!void {
8447 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {9192 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
8448 .none => .awake,9193 .none => .awake,
8449 .duration => |d| d.clock,9194 .duration => |d| d.clock,
...@@ -8455,22 +9200,22 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -8455,22 +9200,22 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
8455 .deadline => |deadline| deadline.raw.nanoseconds,9200 .deadline => |deadline| deadline.raw.nanoseconds,
8456 };9201 };
8457 var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds);9202 var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds);
8458 try current_thread.beginSyscall();9203 const syscall: Syscall = try .start();
8459 while (true) {9204 while (true) {
8460 switch (std.os.linux.errno(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) {9205 switch (std.os.linux.errno(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) {
8461 .none, .duration => false,9206 .none, .duration => false,
8462 .deadline => true,9207 .deadline => true,
8463 } }, &timespec, &timespec))) {9208 } }, &timespec, &timespec))) {
8464 .SUCCESS => {9209 .SUCCESS => {
8465 current_thread.endSyscall();9210 syscall.finish();
8466 return;9211 return;
8467 },9212 },
8468 .INTR => {9213 .INTR => {
8469 try current_thread.checkCancel();9214 try syscall.checkCancel();
8470 continue;9215 continue;
8471 },9216 },
8472 else => |e| {9217 else => |e| {
8473 current_thread.endSyscall();9218 syscall.finish();
8474 switch (e) {9219 switch (e) {
8475 .INVAL => return error.UnsupportedClock,9220 .INVAL => return error.UnsupportedClock,
8476 else => |err| return posix.unexpectedErrno(err),9221 else => |err| return posix.unexpectedErrno(err),
...@@ -8480,23 +9225,7 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -8480,23 +9225,7 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
8480 }9225 }
8481}9226}
84829227
8483fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {9228fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void {
8484 const t: *Threaded = @ptrCast(@alignCast(userdata));
8485 const current_thread = Thread.getCurrent(t);
8486 const t_io = ioBasic(t);
8487 try current_thread.checkCancel();
8488 const ms = ms: {
8489 const d = (try timeout.toDurationFromNow(t_io)) orelse
8490 break :ms std.math.maxInt(windows.DWORD);
8491 break :ms std.math.lossyCast(windows.DWORD, d.raw.toMilliseconds());
8492 };
8493 // TODO: alertable true with checkCancel in a loop plus deadline
8494 _ = windows.kernel32.SleepEx(ms, windows.FALSE);
8495}
8496
8497fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
8498 const t: *Threaded = @ptrCast(@alignCast(userdata));
8499 const current_thread = Thread.getCurrent(t);
8500 const t_io = ioBasic(t);9229 const t_io = ioBasic(t);
8501 const w = std.os.wasi;9230 const w = std.os.wasi;
85029231
...@@ -8520,14 +9249,12 @@ fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -8520,14 +9249,12 @@ fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
8520 };9249 };
8521 var event: w.event_t = undefined;9250 var event: w.event_t = undefined;
8522 var nevents: usize = undefined;9251 var nevents: usize = undefined;
8523 try current_thread.beginSyscall();9252 const syscall: Syscall = try .start();
8524 _ = w.poll_oneoff(&in, &event, 1, &nevents);9253 _ = w.poll_oneoff(&in, &event, 1, &nevents);
8525 current_thread.endSyscall();9254 syscall.finish();
8526}9255}
85279256
8528fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {9257fn sleepPosix(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void {
8529 const t: *Threaded = @ptrCast(@alignCast(userdata));
8530 const current_thread = Thread.getCurrent(t);
8531 const t_io = ioBasic(t);9258 const t_io = ioBasic(t);
8532 const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;9259 const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;
8533 const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;9260 const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;
...@@ -8539,48 +9266,85 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -8539,48 +9266,85 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
8539 };9266 };
8540 break :t timestampToPosix(d.raw.toNanoseconds());9267 break :t timestampToPosix(d.raw.toNanoseconds());
8541 };9268 };
8542 try current_thread.beginSyscall();9269 const syscall: Syscall = try .start();
8543 while (true) {9270 while (true) {
8544 switch (posix.errno(posix.system.nanosleep(&timespec, &timespec))) {9271 switch (posix.errno(posix.system.nanosleep(&timespec, &timespec))) {
8545 .INTR => {9272 .INTR => {
8546 try current_thread.checkCancel();9273 try syscall.checkCancel();
8547 continue;9274 continue;
8548 },9275 },
8549 // This prong handles success as well as unexpected errors.9276 // This prong handles success as well as unexpected errors.
8550 else => return current_thread.endSyscall(),9277 else => return syscall.finish(),
8551 }9278 }
8552 }9279 }
8553}9280}
85549281
8555fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize {9282fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize {
8556 const t: *Threaded = @ptrCast(@alignCast(userdata));9283 const t: *Threaded = @ptrCast(@alignCast(userdata));
9284 _ = t;
85579285
8558 var event: Io.Event = .unset;9286 var num_completed: std.atomic.Value(u32) = .init(0);
85599287
8560 for (futures, 0..) |future, i| {9288 for (futures, 0..) |any_future, i| {
8561 const closure: *AsyncClosure = @ptrCast(@alignCast(future));9289 const future: *Future = @ptrCast(@alignCast(any_future));
8562 if (@atomicRmw(?*Io.Event, &closure.select_condition, .Xchg, &event, .seq_cst) == AsyncClosure.done_event) {9290 future.awaiter = &num_completed;
8563 for (futures[0..i]) |cleanup_future| {9291 const old_status = future.status.fetchOr(
8564 const cleanup_closure: *AsyncClosure = @ptrCast(@alignCast(cleanup_future));9292 .{ .tag = .pending_awaited, .thread = .null },
8565 if (@atomicRmw(?*Io.Event, &cleanup_closure.select_condition, .Xchg, null, .seq_cst) == AsyncClosure.done_event) {9293 .release, // release `future.awaiter`
8566 cleanup_closure.event.waitUncancelable(ioBasic(t)); // Ensure no reference to our stack-allocated event.9294 );
8567 }9295 switch (old_status.tag) {
8568 }9296 .pending => {},
8569 return i;9297 .pending_awaited => unreachable, // `await` raced with `select`
9298 .pending_canceled => unreachable, // `cancel` raced with `select`
9299 .done => {
9300 future.status.store(old_status, .monotonic);
9301 _ = finishSelect(&num_completed, futures[0..i]);
9302 return i;
9303 },
8570 }9304 }
8571 }9305 }
85729306
8573 try event.wait(ioBasic(t));9307 errdefer _ = finishSelect(&num_completed, futures);
85749308
8575 var result: ?usize = null;9309 while (true) {
8576 for (futures, 0..) |future, i| {9310 const n = num_completed.load(.acquire);
8577 const closure: *AsyncClosure = @ptrCast(@alignCast(future));9311 if (n > 0) break;
8578 if (@atomicRmw(?*Io.Event, &closure.select_condition, .Xchg, null, .seq_cst) == AsyncClosure.done_event) {9312 assert(n < futures.len);
8579 closure.event.waitUncancelable(ioBasic(t)); // Ensure no reference to our stack-allocated event.9313 try Thread.futexWait(&num_completed.raw, n, null);
8580 if (result == null) result = i; // In case multiple are ready, return first.9314 }
8581 }9315 return finishSelect(&num_completed, futures).?;
9316}
9317fn finishSelect(
9318 num_completed: *std.atomic.Value(u32),
9319 futures: []const *Io.AnyFuture,
9320) ?usize {
9321 var completed_index: ?usize = null;
9322 var expect_completed: u32 = 0;
9323 for (futures, 0..) |any_future, i| {
9324 const future: *Future = @ptrCast(@alignCast(any_future));
9325 // This operation will convert `.pending_awaited` to `.pending`, or leave `.done` untouched.
9326 switch (future.status.fetchAnd(
9327 .{ .tag = @enumFromInt(0b10), .thread = .all_ones },
9328 .monotonic,
9329 ).tag) {
9330 .pending_awaited => {},
9331 .pending => unreachable,
9332 .pending_canceled => unreachable,
9333 .done => {
9334 expect_completed += 1;
9335 completed_index = i;
9336 },
9337 }
9338 }
9339 // If any future has just finished, wait for it to signal `num_completed` to avoid dangling
9340 // references to stack memory.
9341 while (true) {
9342 const n = num_completed.load(.acquire);
9343 if (n == expect_completed) break;
9344 assert(n < expect_completed);
9345 Thread.futexWaitUncancelable(&num_completed.raw, n, null);
8582 }9346 }
8583 return result.?;9347 return completed_index;
8584}9348}
85859349
8586fn netListenIpPosix(9350fn netListenIpPosix(
...@@ -8590,37 +9354,37 @@ fn netListenIpPosix(...@@ -8590,37 +9354,37 @@ fn netListenIpPosix(
8590) IpAddress.ListenError!net.Server {9354) IpAddress.ListenError!net.Server {
8591 if (!have_networking) return error.NetworkDown;9355 if (!have_networking) return error.NetworkDown;
8592 const t: *Threaded = @ptrCast(@alignCast(userdata));9356 const t: *Threaded = @ptrCast(@alignCast(userdata));
8593 const current_thread = Thread.getCurrent(t);9357 _ = t;
8594 const family = posixAddressFamily(&address);9358 const family = posixAddressFamily(&address);
8595 const socket_fd = try openSocketPosix(current_thread, family, .{9359 const socket_fd = try openSocketPosix(family, .{
8596 .mode = options.mode,9360 .mode = options.mode,
8597 .protocol = options.protocol,9361 .protocol = options.protocol,
8598 });9362 });
8599 errdefer posix.close(socket_fd);9363 errdefer posix.close(socket_fd);
86009364
8601 if (options.reuse_address) {9365 if (options.reuse_address) {
8602 try setSocketOption(current_thread, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);9366 try setSocketOption(socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);
8603 if (@hasDecl(posix.SO, "REUSEPORT"))9367 if (@hasDecl(posix.SO, "REUSEPORT"))
8604 try setSocketOption(current_thread, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEPORT, 1);9368 try setSocketOption(socket_fd, posix.SOL.SOCKET, posix.SO.REUSEPORT, 1);
8605 }9369 }
86069370
8607 var storage: PosixAddress = undefined;9371 var storage: PosixAddress = undefined;
8608 var addr_len = addressToPosix(&address, &storage);9372 var addr_len = addressToPosix(&address, &storage);
8609 try posixBind(current_thread, socket_fd, &storage.any, addr_len);9373 try posixBind(socket_fd, &storage.any, addr_len);
86109374
8611 try current_thread.beginSyscall();9375 const syscall: Syscall = try .start();
8612 while (true) {9376 while (true) {
8613 switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {9377 switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {
8614 .SUCCESS => {9378 .SUCCESS => {
8615 current_thread.endSyscall();9379 syscall.finish();
8616 break;9380 break;
8617 },9381 },
8618 .INTR => {9382 .INTR => {
8619 try current_thread.checkCancel();9383 try syscall.checkCancel();
8620 continue;9384 continue;
8621 },9385 },
8622 else => |e| {9386 else => |e| {
8623 current_thread.endSyscall();9387 syscall.finish();
8624 switch (e) {9388 switch (e) {
8625 .ADDRINUSE => return error.AddressInUse,9389 .ADDRINUSE => return error.AddressInUse,
8626 .BADF => |err| return errnoBug(err), // File descriptor used after closed.9390 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -8630,7 +9394,7 @@ fn netListenIpPosix(...@@ -8630,7 +9394,7 @@ fn netListenIpPosix(
8630 }9394 }
8631 }9395 }
86329396
8633 try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len);9397 try posixGetSockName(socket_fd, &storage.any, &addr_len);
8634 return .{9398 return .{
8635 .socket = .{9399 .socket = .{
8636 .handle = socket_fd,9400 .handle = socket_fd,
...@@ -8646,9 +9410,8 @@ fn netListenIpWindows(...@@ -8646,9 +9410,8 @@ fn netListenIpWindows(
8646) IpAddress.ListenError!net.Server {9410) IpAddress.ListenError!net.Server {
8647 if (!have_networking) return error.NetworkDown;9411 if (!have_networking) return error.NetworkDown;
8648 const t: *Threaded = @ptrCast(@alignCast(userdata));9412 const t: *Threaded = @ptrCast(@alignCast(userdata));
8649 const current_thread = Thread.getCurrent(t);
8650 const family = posixAddressFamily(&address);9413 const family = posixAddressFamily(&address);
8651 const socket_handle = try openSocketWsa(t, current_thread, family, .{9414 const socket_handle = try openSocketWsa(t, family, .{
8652 .mode = options.mode,9415 .mode = options.mode,
8653 .protocol = options.protocol,9416 .protocol = options.protocol,
8654 });9417 });
...@@ -8660,27 +9423,27 @@ fn netListenIpWindows(...@@ -8660,27 +9423,27 @@ fn netListenIpWindows(
8660 var storage: WsaAddress = undefined;9423 var storage: WsaAddress = undefined;
8661 var addr_len = addressToWsa(&address, &storage);9424 var addr_len = addressToWsa(&address, &storage);
86629425
8663 try current_thread.beginSyscall();9426 var syscall: Syscall = try .start();
8664 while (true) {9427 while (true) {
8665 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);9428 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
8666 if (rc != ws2_32.SOCKET_ERROR) {9429 if (rc != ws2_32.SOCKET_ERROR) {
8667 current_thread.endSyscall();9430 syscall.finish();
8668 break;9431 break;
8669 }9432 }
8670 switch (ws2_32.WSAGetLastError()) {9433 switch (ws2_32.WSAGetLastError()) {
8671 .EINTR => {
8672 try current_thread.checkCancel();
8673 continue;
8674 },
8675 .NOTINITIALISED => {9434 .NOTINITIALISED => {
9435 syscall.finish();
8676 try initializeWsa(t);9436 try initializeWsa(t);
8677 try current_thread.checkCancel();9437 syscall = try .start();
9438 continue;
9439 },
9440 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9441 try syscall.checkCancel();
8678 continue;9442 continue;
8679 },9443 },
8680 else => |e| {9444 else => |e| {
8681 current_thread.endSyscall();9445 syscall.finish();
8682 switch (e) {9446 switch (e) {
8683 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
8684 .EADDRINUSE => return error.AddressInUse,9447 .EADDRINUSE => return error.AddressInUse,
8685 .EADDRNOTAVAIL => return error.AddressUnavailable,9448 .EADDRNOTAVAIL => return error.AddressUnavailable,
8686 .ENOTSOCK => |err| return wsaErrorBug(err),9449 .ENOTSOCK => |err| return wsaErrorBug(err),
...@@ -8694,27 +9457,27 @@ fn netListenIpWindows(...@@ -8694,27 +9457,27 @@ fn netListenIpWindows(
8694 }9457 }
8695 }9458 }
86969459
8697 try current_thread.beginSyscall();9460 syscall = try .start();
8698 while (true) {9461 while (true) {
8699 const rc = ws2_32.listen(socket_handle, options.kernel_backlog);9462 const rc = ws2_32.listen(socket_handle, options.kernel_backlog);
8700 if (rc != ws2_32.SOCKET_ERROR) {9463 if (rc != ws2_32.SOCKET_ERROR) {
8701 current_thread.endSyscall();9464 syscall.finish();
8702 break;9465 break;
8703 }9466 }
8704 switch (ws2_32.WSAGetLastError()) {9467 switch (ws2_32.WSAGetLastError()) {
8705 .EINTR => {
8706 try current_thread.checkCancel();
8707 continue;
8708 },
8709 .NOTINITIALISED => {9468 .NOTINITIALISED => {
9469 syscall.finish();
8710 try initializeWsa(t);9470 try initializeWsa(t);
8711 try current_thread.checkCancel();9471 syscall = try .start();
9472 continue;
9473 },
9474 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9475 try syscall.checkCancel();
8712 continue;9476 continue;
8713 },9477 },
8714 else => |e| {9478 else => |e| {
8715 current_thread.endSyscall();9479 syscall.finish();
8716 switch (e) {9480 switch (e) {
8717 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
8718 .ENETDOWN => return error.NetworkDown,9481 .ENETDOWN => return error.NetworkDown,
8719 .EADDRINUSE => return error.AddressInUse,9482 .EADDRINUSE => return error.AddressInUse,
8720 .EISCONN => |err| return wsaErrorBug(err),9483 .EISCONN => |err| return wsaErrorBug(err),
...@@ -8729,7 +9492,7 @@ fn netListenIpWindows(...@@ -8729,7 +9492,7 @@ fn netListenIpWindows(
8729 }9492 }
8730 }9493 }
87319494
8732 try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len);9495 try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
87339496
8734 return .{9497 return .{
8735 .socket = .{9498 .socket = .{
...@@ -8757,8 +9520,8 @@ fn netListenUnixPosix(...@@ -8757,8 +9520,8 @@ fn netListenUnixPosix(
8757) net.UnixAddress.ListenError!net.Socket.Handle {9520) net.UnixAddress.ListenError!net.Socket.Handle {
8758 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;9521 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
8759 const t: *Threaded = @ptrCast(@alignCast(userdata));9522 const t: *Threaded = @ptrCast(@alignCast(userdata));
8760 const current_thread = Thread.getCurrent(t);9523 _ = t;
8761 const socket_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {9524 const socket_fd = openSocketPosix(posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
8762 error.ProtocolUnsupportedBySystem => return error.AddressFamilyUnsupported,9525 error.ProtocolUnsupportedBySystem => return error.AddressFamilyUnsupported,
8763 error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,9526 error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,
8764 error.SocketModeUnsupported => return error.AddressFamilyUnsupported,9527 error.SocketModeUnsupported => return error.AddressFamilyUnsupported,
...@@ -8769,21 +9532,21 @@ fn netListenUnixPosix(...@@ -8769,21 +9532,21 @@ fn netListenUnixPosix(
87699532
8770 var storage: UnixAddress = undefined;9533 var storage: UnixAddress = undefined;
8771 const addr_len = addressUnixToPosix(address, &storage);9534 const addr_len = addressUnixToPosix(address, &storage);
8772 try posixBindUnix(current_thread, socket_fd, &storage.any, addr_len);9535 try posixBindUnix(socket_fd, &storage.any, addr_len);
87739536
8774 try current_thread.beginSyscall();9537 const syscall: Syscall = try .start();
8775 while (true) {9538 while (true) {
8776 switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {9539 switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {
8777 .SUCCESS => {9540 .SUCCESS => {
8778 current_thread.endSyscall();9541 syscall.finish();
8779 break;9542 break;
8780 },9543 },
8781 .INTR => {9544 .INTR => {
8782 try current_thread.checkCancel();9545 try syscall.checkCancel();
8783 continue;9546 continue;
8784 },9547 },
8785 else => |e| {9548 else => |e| {
8786 current_thread.endSyscall();9549 syscall.finish();
8787 switch (e) {9550 switch (e) {
8788 .ADDRINUSE => return error.AddressInUse,9551 .ADDRINUSE => return error.AddressInUse,
8789 .BADF => |err| return errnoBug(err), // File descriptor used after closed.9552 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -8803,9 +9566,8 @@ fn netListenUnixWindows(...@@ -8803,9 +9566,8 @@ fn netListenUnixWindows(
8803) net.UnixAddress.ListenError!net.Socket.Handle {9566) net.UnixAddress.ListenError!net.Socket.Handle {
8804 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;9567 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
8805 const t: *Threaded = @ptrCast(@alignCast(userdata));9568 const t: *Threaded = @ptrCast(@alignCast(userdata));
8806 const current_thread = Thread.getCurrent(t);
88079569
8808 const socket_handle = openSocketWsa(t, current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {9570 const socket_handle = openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
8809 error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,9571 error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,
8810 else => |e| return e,9572 else => |e| return e,
8811 };9573 };
...@@ -8814,24 +9576,24 @@ fn netListenUnixWindows(...@@ -8814,24 +9576,24 @@ fn netListenUnixWindows(
8814 var storage: WsaAddress = undefined;9576 var storage: WsaAddress = undefined;
8815 const addr_len = addressUnixToWsa(address, &storage);9577 const addr_len = addressUnixToWsa(address, &storage);
88169578
8817 try current_thread.beginSyscall();9579 var syscall: Syscall = try .start();
8818 while (true) {9580 while (true) {
8819 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);9581 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
8820 if (rc != ws2_32.SOCKET_ERROR) break;9582 if (rc != ws2_32.SOCKET_ERROR) break;
8821 switch (ws2_32.WSAGetLastError()) {9583 switch (ws2_32.WSAGetLastError()) {
8822 .EINTR => {
8823 try current_thread.checkCancel();
8824 continue;
8825 },
8826 .NOTINITIALISED => {9584 .NOTINITIALISED => {
9585 syscall.finish();
8827 try initializeWsa(t);9586 try initializeWsa(t);
8828 try current_thread.checkCancel();9587 syscall = try .start();
9588 continue;
9589 },
9590 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9591 try syscall.checkCancel();
8829 continue;9592 continue;
8830 },9593 },
8831 else => |e| {9594 else => |e| {
8832 current_thread.endSyscall();9595 syscall.finish();
8833 switch (e) {9596 switch (e) {
8834 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
8835 .EADDRINUSE => return error.AddressInUse,9597 .EADDRINUSE => return error.AddressInUse,
8836 .EADDRNOTAVAIL => return error.AddressUnavailable,9598 .EADDRNOTAVAIL => return error.AddressUnavailable,
8837 .ENOTSOCK => |err| return wsaErrorBug(err),9599 .ENOTSOCK => |err| return wsaErrorBug(err),
...@@ -8846,22 +9608,23 @@ fn netListenUnixWindows(...@@ -8846,22 +9608,23 @@ fn netListenUnixWindows(
8846 }9608 }
88479609
8848 while (true) {9610 while (true) {
8849 try current_thread.checkCancel();9611 try syscall.checkCancel();
8850 const rc = ws2_32.listen(socket_handle, options.kernel_backlog);9612 const rc = ws2_32.listen(socket_handle, options.kernel_backlog);
8851 if (rc != ws2_32.SOCKET_ERROR) {9613 if (rc != ws2_32.SOCKET_ERROR) {
8852 current_thread.endSyscall();9614 syscall.finish();
8853 return socket_handle;9615 return socket_handle;
8854 }9616 }
8855 switch (ws2_32.WSAGetLastError()) {9617 switch (ws2_32.WSAGetLastError()) {
8856 .EINTR => continue,9618 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => continue,
8857 .NOTINITIALISED => {9619 .NOTINITIALISED => {
9620 syscall.finish();
8858 try initializeWsa(t);9621 try initializeWsa(t);
9622 syscall = try .start();
8859 continue;9623 continue;
8860 },9624 },
8861 else => |e| {9625 else => |e| {
8862 current_thread.endSyscall();9626 syscall.finish();
8863 switch (e) {9627 switch (e) {
8864 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
8865 .ENETDOWN => return error.NetworkDown,9628 .ENETDOWN => return error.NetworkDown,
8866 .EADDRINUSE => return error.AddressInUse,9629 .EADDRINUSE => return error.AddressInUse,
8867 .EISCONN => |err| return wsaErrorBug(err),9630 .EISCONN => |err| return wsaErrorBug(err),
...@@ -8889,24 +9652,23 @@ fn netListenUnixUnavailable(...@@ -8889,24 +9652,23 @@ fn netListenUnixUnavailable(
8889}9652}
88909653
8891fn posixBindUnix(9654fn posixBindUnix(
8892 current_thread: *Thread,
8893 fd: posix.socket_t,9655 fd: posix.socket_t,
8894 addr: *const posix.sockaddr,9656 addr: *const posix.sockaddr,
8895 addr_len: posix.socklen_t,9657 addr_len: posix.socklen_t,
8896) !void {9658) !void {
8897 try current_thread.beginSyscall();9659 const syscall: Syscall = try .start();
8898 while (true) {9660 while (true) {
8899 switch (posix.errno(posix.system.bind(fd, addr, addr_len))) {9661 switch (posix.errno(posix.system.bind(fd, addr, addr_len))) {
8900 .SUCCESS => {9662 .SUCCESS => {
8901 current_thread.endSyscall();9663 syscall.finish();
8902 break;9664 break;
8903 },9665 },
8904 .INTR => {9666 .INTR => {
8905 try current_thread.checkCancel();9667 try syscall.checkCancel();
8906 continue;9668 continue;
8907 },9669 },
8908 else => |e| {9670 else => |e| {
8909 current_thread.endSyscall();9671 syscall.finish();
8910 switch (e) {9672 switch (e) {
8911 .ACCES => return error.AccessDenied,9673 .ACCES => return error.AccessDenied,
8912 .ADDRINUSE => return error.AddressInUse,9674 .ADDRINUSE => return error.AddressInUse,
...@@ -8933,24 +9695,23 @@ fn posixBindUnix(...@@ -8933,24 +9695,23 @@ fn posixBindUnix(
8933}9695}
89349696
8935fn posixBind(9697fn posixBind(
8936 current_thread: *Thread,
8937 socket_fd: posix.socket_t,9698 socket_fd: posix.socket_t,
8938 addr: *const posix.sockaddr,9699 addr: *const posix.sockaddr,
8939 addr_len: posix.socklen_t,9700 addr_len: posix.socklen_t,
8940) !void {9701) !void {
8941 try current_thread.beginSyscall();9702 const syscall: Syscall = try .start();
8942 while (true) {9703 while (true) {
8943 switch (posix.errno(posix.system.bind(socket_fd, addr, addr_len))) {9704 switch (posix.errno(posix.system.bind(socket_fd, addr, addr_len))) {
8944 .SUCCESS => {9705 .SUCCESS => {
8945 current_thread.endSyscall();9706 syscall.finish();
8946 break;9707 break;
8947 },9708 },
8948 .INTR => {9709 .INTR => {
8949 try current_thread.checkCancel();9710 try syscall.checkCancel();
8950 continue;9711 continue;
8951 },9712 },
8952 else => |e| {9713 else => |e| {
8953 current_thread.endSyscall();9714 syscall.finish();
8954 switch (e) {9715 switch (e) {
8955 .ADDRINUSE => return error.AddressInUse,9716 .ADDRINUSE => return error.AddressInUse,
8956 .BADF => |err| return errnoBug(err), // File descriptor used after closed.9717 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -8968,24 +9729,23 @@ fn posixBind(...@@ -8968,24 +9729,23 @@ fn posixBind(
8968}9729}
89699730
8970fn posixConnect(9731fn posixConnect(
8971 current_thread: *Thread,
8972 socket_fd: posix.socket_t,9732 socket_fd: posix.socket_t,
8973 addr: *const posix.sockaddr,9733 addr: *const posix.sockaddr,
8974 addr_len: posix.socklen_t,9734 addr_len: posix.socklen_t,
8975) !void {9735) !void {
8976 try current_thread.beginSyscall();9736 const syscall: Syscall = try .start();
8977 while (true) {9737 while (true) {
8978 switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) {9738 switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) {
8979 .SUCCESS => {9739 .SUCCESS => {
8980 current_thread.endSyscall();9740 syscall.finish();
8981 return;9741 return;
8982 },9742 },
8983 .INTR => {9743 .INTR => {
8984 try current_thread.checkCancel();9744 try syscall.checkCancel();
8985 continue;9745 continue;
8986 },9746 },
8987 else => |e| {9747 else => |e| {
8988 current_thread.endSyscall();9748 syscall.finish();
8989 switch (e) {9749 switch (e) {
8990 .ADDRNOTAVAIL => return error.AddressUnavailable,9750 .ADDRNOTAVAIL => return error.AddressUnavailable,
8991 .AFNOSUPPORT => return error.AddressFamilyUnsupported,9751 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
...@@ -9014,24 +9774,23 @@ fn posixConnect(...@@ -9014,24 +9774,23 @@ fn posixConnect(
9014}9774}
90159775
9016fn posixConnectUnix(9776fn posixConnectUnix(
9017 current_thread: *Thread,
9018 fd: posix.socket_t,9777 fd: posix.socket_t,
9019 addr: *const posix.sockaddr,9778 addr: *const posix.sockaddr,
9020 addr_len: posix.socklen_t,9779 addr_len: posix.socklen_t,
9021) !void {9780) !void {
9022 try current_thread.beginSyscall();9781 const syscall: Syscall = try .start();
9023 while (true) {9782 while (true) {
9024 switch (posix.errno(posix.system.connect(fd, addr, addr_len))) {9783 switch (posix.errno(posix.system.connect(fd, addr, addr_len))) {
9025 .SUCCESS => {9784 .SUCCESS => {
9026 current_thread.endSyscall();9785 syscall.finish();
9027 return;9786 return;
9028 },9787 },
9029 .INTR => {9788 .INTR => {
9030 try current_thread.checkCancel();9789 try syscall.checkCancel();
9031 continue;9790 continue;
9032 },9791 },
9033 else => |e| {9792 else => |e| {
9034 current_thread.endSyscall();9793 syscall.finish();
9035 switch (e) {9794 switch (e) {
9036 .AFNOSUPPORT => return error.AddressFamilyUnsupported,9795 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
9037 .AGAIN => return error.WouldBlock,9796 .AGAIN => return error.WouldBlock,
...@@ -9058,24 +9817,23 @@ fn posixConnectUnix(...@@ -9058,24 +9817,23 @@ fn posixConnectUnix(
9058}9817}
90599818
9060fn posixGetSockName(9819fn posixGetSockName(
9061 current_thread: *Thread,
9062 socket_fd: posix.fd_t,9820 socket_fd: posix.fd_t,
9063 addr: *posix.sockaddr,9821 addr: *posix.sockaddr,
9064 addr_len: *posix.socklen_t,9822 addr_len: *posix.socklen_t,
9065) !void {9823) !void {
9066 try current_thread.beginSyscall();9824 const syscall: Syscall = try .start();
9067 while (true) {9825 while (true) {
9068 switch (posix.errno(posix.system.getsockname(socket_fd, addr, addr_len))) {9826 switch (posix.errno(posix.system.getsockname(socket_fd, addr, addr_len))) {
9069 .SUCCESS => {9827 .SUCCESS => {
9070 current_thread.endSyscall();9828 syscall.finish();
9071 break;9829 break;
9072 },9830 },
9073 .INTR => {9831 .INTR => {
9074 try current_thread.checkCancel();9832 try syscall.checkCancel();
9075 continue;9833 continue;
9076 },9834 },
9077 else => |e| {9835 else => |e| {
9078 current_thread.endSyscall();9836 syscall.finish();
9079 switch (e) {9837 switch (e) {
9080 .BADF => |err| return errnoBug(err), // File descriptor used after closed.9838 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
9081 .FAULT => |err| return errnoBug(err),9839 .FAULT => |err| return errnoBug(err),
...@@ -9091,32 +9849,31 @@ fn posixGetSockName(...@@ -9091,32 +9849,31 @@ fn posixGetSockName(
90919849
9092fn wsaGetSockName(9850fn wsaGetSockName(
9093 t: *Threaded,9851 t: *Threaded,
9094 current_thread: *Thread,
9095 handle: ws2_32.SOCKET,9852 handle: ws2_32.SOCKET,
9096 addr: *ws2_32.sockaddr,9853 addr: *ws2_32.sockaddr,
9097 addr_len: *i32,9854 addr_len: *i32,
9098) !void {9855) !void {
9099 try current_thread.beginSyscall();9856 var syscall: Syscall = try .start();
9100 while (true) {9857 while (true) {
9101 const rc = ws2_32.getsockname(handle, addr, addr_len);9858 const rc = ws2_32.getsockname(handle, addr, addr_len);
9102 if (rc != ws2_32.SOCKET_ERROR) {9859 if (rc != ws2_32.SOCKET_ERROR) {
9103 current_thread.endSyscall();9860 syscall.finish();
9104 return;9861 return;
9105 }9862 }
9106 switch (ws2_32.WSAGetLastError()) {9863 switch (ws2_32.WSAGetLastError()) {
9107 .EINTR => {9864 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9108 try current_thread.checkCancel();9865 try syscall.checkCancel();
9109 continue;9866 continue;
9110 },9867 },
9111 .NOTINITIALISED => {9868 .NOTINITIALISED => {
9869 syscall.finish();
9112 try initializeWsa(t);9870 try initializeWsa(t);
9113 try current_thread.checkCancel();9871 syscall = try .start();
9114 continue;9872 continue;
9115 },9873 },
9116 else => |e| {9874 else => |e| {
9117 current_thread.endSyscall();9875 syscall.finish();
9118 switch (e) {9876 switch (e) {
9119 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
9120 .ENETDOWN => return error.NetworkDown,9877 .ENETDOWN => return error.NetworkDown,
9121 .EFAULT => |err| return wsaErrorBug(err),9878 .EFAULT => |err| return wsaErrorBug(err),
9122 .ENOTSOCK => |err| return wsaErrorBug(err),9879 .ENOTSOCK => |err| return wsaErrorBug(err),
...@@ -9128,21 +9885,21 @@ fn wsaGetSockName(...@@ -9128,21 +9885,21 @@ fn wsaGetSockName(
9128 }9885 }
9129}9886}
91309887
9131fn setSocketOption(current_thread: *Thread, fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void {9888fn setSocketOption(fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void {
9132 const o: []const u8 = @ptrCast(&option);9889 const o: []const u8 = @ptrCast(&option);
9133 try current_thread.beginSyscall();9890 const syscall: Syscall = try .start();
9134 while (true) {9891 while (true) {
9135 switch (posix.errno(posix.system.setsockopt(fd, level, opt_name, o.ptr, @intCast(o.len)))) {9892 switch (posix.errno(posix.system.setsockopt(fd, level, opt_name, o.ptr, @intCast(o.len)))) {
9136 .SUCCESS => {9893 .SUCCESS => {
9137 current_thread.endSyscall();9894 syscall.finish();
9138 return;9895 return;
9139 },9896 },
9140 .INTR => {9897 .INTR => {
9141 try current_thread.checkCancel();9898 try syscall.checkCancel();
9142 continue;9899 continue;
9143 },9900 },
9144 else => |e| {9901 else => |e| {
9145 current_thread.endSyscall();9902 syscall.finish();
9146 switch (e) {9903 switch (e) {
9147 .BADF => |err| return errnoBug(err), // File descriptor used after closed.9904 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
9148 .NOTSOCK => |err| return errnoBug(err),9905 .NOTSOCK => |err| return errnoBug(err),
...@@ -9157,21 +9914,30 @@ fn setSocketOption(current_thread: *Thread, fd: posix.fd_t, level: i32, opt_name...@@ -9157,21 +9914,30 @@ fn setSocketOption(current_thread: *Thread, fd: posix.fd_t, level: i32, opt_name
91579914
9158fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, opt_name: u32, option: u32) !void {9915fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, opt_name: u32, option: u32) !void {
9159 const o: []const u8 = @ptrCast(&option);9916 const o: []const u8 = @ptrCast(&option);
9917 var syscall: Syscall = try .start();
9160 const rc = ws2_32.setsockopt(socket, level, @bitCast(opt_name), o.ptr, @intCast(o.len));9918 const rc = ws2_32.setsockopt(socket, level, @bitCast(opt_name), o.ptr, @intCast(o.len));
9161 while (true) {9919 while (true) {
9162 if (rc != ws2_32.SOCKET_ERROR) return;9920 if (rc != ws2_32.SOCKET_ERROR) return syscall.finish();
9163 switch (ws2_32.WSAGetLastError()) {9921 switch (ws2_32.WSAGetLastError()) {
9164 .EINTR => continue,9922 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9165 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,9923 try syscall.checkCancel();
9924 continue;
9925 },
9166 .NOTINITIALISED => {9926 .NOTINITIALISED => {
9927 syscall.finish();
9167 try initializeWsa(t);9928 try initializeWsa(t);
9929 syscall = try .start();
9168 continue;9930 continue;
9169 },9931 },
9170 .ENETDOWN => return error.NetworkDown,9932 .ENETDOWN => return syscall.fail(error.NetworkDown),
9171 .EFAULT => |err| return wsaErrorBug(err),9933 .EFAULT, .ENOTSOCK, .EINVAL => |err| {
9172 .ENOTSOCK => |err| return wsaErrorBug(err),9934 syscall.finish();
9173 .EINVAL => |err| return wsaErrorBug(err),9935 return wsaErrorBug(err);
9174 else => |err| return windows.unexpectedWSAError(err),9936 },
9937 else => |err| {
9938 syscall.finish();
9939 return windows.unexpectedWSAError(err);
9940 },
9175 }9941 }
9176 }9942 }
9177}9943}
...@@ -9184,17 +9950,17 @@ fn netConnectIpPosix(...@@ -9184,17 +9950,17 @@ fn netConnectIpPosix(
9184 if (!have_networking) return error.NetworkDown;9950 if (!have_networking) return error.NetworkDown;
9185 if (options.timeout != .none) @panic("TODO implement netConnectIpPosix with timeout");9951 if (options.timeout != .none) @panic("TODO implement netConnectIpPosix with timeout");
9186 const t: *Threaded = @ptrCast(@alignCast(userdata));9952 const t: *Threaded = @ptrCast(@alignCast(userdata));
9187 const current_thread = Thread.getCurrent(t);9953 _ = t;
9188 const family = posixAddressFamily(address);9954 const family = posixAddressFamily(address);
9189 const socket_fd = try openSocketPosix(current_thread, family, .{9955 const socket_fd = try openSocketPosix(family, .{
9190 .mode = options.mode,9956 .mode = options.mode,
9191 .protocol = options.protocol,9957 .protocol = options.protocol,
9192 });9958 });
9193 errdefer posix.close(socket_fd);9959 errdefer posix.close(socket_fd);
9194 var storage: PosixAddress = undefined;9960 var storage: PosixAddress = undefined;
9195 var addr_len = addressToPosix(address, &storage);9961 var addr_len = addressToPosix(address, &storage);
9196 try posixConnect(current_thread, socket_fd, &storage.any, addr_len);9962 try posixConnect(socket_fd, &storage.any, addr_len);
9197 try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len);9963 try posixGetSockName(socket_fd, &storage.any, &addr_len);
9198 return .{ .socket = .{9964 return .{ .socket = .{
9199 .handle = socket_fd,9965 .handle = socket_fd,
9200 .address = addressFromPosix(&storage),9966 .address = addressFromPosix(&storage),
...@@ -9209,9 +9975,8 @@ fn netConnectIpWindows(...@@ -9209,9 +9975,8 @@ fn netConnectIpWindows(
9209 if (!have_networking) return error.NetworkDown;9975 if (!have_networking) return error.NetworkDown;
9210 if (options.timeout != .none) @panic("TODO implement netConnectIpWindows with timeout");9976 if (options.timeout != .none) @panic("TODO implement netConnectIpWindows with timeout");
9211 const t: *Threaded = @ptrCast(@alignCast(userdata));9977 const t: *Threaded = @ptrCast(@alignCast(userdata));
9212 const current_thread = Thread.getCurrent(t);
9213 const family = posixAddressFamily(address);9978 const family = posixAddressFamily(address);
9214 const socket_handle = try openSocketWsa(t, current_thread, family, .{9979 const socket_handle = try openSocketWsa(t, family, .{
9215 .mode = options.mode,9980 .mode = options.mode,
9216 .protocol = options.protocol,9981 .protocol = options.protocol,
9217 });9982 });
...@@ -9220,27 +9985,27 @@ fn netConnectIpWindows(...@@ -9220,27 +9985,27 @@ fn netConnectIpWindows(
9220 var storage: WsaAddress = undefined;9985 var storage: WsaAddress = undefined;
9221 var addr_len = addressToWsa(address, &storage);9986 var addr_len = addressToWsa(address, &storage);
92229987
9223 try current_thread.beginSyscall();9988 var syscall: Syscall = try .start();
9224 while (true) {9989 while (true) {
9225 const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);9990 const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
9226 if (rc != ws2_32.SOCKET_ERROR) {9991 if (rc != ws2_32.SOCKET_ERROR) {
9227 current_thread.endSyscall();9992 syscall.finish();
9228 break;9993 break;
9229 }9994 }
9230 switch (ws2_32.WSAGetLastError()) {9995 switch (ws2_32.WSAGetLastError()) {
9231 .EINTR => {9996 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9232 try current_thread.checkCancel();9997 try syscall.checkCancel();
9233 continue;9998 continue;
9234 },9999 },
9235 .NOTINITIALISED => {10000 .NOTINITIALISED => {
10001 syscall.finish();
9236 try initializeWsa(t);10002 try initializeWsa(t);
9237 try current_thread.checkCancel();10003 syscall = try .start();
9238 continue;10004 continue;
9239 },10005 },
9240 else => |e| {10006 else => |e| {
9241 current_thread.endSyscall();10007 syscall.finish();
9242 switch (e) {10008 switch (e) {
9243 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
9244 .EADDRNOTAVAIL => return error.AddressUnavailable,10009 .EADDRNOTAVAIL => return error.AddressUnavailable,
9245 .ECONNREFUSED => return error.ConnectionRefused,10010 .ECONNREFUSED => return error.ConnectionRefused,
9246 .ECONNRESET => return error.ConnectionResetByPeer,10011 .ECONNRESET => return error.ConnectionResetByPeer,
...@@ -9261,7 +10026,7 @@ fn netConnectIpWindows(...@@ -9261,7 +10026,7 @@ fn netConnectIpWindows(
9261 }10026 }
9262 }10027 }
926310028
9264 try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len);10029 try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
926510030
9266 return .{ .socket = .{10031 return .{ .socket = .{
9267 .handle = socket_handle,10032 .handle = socket_handle,
...@@ -9286,15 +10051,15 @@ fn netConnectUnixPosix(...@@ -9286,15 +10051,15 @@ fn netConnectUnixPosix(
9286) net.UnixAddress.ConnectError!net.Socket.Handle {10051) net.UnixAddress.ConnectError!net.Socket.Handle {
9287 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;10052 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
9288 const t: *Threaded = @ptrCast(@alignCast(userdata));10053 const t: *Threaded = @ptrCast(@alignCast(userdata));
9289 const current_thread = Thread.getCurrent(t);10054 _ = t;
9290 const socket_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {10055 const socket_fd = openSocketPosix(posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
9291 error.OptionUnsupported => return error.Unexpected,10056 error.OptionUnsupported => return error.Unexpected,
9292 else => |e| return e,10057 else => |e| return e,
9293 };10058 };
9294 errdefer posix.close(socket_fd);10059 errdefer posix.close(socket_fd);
9295 var storage: UnixAddress = undefined;10060 var storage: UnixAddress = undefined;
9296 const addr_len = addressUnixToPosix(address, &storage);10061 const addr_len = addressUnixToPosix(address, &storage);
9297 try posixConnectUnix(current_thread, socket_fd, &storage.any, addr_len);10062 try posixConnectUnix(socket_fd, &storage.any, addr_len);
9298 return socket_fd;10063 return socket_fd;
9299}10064}
930010065
...@@ -9304,34 +10069,42 @@ fn netConnectUnixWindows(...@@ -9304,34 +10069,42 @@ fn netConnectUnixWindows(
9304) net.UnixAddress.ConnectError!net.Socket.Handle {10069) net.UnixAddress.ConnectError!net.Socket.Handle {
9305 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;10070 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
9306 const t: *Threaded = @ptrCast(@alignCast(userdata));10071 const t: *Threaded = @ptrCast(@alignCast(userdata));
9307 const current_thread = Thread.getCurrent(t);
930810072
9309 const socket_handle = try openSocketWsa(t, current_thread, posix.AF.UNIX, .{ .mode = .stream });10073 const socket_handle = try openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream });
9310 errdefer closeSocketWindows(socket_handle);10074 errdefer closeSocketWindows(socket_handle);
9311 var storage: WsaAddress = undefined;10075 var storage: WsaAddress = undefined;
9312 const addr_len = addressUnixToWsa(address, &storage);10076 const addr_len = addressUnixToWsa(address, &storage);
931310077
10078 var syscall: Syscall = try .start();
9314 while (true) {10079 while (true) {
9315 const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);10080 const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
9316 if (rc != ws2_32.SOCKET_ERROR) break;10081 if (rc != ws2_32.SOCKET_ERROR) break;
9317 switch (ws2_32.WSAGetLastError()) {10082 switch (ws2_32.WSAGetLastError()) {
9318 .EINTR => continue,10083 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9319 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,10084 try syscall.checkCancel();
10085 continue;
10086 },
9320 .NOTINITIALISED => {10087 .NOTINITIALISED => {
10088 syscall.finish();
9321 try initializeWsa(t);10089 try initializeWsa(t);
10090 syscall = try .start();
9322 continue;10091 continue;
9323 },10092 },
932410093 else => |e| {
9325 .ECONNREFUSED => return error.FileNotFound,10094 syscall.finish();
9326 .EFAULT => |err| return wsaErrorBug(err),10095 switch (e) {
9327 .EINVAL => |err| return wsaErrorBug(err),10096 .ECONNREFUSED => return error.FileNotFound,
9328 .EISCONN => |err| return wsaErrorBug(err),10097 .EFAULT => |err| return wsaErrorBug(err),
9329 .ENOTSOCK => |err| return wsaErrorBug(err),10098 .EINVAL => |err| return wsaErrorBug(err),
9330 .EWOULDBLOCK => return error.WouldBlock,10099 .EISCONN => |err| return wsaErrorBug(err),
9331 .EACCES => return error.AccessDenied,10100 .ENOTSOCK => |err| return wsaErrorBug(err),
9332 .ENOBUFS => return error.SystemResources,10101 .EWOULDBLOCK => return error.WouldBlock,
9333 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,10102 .EACCES => return error.AccessDenied,
9334 else => |err| return windows.unexpectedWSAError(err),10103 .ENOBUFS => return error.SystemResources,
10104 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
10105 else => |err| return windows.unexpectedWSAError(err),
10106 }
10107 },
9335 }10108 }
9336 }10109 }
933710110
...@@ -9354,14 +10127,14 @@ fn netBindIpPosix(...@@ -9354,14 +10127,14 @@ fn netBindIpPosix(
9354) IpAddress.BindError!net.Socket {10127) IpAddress.BindError!net.Socket {
9355 if (!have_networking) return error.NetworkDown;10128 if (!have_networking) return error.NetworkDown;
9356 const t: *Threaded = @ptrCast(@alignCast(userdata));10129 const t: *Threaded = @ptrCast(@alignCast(userdata));
9357 const current_thread = Thread.getCurrent(t);10130 _ = t;
9358 const family = posixAddressFamily(address);10131 const family = posixAddressFamily(address);
9359 const socket_fd = try openSocketPosix(current_thread, family, options);10132 const socket_fd = try openSocketPosix(family, options);
9360 errdefer posix.close(socket_fd);10133 errdefer posix.close(socket_fd);
9361 var storage: PosixAddress = undefined;10134 var storage: PosixAddress = undefined;
9362 var addr_len = addressToPosix(address, &storage);10135 var addr_len = addressToPosix(address, &storage);
9363 try posixBind(current_thread, socket_fd, &storage.any, addr_len);10136 try posixBind(socket_fd, &storage.any, addr_len);
9364 try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len);10137 try posixGetSockName(socket_fd, &storage.any, &addr_len);
9365 return .{10138 return .{
9366 .handle = socket_fd,10139 .handle = socket_fd,
9367 .address = addressFromPosix(&storage),10140 .address = addressFromPosix(&storage),
...@@ -9375,9 +10148,8 @@ fn netBindIpWindows(...@@ -9375,9 +10148,8 @@ fn netBindIpWindows(
9375) IpAddress.BindError!net.Socket {10148) IpAddress.BindError!net.Socket {
9376 if (!have_networking) return error.NetworkDown;10149 if (!have_networking) return error.NetworkDown;
9377 const t: *Threaded = @ptrCast(@alignCast(userdata));10150 const t: *Threaded = @ptrCast(@alignCast(userdata));
9378 const current_thread = Thread.getCurrent(t);
9379 const family = posixAddressFamily(address);10151 const family = posixAddressFamily(address);
9380 const socket_handle = try openSocketWsa(t, current_thread, family, .{10152 const socket_handle = try openSocketWsa(t, family, .{
9381 .mode = options.mode,10153 .mode = options.mode,
9382 .protocol = options.protocol,10154 .protocol = options.protocol,
9383 });10155 });
...@@ -9386,27 +10158,27 @@ fn netBindIpWindows(...@@ -9386,27 +10158,27 @@ fn netBindIpWindows(
9386 var storage: WsaAddress = undefined;10158 var storage: WsaAddress = undefined;
9387 var addr_len = addressToWsa(address, &storage);10159 var addr_len = addressToWsa(address, &storage);
938810160
9389 try current_thread.beginSyscall();10161 var syscall: Syscall = try .start();
9390 while (true) {10162 while (true) {
9391 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);10163 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
9392 if (rc != ws2_32.SOCKET_ERROR) {10164 if (rc != ws2_32.SOCKET_ERROR) {
9393 current_thread.endSyscall();10165 syscall.finish();
9394 break;10166 break;
9395 }10167 }
9396 switch (ws2_32.WSAGetLastError()) {10168 switch (ws2_32.WSAGetLastError()) {
9397 .EINTR => {10169 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9398 try current_thread.checkCancel();10170 try syscall.checkCancel();
9399 continue;10171 continue;
9400 },10172 },
9401 .NOTINITIALISED => {10173 .NOTINITIALISED => {
10174 syscall.finish();
9402 try initializeWsa(t);10175 try initializeWsa(t);
9403 try current_thread.checkCancel();10176 syscall = try .start();
9404 continue;10177 continue;
9405 },10178 },
9406 else => |e| {10179 else => |e| {
9407 current_thread.endSyscall();10180 syscall.finish();
9408 switch (e) {10181 switch (e) {
9409 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
9410 .EADDRINUSE => return error.AddressInUse,10182 .EADDRINUSE => return error.AddressInUse,
9411 .EADDRNOTAVAIL => return error.AddressUnavailable,10183 .EADDRNOTAVAIL => return error.AddressUnavailable,
9412 .ENOTSOCK => |err| return wsaErrorBug(err),10184 .ENOTSOCK => |err| return wsaErrorBug(err),
...@@ -9420,7 +10192,7 @@ fn netBindIpWindows(...@@ -9420,7 +10192,7 @@ fn netBindIpWindows(
9420 }10192 }
9421 }10193 }
942210194
9423 try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len);10195 try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
942410196
9425 return .{10197 return .{
9426 .handle = socket_handle,10198 .handle = socket_handle,
...@@ -9440,7 +10212,6 @@ fn netBindIpUnavailable(...@@ -9440,7 +10212,6 @@ fn netBindIpUnavailable(
9440}10212}
944110213
9442fn openSocketPosix(10214fn openSocketPosix(
9443 current_thread: *Thread,
9444 family: posix.sa_family_t,10215 family: posix.sa_family_t,
9445 options: IpAddress.BindOptions,10216 options: IpAddress.BindOptions,
9446) error{10217) error{
...@@ -9457,7 +10228,7 @@ fn openSocketPosix(...@@ -9457,7 +10228,7 @@ fn openSocketPosix(
9457}!posix.socket_t {10228}!posix.socket_t {
9458 const mode = posixSocketMode(options.mode);10229 const mode = posixSocketMode(options.mode);
9459 const protocol = posixProtocol(options.protocol);10230 const protocol = posixProtocol(options.protocol);
9460 try current_thread.beginSyscall();10231 const syscall: Syscall = try .start();
9461 const socket_fd = while (true) {10232 const socket_fd = while (true) {
9462 const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;10233 const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;
9463 const socket_rc = posix.system.socket(family, flags, protocol);10234 const socket_rc = posix.system.socket(family, flags, protocol);
...@@ -9466,25 +10237,25 @@ fn openSocketPosix(...@@ -9466,25 +10237,25 @@ fn openSocketPosix(
9466 const fd: posix.fd_t = @intCast(socket_rc);10237 const fd: posix.fd_t = @intCast(socket_rc);
9467 errdefer posix.close(fd);10238 errdefer posix.close(fd);
9468 if (socket_flags_unsupported) while (true) {10239 if (socket_flags_unsupported) while (true) {
9469 try current_thread.checkCancel();10240 try syscall.checkCancel();
9470 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {10241 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
9471 .SUCCESS => break,10242 .SUCCESS => break,
9472 .INTR => continue,10243 .INTR => continue,
9473 else => |err| {10244 else => |err| {
9474 current_thread.endSyscall();10245 syscall.finish();
9475 return posix.unexpectedErrno(err);10246 return posix.unexpectedErrno(err);
9476 },10247 },
9477 }10248 }
9478 };10249 };
9479 current_thread.endSyscall();10250 syscall.finish();
9480 break fd;10251 break fd;
9481 },10252 },
9482 .INTR => {10253 .INTR => {
9483 try current_thread.checkCancel();10254 try syscall.checkCancel();
9484 continue;10255 continue;
9485 },10256 },
9486 else => |e| {10257 else => |e| {
9487 current_thread.endSyscall();10258 syscall.finish();
9488 switch (e) {10259 switch (e) {
9489 .AFNOSUPPORT => return error.AddressFamilyUnsupported,10260 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
9490 .INVAL => return error.ProtocolUnsupportedBySystem,10261 .INVAL => return error.ProtocolUnsupportedBySystem,
...@@ -9503,7 +10274,7 @@ fn openSocketPosix(...@@ -9503,7 +10274,7 @@ fn openSocketPosix(
950310274
9504 if (options.ip6_only) {10275 if (options.ip6_only) {
9505 if (posix.IPV6 == void) return error.OptionUnsupported;10276 if (posix.IPV6 == void) return error.OptionUnsupported;
9506 try setSocketOption(current_thread, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0);10277 try setSocketOption(socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0);
9507 }10278 }
950810279
9509 return socket_fd;10280 return socket_fd;
...@@ -9511,34 +10282,33 @@ fn openSocketPosix(...@@ -9511,34 +10282,33 @@ fn openSocketPosix(
951110282
9512fn openSocketWsa(10283fn openSocketWsa(
9513 t: *Threaded,10284 t: *Threaded,
9514 current_thread: *Thread,
9515 family: posix.sa_family_t,10285 family: posix.sa_family_t,
9516 options: IpAddress.BindOptions,10286 options: IpAddress.BindOptions,
9517) !ws2_32.SOCKET {10287) !ws2_32.SOCKET {
9518 const mode = posixSocketMode(options.mode);10288 const mode = posixSocketMode(options.mode);
9519 const protocol = posixProtocol(options.protocol);10289 const protocol = posixProtocol(options.protocol);
9520 const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;10290 const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
9521 try current_thread.beginSyscall();10291 var syscall: Syscall = try .start();
9522 while (true) {10292 while (true) {
9523 const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags);10293 const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags);
9524 if (rc != ws2_32.INVALID_SOCKET) {10294 if (rc != ws2_32.INVALID_SOCKET) {
9525 current_thread.endSyscall();10295 syscall.finish();
9526 return rc;10296 return rc;
9527 }10297 }
9528 switch (ws2_32.WSAGetLastError()) {10298 switch (ws2_32.WSAGetLastError()) {
9529 .EINTR => {10299 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9530 try current_thread.checkCancel();10300 try syscall.checkCancel();
9531 continue;10301 continue;
9532 },10302 },
9533 .NOTINITIALISED => {10303 .NOTINITIALISED => {
10304 syscall.finish();
9534 try initializeWsa(t);10305 try initializeWsa(t);
9535 try current_thread.checkCancel();10306 syscall = try .start();
9536 continue;10307 continue;
9537 },10308 },
9538 else => |e| {10309 else => |e| {
9539 current_thread.endSyscall();10310 syscall.finish();
9540 switch (e) {10311 switch (e) {
9541 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
9542 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,10312 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
9543 .EMFILE => return error.ProcessFdQuotaExceeded,10313 .EMFILE => return error.ProcessFdQuotaExceeded,
9544 .ENOBUFS => return error.SystemResources,10314 .ENOBUFS => return error.SystemResources,
...@@ -9553,10 +10323,10 @@ fn openSocketWsa(...@@ -9553,10 +10323,10 @@ fn openSocketWsa(
9553fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream {10323fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream {
9554 if (!have_networking) return error.NetworkDown;10324 if (!have_networking) return error.NetworkDown;
9555 const t: *Threaded = @ptrCast(@alignCast(userdata));10325 const t: *Threaded = @ptrCast(@alignCast(userdata));
9556 const current_thread = Thread.getCurrent(t);10326 _ = t;
9557 var storage: PosixAddress = undefined;10327 var storage: PosixAddress = undefined;
9558 var addr_len: posix.socklen_t = @sizeOf(PosixAddress);10328 var addr_len: posix.socklen_t = @sizeOf(PosixAddress);
9559 try current_thread.beginSyscall();10329 const syscall: Syscall = try .start();
9560 const fd = while (true) {10330 const fd = while (true) {
9561 const rc = if (have_accept4)10331 const rc = if (have_accept4)
9562 posix.system.accept4(listen_fd, &storage.any, &addr_len, posix.SOCK.CLOEXEC)10332 posix.system.accept4(listen_fd, &storage.any, &addr_len, posix.SOCK.CLOEXEC)
...@@ -9567,25 +10337,25 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve...@@ -9567,25 +10337,25 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve
9567 const fd: posix.fd_t = @intCast(rc);10337 const fd: posix.fd_t = @intCast(rc);
9568 errdefer posix.close(fd);10338 errdefer posix.close(fd);
9569 if (!have_accept4) while (true) {10339 if (!have_accept4) while (true) {
9570 try current_thread.checkCancel();10340 try syscall.checkCancel();
9571 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {10341 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
9572 .SUCCESS => break,10342 .SUCCESS => break,
9573 .INTR => continue,10343 .INTR => continue,
9574 else => |err| {10344 else => |err| {
9575 current_thread.endSyscall();10345 syscall.finish();
9576 return posix.unexpectedErrno(err);10346 return posix.unexpectedErrno(err);
9577 },10347 },
9578 }10348 }
9579 };10349 };
9580 current_thread.endSyscall();10350 syscall.finish();
9581 break fd;10351 break fd;
9582 },10352 },
9583 .INTR => {10353 .INTR => {
9584 try current_thread.checkCancel();10354 try syscall.checkCancel();
9585 continue;10355 continue;
9586 },10356 },
9587 else => |e| {10357 else => |e| {
9588 current_thread.endSyscall();10358 syscall.finish();
9589 switch (e) {10359 switch (e) {
9590 .AGAIN => |err| return errnoBug(err),10360 .AGAIN => |err| return errnoBug(err),
9591 .BADF => |err| return errnoBug(err), // File descriptor used after closed.10361 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
...@@ -9614,33 +10384,32 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve...@@ -9614,33 +10384,32 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve
9614fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream {10384fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream {
9615 if (!have_networking) return error.NetworkDown;10385 if (!have_networking) return error.NetworkDown;
9616 const t: *Threaded = @ptrCast(@alignCast(userdata));10386 const t: *Threaded = @ptrCast(@alignCast(userdata));
9617 const current_thread = Thread.getCurrent(t);
9618 var storage: WsaAddress = undefined;10387 var storage: WsaAddress = undefined;
9619 var addr_len: i32 = @sizeOf(WsaAddress);10388 var addr_len: i32 = @sizeOf(WsaAddress);
9620 try current_thread.beginSyscall();10389 var syscall: Syscall = try .start();
9621 while (true) {10390 while (true) {
9622 const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len);10391 const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len);
9623 if (rc != ws2_32.INVALID_SOCKET) {10392 if (rc != ws2_32.INVALID_SOCKET) {
9624 current_thread.endSyscall();10393 syscall.finish();
9625 return .{ .socket = .{10394 return .{ .socket = .{
9626 .handle = rc,10395 .handle = rc,
9627 .address = addressFromWsa(&storage),10396 .address = addressFromWsa(&storage),
9628 } };10397 } };
9629 }10398 }
9630 switch (ws2_32.WSAGetLastError()) {10399 switch (ws2_32.WSAGetLastError()) {
9631 .EINTR => {10400 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9632 try current_thread.checkCancel();10401 try syscall.checkCancel();
9633 continue;10402 continue;
9634 },10403 },
9635 .NOTINITIALISED => {10404 .NOTINITIALISED => {
10405 syscall.finish();
9636 try initializeWsa(t);10406 try initializeWsa(t);
9637 try current_thread.checkCancel();10407 syscall = try .start();
9638 continue;10408 continue;
9639 },10409 },
9640 else => |e| {10410 else => |e| {
9641 current_thread.endSyscall();10411 syscall.finish();
9642 switch (e) {10412 switch (e) {
9643 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
9644 .ECONNRESET => return error.ConnectionAborted,10413 .ECONNRESET => return error.ConnectionAborted,
9645 .EFAULT => |err| return wsaErrorBug(err),10414 .EFAULT => |err| return wsaErrorBug(err),
9646 .ENOTSOCK => |err| return wsaErrorBug(err),10415 .ENOTSOCK => |err| return wsaErrorBug(err),
...@@ -9665,7 +10434,7 @@ fn netAcceptUnavailable(userdata: ?*anyopaque, listen_handle: net.Socket.Handle)...@@ -9665,7 +10434,7 @@ fn netAcceptUnavailable(userdata: ?*anyopaque, listen_handle: net.Socket.Handle)
9665fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {10434fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
9666 if (!have_networking) return error.NetworkDown;10435 if (!have_networking) return error.NetworkDown;
9667 const t: *Threaded = @ptrCast(@alignCast(userdata));10436 const t: *Threaded = @ptrCast(@alignCast(userdata));
9668 const current_thread = Thread.getCurrent(t);10437 _ = t;
966910438
9670 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;10439 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
9671 var i: usize = 0;10440 var i: usize = 0;
...@@ -9680,20 +10449,20 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net....@@ -9680,20 +10449,20 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.
9680 assert(dest[0].len > 0);10449 assert(dest[0].len > 0);
968110450
9682 if (native_os == .wasi and !builtin.link_libc) {10451 if (native_os == .wasi and !builtin.link_libc) {
9683 try current_thread.beginSyscall();10452 const syscall: Syscall = try .start();
9684 while (true) {10453 while (true) {
9685 var n: usize = undefined;10454 var n: usize = undefined;
9686 switch (std.os.wasi.fd_read(fd, dest.ptr, dest.len, &n)) {10455 switch (std.os.wasi.fd_read(fd, dest.ptr, dest.len, &n)) {
9687 .SUCCESS => {10456 .SUCCESS => {
9688 current_thread.endSyscall();10457 syscall.finish();
9689 return n;10458 return n;
9690 },10459 },
9691 .INTR => {10460 .INTR => {
9692 try current_thread.checkCancel();10461 try syscall.checkCancel();
9693 continue;10462 continue;
9694 },10463 },
9695 else => |e| {10464 else => |e| {
9696 current_thread.endSyscall();10465 syscall.finish();
9697 switch (e) {10466 switch (e) {
9698 .INVAL => |err| return errnoBug(err),10467 .INVAL => |err| return errnoBug(err),
9699 .FAULT => |err| return errnoBug(err),10468 .FAULT => |err| return errnoBug(err),
...@@ -9712,20 +10481,20 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net....@@ -9712,20 +10481,20 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.
9712 }10481 }
9713 }10482 }
971410483
9715 try current_thread.beginSyscall();10484 const syscall: Syscall = try .start();
9716 while (true) {10485 while (true) {
9717 const rc = posix.system.readv(fd, dest.ptr, @intCast(dest.len));10486 const rc = posix.system.readv(fd, dest.ptr, @intCast(dest.len));
9718 switch (posix.errno(rc)) {10487 switch (posix.errno(rc)) {
9719 .SUCCESS => {10488 .SUCCESS => {
9720 current_thread.endSyscall();10489 syscall.finish();
9721 return @intCast(rc);10490 return @intCast(rc);
9722 },10491 },
9723 .INTR => {10492 .INTR => {
9724 try current_thread.checkCancel();10493 try syscall.checkCancel();
9725 continue;10494 continue;
9726 },10495 },
9727 else => |e| {10496 else => |e| {
9728 current_thread.endSyscall();10497 syscall.finish();
9729 switch (e) {10498 switch (e) {
9730 .INVAL => |err| return errnoBug(err),10499 .INVAL => |err| return errnoBug(err),
9731 .FAULT => |err| return errnoBug(err),10500 .FAULT => |err| return errnoBug(err),
...@@ -9748,7 +10517,6 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net....@@ -9748,7 +10517,6 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.
9748fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {10517fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
9749 if (!have_networking) return error.NetworkDown;10518 if (!have_networking) return error.NetworkDown;
9750 const t: *Threaded = @ptrCast(@alignCast(userdata));10519 const t: *Threaded = @ptrCast(@alignCast(userdata));
9751 const current_thread = Thread.getCurrent(t);
975210520
9753 const bufs = b: {10521 const bufs = b: {
9754 var iovec_buffer: [max_iovecs_len]ws2_32.WSABUF = undefined;10522 var iovec_buffer: [max_iovecs_len]ws2_32.WSABUF = undefined;
...@@ -9775,48 +10543,41 @@ fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8...@@ -9775,48 +10543,41 @@ fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8
9775 break :b bufs;10543 break :b bufs;
9776 };10544 };
977710545
10546 var syscall: Syscall = try .start();
9778 while (true) {10547 while (true) {
9779 try current_thread.checkCancel();
9780
9781 var flags: u32 = 0;10548 var flags: u32 = 0;
9782 var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);
9783 var n: u32 = undefined;10549 var n: u32 = undefined;
9784 const rc = ws2_32.WSARecv(handle, bufs.ptr, @intCast(bufs.len), &n, &flags, &overlapped, null);10550 const rc = ws2_32.WSARecv(handle, bufs.ptr, @intCast(bufs.len), &n, &flags, null, null);
9785 if (rc != ws2_32.SOCKET_ERROR) return n;10551 if (rc != ws2_32.SOCKET_ERROR) {
9786 const wsa_error: ws2_32.WinsockError = switch (ws2_32.WSAGetLastError()) {10552 syscall.finish();
9787 .IO_PENDING => e: {10553 return n;
9788 var result_flags: u32 = undefined;10554 }
9789 const overlapped_rc = ws2_32.WSAGetOverlappedResult(10555 switch (ws2_32.WSAGetLastError()) {
9790 handle,10556 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9791 &overlapped,10557 try syscall.checkCancel();
9792 &n,10558 continue;
9793 windows.TRUE,
9794 &result_flags,
9795 );
9796 if (overlapped_rc == windows.FALSE) {
9797 break :e ws2_32.WSAGetLastError();
9798 } else {
9799 return n;
9800 }
9801 },10559 },
9802 else => |err| err,
9803 };
9804 switch (wsa_error) {
9805 .EINTR => continue,
9806 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
9807 .NOTINITIALISED => {10560 .NOTINITIALISED => {
10561 syscall.finish();
9808 try initializeWsa(t);10562 try initializeWsa(t);
10563 syscall = try .start();
9809 continue;10564 continue;
9810 },10565 },
981110566
9812 .ECONNRESET => return error.ConnectionResetByPeer,10567 .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
10568 .ENETDOWN => return syscall.fail(error.NetworkDown),
10569 .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
10570 .ENOTCONN => return syscall.fail(error.SocketUnconnected),
9813 .EFAULT => unreachable, // a pointer is not completely contained in user address space.10571 .EFAULT => unreachable, // a pointer is not completely contained in user address space.
9814 .EINVAL => |err| return wsaErrorBug(err),10572
9815 .EMSGSIZE => |err| return wsaErrorBug(err),10573 else => |err| {
9816 .ENETDOWN => return error.NetworkDown,10574 syscall.finish();
9817 .ENETRESET => return error.ConnectionResetByPeer,10575 switch (err) {
9818 .ENOTCONN => return error.SocketUnconnected,10576 .EINVAL => return wsaErrorBug(err),
9819 else => |err| return windows.unexpectedWSAError(err),10577 .EMSGSIZE => return wsaErrorBug(err),
10578 else => return windows.unexpectedWSAError(err),
10579 }
10580 },
9820 }10581 }
9821 }10582 }
9822}10583}
...@@ -9836,7 +10597,6 @@ fn netSendPosix(...@@ -9836,7 +10597,6 @@ fn netSendPosix(
9836) struct { ?net.Socket.SendError, usize } {10597) struct { ?net.Socket.SendError, usize } {
9837 if (!have_networking) return .{ error.NetworkDown, 0 };10598 if (!have_networking) return .{ error.NetworkDown, 0 };
9838 const t: *Threaded = @ptrCast(@alignCast(userdata));10599 const t: *Threaded = @ptrCast(@alignCast(userdata));
9839 const current_thread = Thread.getCurrent(t);
984010600
9841 const posix_flags: u32 =10601 const posix_flags: u32 =
9842 @as(u32, if (@hasDecl(posix.MSG, "CONFIRM") and flags.confirm) posix.MSG.CONFIRM else 0) |10602 @as(u32, if (@hasDecl(posix.MSG, "CONFIRM") and flags.confirm) posix.MSG.CONFIRM else 0) |
...@@ -9849,10 +10609,10 @@ fn netSendPosix(...@@ -9849,10 +10609,10 @@ fn netSendPosix(
9849 var i: usize = 0;10609 var i: usize = 0;
9850 while (messages.len - i != 0) {10610 while (messages.len - i != 0) {
9851 if (have_sendmmsg) {10611 if (have_sendmmsg) {
9852 i += netSendMany(current_thread, handle, messages[i..], posix_flags) catch |err| return .{ err, i };10612 i += netSendMany(handle, messages[i..], posix_flags) catch |err| return .{ err, i };
9853 continue;10613 continue;
9854 }10614 }
9855 netSendOne(t, current_thread, handle, &messages[i], posix_flags) catch |err| return .{ err, i };10615 netSendOne(t, handle, &messages[i], posix_flags) catch |err| return .{ err, i };
9856 i += 1;10616 i += 1;
9857 }10617 }
9858 return .{ null, i };10618 return .{ null, i };
...@@ -9888,7 +10648,6 @@ fn netSendUnavailable(...@@ -9888,7 +10648,6 @@ fn netSendUnavailable(
988810648
9889fn netSendOne(10649fn netSendOne(
9890 t: *Threaded,10650 t: *Threaded,
9891 current_thread: *Thread,
9892 handle: net.Socket.Handle,10651 handle: net.Socket.Handle,
9893 message: *net.OutgoingMessage,10652 message: *net.OutgoingMessage,
9894 flags: u32,10653 flags: u32,
...@@ -9905,29 +10664,29 @@ fn netSendOne(...@@ -9905,29 +10664,29 @@ fn netSendOne(
9905 .controllen = @intCast(message.control.len),10664 .controllen = @intCast(message.control.len),
9906 .flags = 0,10665 .flags = 0,
9907 };10666 };
9908 try current_thread.beginSyscall();10667 var syscall: Syscall = try .start();
9909 while (true) {10668 while (true) {
9910 const rc = posix.system.sendmsg(handle, &msg, flags);10669 const rc = posix.system.sendmsg(handle, &msg, flags);
9911 if (is_windows) {10670 if (is_windows) {
9912 if (rc != ws2_32.SOCKET_ERROR) {10671 if (rc != ws2_32.SOCKET_ERROR) {
9913 current_thread.endSyscall();10672 syscall.finish();
9914 message.data_len = @intCast(rc);10673 message.data_len = @intCast(rc);
9915 return;10674 return;
9916 }10675 }
9917 switch (ws2_32.WSAGetLastError()) {10676 switch (ws2_32.WSAGetLastError()) {
9918 .EINTR => {10677 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
9919 try current_thread.checkCancel();10678 try syscall.checkCancel();
9920 continue;10679 continue;
9921 },10680 },
9922 .NOTINITIALISED => {10681 .NOTINITIALISED => {
10682 syscall.finish();
9923 try initializeWsa(t);10683 try initializeWsa(t);
9924 try current_thread.checkCancel();10684 syscall = try .start();
9925 continue;10685 continue;
9926 },10686 },
9927 else => |e| {10687 else => |e| {
9928 current_thread.endSyscall();10688 syscall.finish();
9929 switch (e) {10689 switch (e) {
9930 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
9931 .EACCES => return error.AccessDenied,10690 .EACCES => return error.AccessDenied,
9932 .EADDRNOTAVAIL => return error.AddressUnavailable,10691 .EADDRNOTAVAIL => return error.AddressUnavailable,
9933 .ECONNRESET => return error.ConnectionResetByPeer,10692 .ECONNRESET => return error.ConnectionResetByPeer,
...@@ -9951,16 +10710,16 @@ fn netSendOne(...@@ -9951,16 +10710,16 @@ fn netSendOne(
9951 }10710 }
9952 switch (posix.errno(rc)) {10711 switch (posix.errno(rc)) {
9953 .SUCCESS => {10712 .SUCCESS => {
9954 current_thread.endSyscall();10713 syscall.finish();
9955 message.data_len = @intCast(rc);10714 message.data_len = @intCast(rc);
9956 return;10715 return;
9957 },10716 },
9958 .INTR => {10717 .INTR => {
9959 try current_thread.checkCancel();10718 try syscall.checkCancel();
9960 continue;10719 continue;
9961 },10720 },
9962 else => |e| {10721 else => |e| {
9963 current_thread.endSyscall();10722 syscall.finish();
9964 switch (e) {10723 switch (e) {
9965 .ACCES => return error.AccessDenied,10724 .ACCES => return error.AccessDenied,
9966 .ALREADY => return error.FastOpenAlreadyInProgress,10725 .ALREADY => return error.FastOpenAlreadyInProgress,
...@@ -9989,7 +10748,6 @@ fn netSendOne(...@@ -9989,7 +10748,6 @@ fn netSendOne(
9989}10748}
999010749
9991fn netSendMany(10750fn netSendMany(
9992 current_thread: *Thread,
9993 handle: net.Socket.Handle,10751 handle: net.Socket.Handle,
9994 messages: []net.OutgoingMessage,10752 messages: []net.OutgoingMessage,
9995 flags: u32,10753 flags: u32,
...@@ -10019,12 +10777,12 @@ fn netSendMany(...@@ -10019,12 +10777,12 @@ fn netSendMany(
10019 };10777 };
10020 }10778 }
1002110779
10022 try current_thread.beginSyscall();10780 const syscall: Syscall = try .start();
10023 while (true) {10781 while (true) {
10024 const rc = posix.system.sendmmsg(handle, clamped_msgs.ptr, @intCast(clamped_msgs.len), flags);10782 const rc = posix.system.sendmmsg(handle, clamped_msgs.ptr, @intCast(clamped_msgs.len), flags);
10025 switch (posix.errno(rc)) {10783 switch (posix.errno(rc)) {
10026 .SUCCESS => {10784 .SUCCESS => {
10027 current_thread.endSyscall();10785 syscall.finish();
10028 const n: usize = @intCast(rc);10786 const n: usize = @intCast(rc);
10029 for (clamped_messages[0..n], clamped_msgs[0..n]) |*message, *msg| {10787 for (clamped_messages[0..n], clamped_msgs[0..n]) |*message, *msg| {
10030 message.data_len = msg.len;10788 message.data_len = msg.len;
...@@ -10032,11 +10790,11 @@ fn netSendMany(...@@ -10032,11 +10790,11 @@ fn netSendMany(
10032 return n;10790 return n;
10033 },10791 },
10034 .INTR => {10792 .INTR => {
10035 try current_thread.checkCancel();10793 try syscall.checkCancel();
10036 continue;10794 continue;
10037 },10795 },
10038 else => |e| {10796 else => |e| {
10039 current_thread.endSyscall();10797 syscall.finish();
10040 switch (e) {10798 switch (e) {
10041 .AGAIN => |err| return errnoBug(err),10799 .AGAIN => |err| return errnoBug(err),
10042 .ALREADY => return error.FastOpenAlreadyInProgress,10800 .ALREADY => return error.FastOpenAlreadyInProgress,
...@@ -10074,7 +10832,6 @@ fn netReceivePosix(...@@ -10074,7 +10832,6 @@ fn netReceivePosix(
10074) struct { ?net.Socket.ReceiveTimeoutError, usize } {10832) struct { ?net.Socket.ReceiveTimeoutError, usize } {
10075 if (!have_networking) return .{ error.NetworkDown, 0 };10833 if (!have_networking) return .{ error.NetworkDown, 0 };
10076 const t: *Threaded = @ptrCast(@alignCast(userdata));10834 const t: *Threaded = @ptrCast(@alignCast(userdata));
10077 const current_thread = Thread.getCurrent(t);
10078 const t_io = io(t);10835 const t_io = io(t);
1007910836
10080 // recvmmsg is useless, here's why:10837 // recvmmsg is useless, here's why:
...@@ -10120,9 +10877,12 @@ fn netReceivePosix(...@@ -10120,9 +10877,12 @@ fn netReceivePosix(
10120 .flags = undefined,10877 .flags = undefined,
10121 };10878 };
1012210879
10123 current_thread.beginSyscall() catch |err| return .{ err, message_i };10880 const recv_rc = rc: {
10124 const recv_rc = posix.system.recvmsg(handle, &msg, posix_flags);10881 const syscall = Syscall.start() catch |err| return .{ err, message_i };
10125 current_thread.endSyscall();10882 const rc = posix.system.recvmsg(handle, &msg, posix_flags);
10883 syscall.finish();
10884 break :rc rc;
10885 };
10126 switch (posix.errno(recv_rc)) {10886 switch (posix.errno(recv_rc)) {
10127 .SUCCESS => {10887 .SUCCESS => {
10128 const data = remaining_data_buffer[0..@intCast(recv_rc)];10888 const data = remaining_data_buffer[0..@intCast(recv_rc)];
...@@ -10152,9 +10912,9 @@ fn netReceivePosix(...@@ -10152,9 +10912,9 @@ fn netReceivePosix(
10152 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));10912 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));
10153 } else max_poll_ms;10913 } else max_poll_ms;
1015410914
10155 current_thread.beginSyscall() catch |err| return .{ err, message_i };10915 const syscall = Syscall.start() catch |err| return .{ err, message_i };
10156 const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms);10916 const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms);
10157 current_thread.endSyscall();10917 syscall.finish();
1015810918
10159 switch (posix.errno(poll_rc)) {10919 switch (posix.errno(poll_rc)) {
10160 .SUCCESS => {10920 .SUCCESS => {
...@@ -10240,7 +11000,7 @@ fn netWritePosix(...@@ -10240,7 +11000,7 @@ fn netWritePosix(
10240) net.Stream.Writer.Error!usize {11000) net.Stream.Writer.Error!usize {
10241 if (!have_networking) return error.NetworkDown;11001 if (!have_networking) return error.NetworkDown;
10242 const t: *Threaded = @ptrCast(@alignCast(userdata));11002 const t: *Threaded = @ptrCast(@alignCast(userdata));
10243 const current_thread = Thread.getCurrent(t);11003 _ = t;
1024411004
10245 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;11005 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
10246 var msg: posix.msghdr_const = .{11006 var msg: posix.msghdr_const = .{
...@@ -10282,20 +11042,20 @@ fn netWritePosix(...@@ -10282,20 +11042,20 @@ fn netWritePosix(
10282 };11042 };
10283 const flags = posix.MSG.NOSIGNAL;11043 const flags = posix.MSG.NOSIGNAL;
1028411044
10285 try current_thread.beginSyscall();11045 const syscall: Syscall = try .start();
10286 while (true) {11046 while (true) {
10287 const rc = posix.system.sendmsg(fd, &msg, flags);11047 const rc = posix.system.sendmsg(fd, &msg, flags);
10288 switch (posix.errno(rc)) {11048 switch (posix.errno(rc)) {
10289 .SUCCESS => {11049 .SUCCESS => {
10290 current_thread.endSyscall();11050 syscall.finish();
10291 return @intCast(rc);11051 return @intCast(rc);
10292 },11052 },
10293 .INTR => {11053 .INTR => {
10294 try current_thread.checkCancel();11054 try syscall.checkCancel();
10295 continue;11055 continue;
10296 },11056 },
10297 else => |e| {11057 else => |e| {
10298 current_thread.endSyscall();11058 syscall.finish();
10299 switch (e) {11059 switch (e) {
10300 .ACCES => |err| return errnoBug(err),11060 .ACCES => |err| return errnoBug(err),
10301 .AGAIN => |err| return errnoBug(err),11061 .AGAIN => |err| return errnoBug(err),
...@@ -10332,7 +11092,6 @@ fn netWriteWindows(...@@ -10332,7 +11092,6 @@ fn netWriteWindows(
10332 splat: usize,11092 splat: usize,
10333) net.Stream.Writer.Error!usize {11093) net.Stream.Writer.Error!usize {
10334 const t: *Threaded = @ptrCast(@alignCast(userdata));11094 const t: *Threaded = @ptrCast(@alignCast(userdata));
10335 const current_thread = Thread.getCurrent(t);
10336 comptime assert(native_os == .windows);11095 comptime assert(native_os == .windows);
1033711096
10338 var iovecs: [max_iovecs_len]ws2_32.WSABUF = undefined;11097 var iovecs: [max_iovecs_len]ws2_32.WSABUF = undefined;
...@@ -10365,49 +11124,44 @@ fn netWriteWindows(...@@ -10365,49 +11124,44 @@ fn netWriteWindows(
10365 },11124 },
10366 };11125 };
1036711126
11127 var syscall: Syscall = try .start();
10368 while (true) {11128 while (true) {
10369 try current_thread.checkCancel();
10370
10371 var n: u32 = undefined;11129 var n: u32 = undefined;
10372 var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);11130 const rc = ws2_32.WSASend(handle, &iovecs, len, &n, 0, null, null);
10373 const rc = ws2_32.WSASend(handle, &iovecs, len, &n, 0, &overlapped, null);11131 if (rc != ws2_32.SOCKET_ERROR) {
10374 if (rc != ws2_32.SOCKET_ERROR) return n;11132 syscall.finish();
10375 const wsa_error: ws2_32.WinsockError = switch (ws2_32.WSAGetLastError()) {11133 return n;
10376 .IO_PENDING => e: {11134 }
10377 var result_flags: u32 = undefined;11135 switch (ws2_32.WSAGetLastError()) {
10378 const overlapped_rc = ws2_32.WSAGetOverlappedResult(11136 .IO_PENDING => unreachable, // not overlapped
10379 handle,11137 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
10380 &overlapped,11138 try syscall.checkCancel();
10381 &n,11139 continue;
10382 windows.TRUE,
10383 &result_flags,
10384 );
10385 if (overlapped_rc == windows.FALSE) {
10386 break :e ws2_32.WSAGetLastError();
10387 } else {
10388 return n;
10389 }
10390 },11140 },
10391 else => |err| err,
10392 };
10393 switch (wsa_error) {
10394 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => continue,
10395 .NOTINITIALISED => {11141 .NOTINITIALISED => {
11142 syscall.finish();
10396 try initializeWsa(t);11143 try initializeWsa(t);
11144 syscall = try .start();
10397 continue;11145 continue;
10398 },11146 },
1039911147
10400 .ECONNABORTED => return error.ConnectionResetByPeer,11148 .ECONNABORTED => return syscall.fail(error.ConnectionResetByPeer),
10401 .ECONNRESET => return error.ConnectionResetByPeer,11149 .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
10402 .EINVAL => return error.SocketUnconnected,11150 .EINVAL => return syscall.fail(error.SocketUnconnected),
10403 .ENETDOWN => return error.NetworkDown,11151 .ENETDOWN => return syscall.fail(error.NetworkDown),
10404 .ENETRESET => return error.ConnectionResetByPeer,11152 .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
10405 .ENOBUFS => return error.SystemResources,11153 .ENOBUFS => return syscall.fail(error.SystemResources),
10406 .ENOTCONN => return error.SocketUnconnected,11154 .ENOTCONN => return syscall.fail(error.SocketUnconnected),
10407 .ENOTSOCK => |err| return wsaErrorBug(err),11155
10408 .EOPNOTSUPP => |err| return wsaErrorBug(err),11156 else => |err| {
10409 .ESHUTDOWN => |err| return wsaErrorBug(err),11157 syscall.finish();
10410 else => |err| return windows.unexpectedWSAError(err),11158 switch (err) {
11159 .ENOTSOCK => return wsaErrorBug(err),
11160 .EOPNOTSUPP => return wsaErrorBug(err),
11161 .ESHUTDOWN => return wsaErrorBug(err),
11162 else => return windows.unexpectedWSAError(err),
11163 }
11164 },
10411 }11165 }
10412 }11166 }
10413}11167}
...@@ -10476,7 +11230,7 @@ fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle...@@ -10476,7 +11230,7 @@ fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle
10476fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {11230fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
10477 if (!have_networking) return error.NetworkDown;11231 if (!have_networking) return error.NetworkDown;
10478 const t: *Threaded = @ptrCast(@alignCast(userdata));11232 const t: *Threaded = @ptrCast(@alignCast(userdata));
10479 const current_thread = Thread.getCurrent(t);11233 _ = t;
1048011234
10481 const posix_how: i32 = switch (how) {11235 const posix_how: i32 = switch (how) {
10482 .recv => posix.SHUT.RD,11236 .recv => posix.SHUT.RD,
...@@ -10484,19 +11238,16 @@ fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.S...@@ -10484,19 +11238,16 @@ fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.S
10484 .both => posix.SHUT.RDWR,11238 .both => posix.SHUT.RDWR,
10485 };11239 };
1048611240
10487 try current_thread.beginSyscall();11241 const syscall: Syscall = try .start();
10488 while (true) {11242 while (true) {
10489 switch (posix.errno(posix.system.shutdown(handle, posix_how))) {11243 switch (posix.errno(posix.system.shutdown(handle, posix_how))) {
10490 .SUCCESS => {11244 .SUCCESS => return syscall.finish(),
10491 current_thread.endSyscall();
10492 return;
10493 },
10494 .INTR => {11245 .INTR => {
10495 try current_thread.checkCancel();11246 try syscall.checkCancel();
10496 continue;11247 continue;
10497 },11248 },
10498 else => |e| {11249 else => |e| {
10499 current_thread.endSyscall();11250 syscall.finish();
10500 switch (e) {11251 switch (e) {
10501 .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err),11252 .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err),
10502 .NOTCONN => return error.SocketUnconnected,11253 .NOTCONN => return error.SocketUnconnected,
...@@ -10511,7 +11262,6 @@ fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.S...@@ -10511,7 +11262,6 @@ fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.S
10511fn netShutdownWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {11262fn netShutdownWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
10512 if (!have_networking) return error.NetworkDown;11263 if (!have_networking) return error.NetworkDown;
10513 const t: *Threaded = @ptrCast(@alignCast(userdata));11264 const t: *Threaded = @ptrCast(@alignCast(userdata));
10514 const current_thread = Thread.getCurrent(t);
1051511265
10516 const wsa_how: i32 = switch (how) {11266 const wsa_how: i32 = switch (how) {
10517 .recv => ws2_32.SD_RECEIVE,11267 .recv => ws2_32.SD_RECEIVE,
...@@ -10519,27 +11269,27 @@ fn netShutdownWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net...@@ -10519,27 +11269,27 @@ fn netShutdownWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net
10519 .both => ws2_32.SD_BOTH,11269 .both => ws2_32.SD_BOTH,
10520 };11270 };
1052111271
10522 try current_thread.beginSyscall();11272 var syscall: Syscall = try .start();
10523 while (true) {11273 while (true) {
10524 const rc = ws2_32.shutdown(handle, wsa_how);11274 const rc = ws2_32.shutdown(handle, wsa_how);
10525 if (rc != ws2_32.SOCKET_ERROR) {11275 if (rc != ws2_32.SOCKET_ERROR) {
10526 current_thread.endSyscall();11276 syscall.finish();
10527 return;11277 return;
10528 }11278 }
10529 switch (ws2_32.WSAGetLastError()) {11279 switch (ws2_32.WSAGetLastError()) {
10530 .EINTR => {11280 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
10531 try current_thread.checkCancel();11281 try syscall.checkCancel();
10532 continue;11282 continue;
10533 },11283 },
10534 .NOTINITIALISED => {11284 .NOTINITIALISED => {
11285 syscall.finish();
10535 try initializeWsa(t);11286 try initializeWsa(t);
10536 try current_thread.checkCancel();11287 syscall = try .start();
10537 continue;11288 continue;
10538 },11289 },
10539 else => |e| {11290 else => |e| {
10540 current_thread.endSyscall();11291 syscall.finish();
10541 switch (e) {11292 switch (e) {
10542 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
10543 .ECONNABORTED => return error.ConnectionAborted,11293 .ECONNABORTED => return error.ConnectionAborted,
10544 .ECONNRESET => return error.ConnectionResetByPeer,11294 .ECONNRESET => return error.ConnectionResetByPeer,
10545 .ENETDOWN => return error.NetworkDown,11295 .ENETDOWN => return error.NetworkDown,
...@@ -10562,10 +11312,10 @@ fn netInterfaceNameResolve(...@@ -10562,10 +11312,10 @@ fn netInterfaceNameResolve(
10562) net.Interface.Name.ResolveError!net.Interface {11312) net.Interface.Name.ResolveError!net.Interface {
10563 if (!have_networking) return error.InterfaceNotFound;11313 if (!have_networking) return error.InterfaceNotFound;
10564 const t: *Threaded = @ptrCast(@alignCast(userdata));11314 const t: *Threaded = @ptrCast(@alignCast(userdata));
10565 const current_thread = Thread.getCurrent(t);11315 _ = t;
1056611316
10567 if (native_os == .linux) {11317 if (native_os == .linux) {
10568 const sock_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .dgram }) catch |err| switch (err) {11318 const sock_fd = openSocketPosix(posix.AF.UNIX, .{ .mode = .dgram }) catch |err| switch (err) {
10569 error.ProcessFdQuotaExceeded => return error.SystemResources,11319 error.ProcessFdQuotaExceeded => return error.SystemResources,
10570 error.SystemFdQuotaExceeded => return error.SystemResources,11320 error.SystemFdQuotaExceeded => return error.SystemResources,
10571 error.AddressFamilyUnsupported => return error.Unexpected,11321 error.AddressFamilyUnsupported => return error.Unexpected,
...@@ -10582,19 +11332,19 @@ fn netInterfaceNameResolve(...@@ -10582,19 +11332,19 @@ fn netInterfaceNameResolve(
10582 .ifru = undefined,11332 .ifru = undefined,
10583 };11333 };
1058411334
10585 try current_thread.beginSyscall();11335 const syscall: Syscall = try .start();
10586 while (true) {11336 while (true) {
10587 switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) {11337 switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) {
10588 .SUCCESS => {11338 .SUCCESS => {
10589 current_thread.endSyscall();11339 syscall.finish();
10590 return .{ .index = @bitCast(ifr.ifru.ivalue) };11340 return .{ .index = @bitCast(ifr.ifru.ivalue) };
10591 },11341 },
10592 .INTR => {11342 .INTR => {
10593 try current_thread.checkCancel();11343 try syscall.checkCancel();
10594 continue;11344 continue;
10595 },11345 },
10596 else => |e| {11346 else => |e| {
10597 current_thread.endSyscall();11347 syscall.finish();
10598 switch (e) {11348 switch (e) {
10599 .INVAL => |err| return errnoBug(err), // Bad parameters.11349 .INVAL => |err| return errnoBug(err), // Bad parameters.
10600 .NOTTY => |err| return errnoBug(err),11350 .NOTTY => |err| return errnoBug(err),
...@@ -10611,12 +11361,12 @@ fn netInterfaceNameResolve(...@@ -10611,12 +11361,12 @@ fn netInterfaceNameResolve(
10611 }11361 }
1061211362
10613 if (native_os == .windows) {11363 if (native_os == .windows) {
10614 try current_thread.checkCancel();11364 try Thread.checkCancel();
10615 @panic("TODO implement netInterfaceNameResolve for Windows");11365 @panic("TODO implement netInterfaceNameResolve for Windows");
10616 }11366 }
1061711367
10618 if (builtin.link_libc) {11368 if (builtin.link_libc) {
10619 try current_thread.checkCancel();11369 try Thread.checkCancel();
10620 const index = std.c.if_nametoindex(&name.bytes);11370 const index = std.c.if_nametoindex(&name.bytes);
10621 if (index == 0) return error.InterfaceNotFound;11371 if (index == 0) return error.InterfaceNotFound;
10622 return .{ .index = @bitCast(index) };11372 return .{ .index = @bitCast(index) };
...@@ -10636,8 +11386,8 @@ fn netInterfaceNameResolveUnavailable(...@@ -10636,8 +11386,8 @@ fn netInterfaceNameResolveUnavailable(
1063611386
10637fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {11387fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
10638 const t: *Threaded = @ptrCast(@alignCast(userdata));11388 const t: *Threaded = @ptrCast(@alignCast(userdata));
10639 const current_thread = Thread.getCurrent(t);11389 _ = t;
10640 try current_thread.checkCancel();11390 try Thread.checkCancel();
1064111391
10642 if (native_os == .linux) {11392 if (native_os == .linux) {
10643 _ = interface;11393 _ = interface;
...@@ -10696,7 +11446,6 @@ fn netLookupFallible(...@@ -10696,7 +11446,6 @@ fn netLookupFallible(
10696) (net.HostName.LookupError || Io.QueueClosedError)!void {11446) (net.HostName.LookupError || Io.QueueClosedError)!void {
10697 if (!have_networking) return error.NetworkDown;11447 if (!have_networking) return error.NetworkDown;
1069811448
10699 const current_thread: *Thread = .getCurrent(t);
10700 const t_io = io(t);11449 const t_io = io(t);
10701 const name = host_name.bytes;11450 const name = host_name.bytes;
10702 assert(name.len <= HostName.max_len);11451 assert(name.len <= HostName.max_len);
...@@ -10733,18 +11482,17 @@ fn netLookupFallible(...@@ -10733,18 +11482,17 @@ fn netLookupFallible(
10733 .provider = null,11482 .provider = null,
10734 .next = null,11483 .next = null,
10735 };11484 };
10736 const cancel_handle: ?*windows.HANDLE = null;
10737 var res: *ws2_32.ADDRINFOEXW = undefined;11485 var res: *ws2_32.ADDRINFOEXW = undefined;
10738 const timeout: ?*ws2_32.timeval = null;11486 const timeout: ?*ws2_32.timeval = null;
10739 while (true) {11487 while (true) {
10740 try current_thread.checkCancel(); // TODO make requestCancel call GetAddrInfoExCancel11488 // TODO: hook this up to cancelation with `Thread.Status.cancelation.blocked_windows_dns`.
10741 // TODO make this append to the queue eagerly rather than blocking until11489 // See matching TODO in `Thread.cancelAwaitable`.
10742 // the whole thing finishes11490 try Thread.checkCancel();
10743 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, cancel_handle));11491 // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes
11492 const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));
10744 switch (rc) {11493 switch (rc) {
10745 @as(ws2_32.WinsockError, @enumFromInt(0)) => break,11494 @as(ws2_32.WinsockError, @enumFromInt(0)) => break,
10746 .EINTR => continue,11495 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => continue,
10747 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
10748 .NOTINITIALISED => {11496 .NOTINITIALISED => {
10749 try initializeWsa(t);11497 try initializeWsa(t);
10750 continue;11498 continue;
...@@ -10884,25 +11632,25 @@ fn netLookupFallible(...@@ -10884,25 +11632,25 @@ fn netLookupFallible(
10884 .next = null,11632 .next = null,
10885 };11633 };
10886 var res: ?*posix.addrinfo = null;11634 var res: ?*posix.addrinfo = null;
10887 try current_thread.beginSyscall();11635 const syscall: Syscall = try .start();
10888 while (true) {11636 while (true) {
10889 switch (posix.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {11637 switch (posix.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
10890 @as(posix.system.EAI, @enumFromInt(0)) => {11638 @as(posix.system.EAI, @enumFromInt(0)) => {
10891 current_thread.endSyscall();11639 syscall.finish();
10892 break;11640 break;
10893 },11641 },
10894 .SYSTEM => switch (posix.errno(-1)) {11642 .SYSTEM => switch (posix.errno(-1)) {
10895 .INTR => {11643 .INTR => {
10896 try current_thread.checkCancel();11644 try syscall.checkCancel();
10897 continue;11645 continue;
10898 },11646 },
10899 else => |e| {11647 else => |e| {
10900 current_thread.endSyscall();11648 syscall.finish();
10901 return posix.unexpectedErrno(e);11649 return posix.unexpectedErrno(e);
10902 },11650 },
10903 },11651 },
10904 else => |e| {11652 else => |e| {
10905 current_thread.endSyscall();11653 syscall.finish();
10906 switch (e) {11654 switch (e) {
10907 .ADDRFAMILY => return error.AddressFamilyUnsupported,11655 .ADDRFAMILY => return error.AddressFamilyUnsupported,
10908 .AGAIN => return error.NameServerFailure,11656 .AGAIN => return error.NameServerFailure,
...@@ -10977,7 +11725,7 @@ fn unlockStderr(userdata: ?*anyopaque) void {...@@ -10977,7 +11725,7 @@ fn unlockStderr(userdata: ?*anyopaque) void {
10977 const t: *Threaded = @ptrCast(@alignCast(userdata));11725 const t: *Threaded = @ptrCast(@alignCast(userdata));
10978 t.stderr_writer.interface.flush() catch |err| switch (err) {11726 t.stderr_writer.interface.flush() catch |err| switch (err) {
10979 error.WriteFailed => switch (t.stderr_writer.err.?) {11727 error.WriteFailed => switch (t.stderr_writer.err.?) {
10980 error.Canceled => recancel(t),11728 error.Canceled => recancelInner(),
10981 else => {},11729 else => {},
10982 },11730 },
10983 };11731 };
...@@ -10989,62 +11737,66 @@ fn unlockStderr(userdata: ?*anyopaque) void {...@@ -10989,62 +11737,66 @@ fn unlockStderr(userdata: ?*anyopaque) void {
10989fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void {11737fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void {
10990 if (native_os == .wasi) return error.OperationUnsupported;11738 if (native_os == .wasi) return error.OperationUnsupported;
10991 const t: *Threaded = @ptrCast(@alignCast(userdata));11739 const t: *Threaded = @ptrCast(@alignCast(userdata));
10992 const current_thread = Thread.getCurrent(t);11740 _ = t;
1099311741
10994 if (is_windows) {11742 if (is_windows) {
10995 try current_thread.checkCancel();
10996 var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined;11743 var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined;
10997 // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks11744 // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks
11745 try Thread.checkCancel();
10998 const dir_path = try windows.GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer);11746 const dir_path = try windows.GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer);
10999 const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong;11747 const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong;
11000 try current_thread.checkCancel();
11001 var nt_name: windows.UNICODE_STRING = .{11748 var nt_name: windows.UNICODE_STRING = .{
11002 .Length = path_len_bytes,11749 .Length = path_len_bytes,
11003 .MaximumLength = path_len_bytes,11750 .MaximumLength = path_len_bytes,
11004 .Buffer = @constCast(dir_path.ptr),11751 .Buffer = @constCast(dir_path.ptr),
11005 };11752 };
11006 switch (windows.ntdll.RtlSetCurrentDirectory_U(&nt_name)) {11753 const syscall: Syscall = try .start();
11007 .SUCCESS => return,11754 while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&nt_name)) {
11008 .OBJECT_NAME_INVALID => return error.BadPathName,11755 .SUCCESS => return syscall.finish(),
11009 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,11756 .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
11010 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,11757 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
11011 .NO_MEDIA_IN_DEVICE => return error.NoDevice,11758 .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
11012 .INVALID_PARAMETER => |err| return windows.statusBug(err),11759 .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
11013 .ACCESS_DENIED => return error.AccessDenied,11760 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
11014 .OBJECT_PATH_SYNTAX_BAD => |err| return windows.statusBug(err),11761 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
11015 .NOT_A_DIRECTORY => return error.NotDir,11762 .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
11016 else => |status| return windows.unexpectedStatus(status),11763 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
11017 }11764 .CANCELLED => {
11765 try syscall.checkCancel();
11766 continue;
11767 },
11768 else => |status| return syscall.unexpectedNtstatus(status),
11769 };
11018 }11770 }
1101911771
11020 if (dir.handle == posix.AT.FDCWD) return;11772 if (dir.handle == posix.AT.FDCWD) return;
1102111773
11022 try current_thread.beginSyscall();11774 const syscall: Syscall = try .start();
11023 while (true) {11775 while (true) {
11024 switch (posix.errno(posix.system.fchdir(dir.handle))) {11776 switch (posix.errno(posix.system.fchdir(dir.handle))) {
11025 .SUCCESS => return current_thread.endSyscall(),11777 .SUCCESS => return syscall.finish(),
11026 .INTR => {11778 .INTR => {
11027 try current_thread.checkCancel();11779 try syscall.checkCancel();
11028 continue;11780 continue;
11029 },11781 },
11030 .ACCES => {11782 .ACCES => {
11031 current_thread.endSyscall();11783 syscall.finish();
11032 return error.AccessDenied;11784 return error.AccessDenied;
11033 },11785 },
11034 .BADF => |err| {11786 .BADF => |err| {
11035 current_thread.endSyscall();11787 syscall.finish();
11036 return errnoBug(err);11788 return errnoBug(err);
11037 },11789 },
11038 .NOTDIR => {11790 .NOTDIR => {
11039 current_thread.endSyscall();11791 syscall.finish();
11040 return error.NotDir;11792 return error.NotDir;
11041 },11793 },
11042 .IO => {11794 .IO => {
11043 current_thread.endSyscall();11795 syscall.finish();
11044 return error.FileSystem;11796 return error.FileSystem;
11045 },11797 },
11046 else => |err| {11798 else => |err| {
11047 current_thread.endSyscall();11799 syscall.finish();
11048 return posix.unexpectedErrno(err);11800 return posix.unexpectedErrno(err);
11049 },11801 },
11050 }11802 }
...@@ -11825,391 +12577,6 @@ fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {...@@ -11825,391 +12577,6 @@ fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {
1182512577
11826fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {}12578fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {}
1182712579
11828const pthreads_futex = struct {
11829 const c = std.c;
11830 const atomic = std.atomic;
11831
11832 const Event = struct {
11833 cond: c.pthread_cond_t,
11834 mutex: c.pthread_mutex_t,
11835 state: enum { empty, waiting, notified },
11836
11837 fn init(self: *Event) void {
11838 // Use static init instead of pthread_cond/mutex_init() since this is generally faster.
11839 self.cond = .{};
11840 self.mutex = .{};
11841 self.state = .empty;
11842 }
11843
11844 fn deinit(self: *Event) void {
11845 // Some platforms reportedly give EINVAL for statically initialized pthread types.
11846 const rc = c.pthread_cond_destroy(&self.cond);
11847 assert(rc == .SUCCESS or rc == .INVAL);
11848
11849 const rm = c.pthread_mutex_destroy(&self.mutex);
11850 assert(rm == .SUCCESS or rm == .INVAL);
11851
11852 self.* = undefined;
11853 }
11854
11855 fn wait(self: *Event, timeout: ?u64) error{Timeout}!void {
11856 assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
11857 defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
11858
11859 // Early return if the event was already set.
11860 if (self.state == .notified) {
11861 return;
11862 }
11863
11864 // Compute the absolute timeout if one was specified.
11865 // POSIX requires that REALTIME is used by default for the pthread timedwait functions.
11866 // This can be changed with pthread_condattr_setclock, but it's an extension and may not be available everywhere.
11867 var ts: c.timespec = undefined;
11868 if (timeout) |timeout_ns| {
11869 ts = std.posix.clock_gettime(c.CLOCK.REALTIME) catch return error.Timeout;
11870 ts.sec +|= @as(@TypeOf(ts.sec), @intCast(timeout_ns / std.time.ns_per_s));
11871 ts.nsec += @as(@TypeOf(ts.nsec), @intCast(timeout_ns % std.time.ns_per_s));
11872
11873 if (ts.nsec >= std.time.ns_per_s) {
11874 ts.sec +|= 1;
11875 ts.nsec -= std.time.ns_per_s;
11876 }
11877 }
11878
11879 // Start waiting on the event - there can be only one thread waiting.
11880 assert(self.state == .empty);
11881 self.state = .waiting;
11882
11883 while (true) {
11884 // Block using either pthread_cond_wait or pthread_cond_timewait if there's an absolute timeout.
11885 const rc = blk: {
11886 if (timeout == null) break :blk c.pthread_cond_wait(&self.cond, &self.mutex);
11887 break :blk c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts);
11888 };
11889
11890 // After waking up, check if the event was set.
11891 if (self.state == .notified) {
11892 return;
11893 }
11894
11895 assert(self.state == .waiting);
11896 switch (rc) {
11897 .SUCCESS => {},
11898 .TIMEDOUT => {
11899 // If timed out, reset the event to avoid the set() thread doing an unnecessary signal().
11900 self.state = .empty;
11901 return error.Timeout;
11902 },
11903 .INVAL => recoverableOsBugDetected(), // cond, mutex, and potentially ts should all be valid
11904 .PERM => recoverableOsBugDetected(), // mutex is locked when cond_*wait() functions are called
11905 else => recoverableOsBugDetected(),
11906 }
11907 }
11908 }
11909
11910 fn set(self: *Event) void {
11911 assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
11912 defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
11913
11914 // Make sure that multiple calls to set() were not done on the same Event.
11915 const old_state = self.state;
11916 assert(old_state != .notified);
11917
11918 // Mark the event as set and wake up the waiting thread if there was one.
11919 // This must be done while the mutex as the wait() thread could deallocate
11920 // the condition variable once it observes the new state, potentially causing a UAF if done unlocked.
11921 self.state = .notified;
11922 if (old_state == .waiting) {
11923 assert(c.pthread_cond_signal(&self.cond) == .SUCCESS);
11924 }
11925 }
11926 };
11927
11928 const Treap = std.Treap(usize, std.math.order);
11929 const Waiter = struct {
11930 node: Treap.Node,
11931 prev: ?*Waiter,
11932 next: ?*Waiter,
11933 tail: ?*Waiter,
11934 is_queued: bool,
11935 event: Event,
11936 };
11937
11938 // An unordered set of Waiters
11939 const WaitList = struct {
11940 top: ?*Waiter = null,
11941 len: usize = 0,
11942
11943 fn push(self: *WaitList, waiter: *Waiter) void {
11944 waiter.next = self.top;
11945 self.top = waiter;
11946 self.len += 1;
11947 }
11948
11949 fn pop(self: *WaitList) ?*Waiter {
11950 const waiter = self.top orelse return null;
11951 self.top = waiter.next;
11952 self.len -= 1;
11953 return waiter;
11954 }
11955 };
11956
11957 const WaitQueue = struct {
11958 fn insert(treap: *Treap, address: usize, waiter: *Waiter) void {
11959 // prepare the waiter to be inserted.
11960 waiter.next = null;
11961 waiter.is_queued = true;
11962
11963 // Find the wait queue entry associated with the address.
11964 // If there isn't a wait queue on the address, this waiter creates the queue.
11965 var entry = treap.getEntryFor(address);
11966 const entry_node = entry.node orelse {
11967 waiter.prev = null;
11968 waiter.tail = waiter;
11969 entry.set(&waiter.node);
11970 return;
11971 };
11972
11973 // There's a wait queue on the address; get the queue head and tail.
11974 const head: *Waiter = @fieldParentPtr("node", entry_node);
11975 const tail = head.tail orelse unreachable;
11976
11977 // Push the waiter to the tail by replacing it and linking to the previous tail.
11978 head.tail = waiter;
11979 tail.next = waiter;
11980 waiter.prev = tail;
11981 }
11982
11983 fn remove(treap: *Treap, address: usize, max_waiters: usize) WaitList {
11984 // Find the wait queue associated with this address and get the head/tail if any.
11985 var entry = treap.getEntryFor(address);
11986 var queue_head: ?*Waiter = if (entry.node) |node| @fieldParentPtr("node", node) else null;
11987 const queue_tail = if (queue_head) |head| head.tail else null;
11988
11989 // Once we're done updating the head, fix it's tail pointer and update the treap's queue head as well.
11990 defer entry.set(blk: {
11991 const new_head = queue_head orelse break :blk null;
11992 new_head.tail = queue_tail;
11993 break :blk &new_head.node;
11994 });
11995
11996 var removed = WaitList{};
11997 while (removed.len < max_waiters) {
11998 // dequeue and collect waiters from their wait queue.
11999 const waiter = queue_head orelse break;
12000 queue_head = waiter.next;
12001 removed.push(waiter);
12002
12003 // When dequeueing, we must mark is_queued as false.
12004 // This ensures that a waiter which calls tryRemove() returns false.
12005 assert(waiter.is_queued);
12006 waiter.is_queued = false;
12007 }
12008
12009 return removed;
12010 }
12011
12012 fn tryRemove(treap: *Treap, address: usize, waiter: *Waiter) bool {
12013 if (!waiter.is_queued) {
12014 return false;
12015 }
12016
12017 queue_remove: {
12018 // Find the wait queue associated with the address.
12019 var entry = blk: {
12020 // A waiter without a previous link means it's the queue head that's in the treap so we can avoid lookup.
12021 if (waiter.prev == null) {
12022 assert(waiter.node.key == address);
12023 break :blk treap.getEntryForExisting(&waiter.node);
12024 }
12025 break :blk treap.getEntryFor(address);
12026 };
12027
12028 // The queue head and tail must exist if we're removing a queued waiter.
12029 const head: *Waiter = @fieldParentPtr("node", entry.node orelse unreachable);
12030 const tail = head.tail orelse unreachable;
12031
12032 // A waiter with a previous link is never the head of the queue.
12033 if (waiter.prev) |prev| {
12034 assert(waiter != head);
12035 prev.next = waiter.next;
12036
12037 // A waiter with both a previous and next link is in the middle.
12038 // We only need to update the surrounding waiter's links to remove it.
12039 if (waiter.next) |next| {
12040 assert(waiter != tail);
12041 next.prev = waiter.prev;
12042 break :queue_remove;
12043 }
12044
12045 // A waiter with a previous but no next link means it's the tail of the queue.
12046 // In that case, we need to update the head's tail reference.
12047 assert(waiter == tail);
12048 head.tail = waiter.prev;
12049 break :queue_remove;
12050 }
12051
12052 // A waiter with no previous link means it's the queue head of queue.
12053 // We must replace (or remove) the head waiter reference in the treap.
12054 assert(waiter == head);
12055 entry.set(blk: {
12056 const new_head = waiter.next orelse break :blk null;
12057 new_head.tail = head.tail;
12058 break :blk &new_head.node;
12059 });
12060 }
12061
12062 // Mark the waiter as successfully removed.
12063 waiter.is_queued = false;
12064 return true;
12065 }
12066 };
12067
12068 const Bucket = struct {
12069 mutex: c.pthread_mutex_t align(atomic.cache_line) = .{},
12070 pending: atomic.Value(usize) = atomic.Value(usize).init(0),
12071 treap: Treap = .{},
12072
12073 // Global array of buckets that addresses map to.
12074 // Bucket array size is pretty much arbitrary here, but it must be a power of two for fibonacci hashing.
12075 var buckets = [_]Bucket{.{}} ** @bitSizeOf(usize);
12076
12077 // https://github.com/Amanieu/parking_lot/blob/1cf12744d097233316afa6c8b7d37389e4211756/core/src/parking_lot.rs#L343-L353
12078 fn from(address: usize) *Bucket {
12079 // The upper `@bitSizeOf(usize)` bits of the fibonacci golden ratio.
12080 // Hashing this via (h * k) >> (64 - b) where k=golden-ration and b=bitsize-of-array
12081 // evenly lays out h=hash values over the bit range even when the hash has poor entropy (identity-hash for pointers).
12082 const max_multiplier_bits = @bitSizeOf(usize);
12083 const fibonacci_multiplier = 0x9E3779B97F4A7C15 >> (64 - max_multiplier_bits);
12084
12085 const max_bucket_bits = @ctz(buckets.len);
12086 comptime assert(std.math.isPowerOfTwo(buckets.len));
12087
12088 const index = (address *% fibonacci_multiplier) >> (max_multiplier_bits - max_bucket_bits);
12089 return &buckets[index];
12090 }
12091 };
12092
12093 const Address = struct {
12094 fn from(ptr: *const u32) usize {
12095 // Get the alignment of the pointer.
12096 const alignment = @alignOf(atomic.Value(u32));
12097 comptime assert(std.math.isPowerOfTwo(alignment));
12098
12099 // Make sure the pointer is aligned,
12100 // then cut off the zero bits from the alignment to get the unique address.
12101 const addr = @intFromPtr(ptr);
12102 assert(addr & (alignment - 1) == 0);
12103 return addr >> @ctz(@as(usize, alignment));
12104 }
12105 };
12106
12107 fn wait(ptr: *const u32, expect: u32, timeout: ?u64) error{Timeout}!void {
12108 const address = Address.from(ptr);
12109 const bucket = Bucket.from(address);
12110
12111 // Announce that there's a waiter in the bucket before checking the ptr/expect condition.
12112 // If the announcement is reordered after the ptr check, the waiter could deadlock:
12113 //
12114 // - T1: checks ptr == expect which is true
12115 // - T2: updates ptr to != expect
12116 // - T2: does Futex.wake(), sees no pending waiters, exits
12117 // - T1: bumps pending waiters (was reordered after the ptr == expect check)
12118 // - T1: goes to sleep and misses both the ptr change and T2's wake up
12119 //
12120 // acquire barrier to ensure the announcement happens before the ptr check below.
12121 var pending = bucket.pending.fetchAdd(1, .acquire);
12122 assert(pending < std.math.maxInt(usize));
12123
12124 // If the wait gets canceled, remove the pending count we previously added.
12125 // This is done outside the mutex lock to keep the critical section short in case of contention.
12126 var canceled = false;
12127 defer if (canceled) {
12128 pending = bucket.pending.fetchSub(1, .monotonic);
12129 assert(pending > 0);
12130 };
12131
12132 var waiter: Waiter = undefined;
12133 {
12134 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
12135 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
12136
12137 canceled = @atomicLoad(u32, ptr, .monotonic) != expect;
12138 if (canceled) {
12139 return;
12140 }
12141
12142 waiter.event.init();
12143 WaitQueue.insert(&bucket.treap, address, &waiter);
12144 }
12145
12146 defer {
12147 assert(!waiter.is_queued);
12148 waiter.event.deinit();
12149 }
12150
12151 waiter.event.wait(timeout) catch {
12152 // If we fail to cancel after a timeout, it means a wake() thread
12153 // dequeued us and will wake us up. We must wait until the event is
12154 // set as that's a signal that the wake() thread won't access the
12155 // waiter memory anymore. If we return early without waiting, the
12156 // waiter on the stack would be invalidated and the wake() thread
12157 // risks a UAF.
12158 defer if (!canceled) waiter.event.wait(null) catch unreachable;
12159
12160 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
12161 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
12162
12163 canceled = WaitQueue.tryRemove(&bucket.treap, address, &waiter);
12164 if (canceled) {
12165 return error.Timeout;
12166 }
12167 };
12168 }
12169
12170 fn wake(ptr: *const u32, max_waiters: u32) void {
12171 const address = Address.from(ptr);
12172 const bucket = Bucket.from(address);
12173
12174 // Quick check if there's even anything to wake up.
12175 // The change to the ptr's value must happen before we check for pending waiters.
12176 // If not, the wake() thread could miss a sleeping waiter and have it deadlock:
12177 //
12178 // - T2: p = has pending waiters (reordered before the ptr update)
12179 // - T1: bump pending waiters
12180 // - T1: if ptr == expected: sleep()
12181 // - T2: update ptr != expected
12182 // - T2: p is false from earlier so doesn't wake (T1 missed ptr update and T2 missed T1 sleeping)
12183 //
12184 // What we really want here is a Release load, but that doesn't exist under the C11 memory model.
12185 // We could instead do `bucket.pending.fetchAdd(0, Release) == 0` which achieves effectively the same thing,
12186 // LLVM lowers the fetchAdd(0, .release) into an mfence+load which avoids gaining ownership of the cache-line.
12187 if (bucket.pending.fetchAdd(0, .release) == 0) {
12188 return;
12189 }
12190
12191 // Keep a list of all the waiters notified and wake then up outside the mutex critical section.
12192 var notified = WaitList{};
12193 defer if (notified.len > 0) {
12194 const pending = bucket.pending.fetchSub(notified.len, .monotonic);
12195 assert(pending >= notified.len);
12196
12197 while (notified.pop()) |waiter| {
12198 assert(!waiter.is_queued);
12199 waiter.event.set();
12200 }
12201 };
12202
12203 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
12204 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
12205
12206 // Another pending check again to avoid the WaitQueue lookup if not necessary.
12207 if (bucket.pending.load(.monotonic) > 0) {
12208 notified = WaitQueue.remove(&bucket.treap, address, max_waiters);
12209 }
12210 }
12211};
12212
12213fn scanEnviron(t: *Threaded) void {12580fn scanEnviron(t: *Threaded) void {
12214 t.mutex.lock();12581 t.mutex.lock();
12215 defer t.mutex.unlock();12582 defer t.mutex.unlock();
...@@ -12328,3 +12695,459 @@ fn scanEnviron(t: *Threaded) void {...@@ -12328,3 +12695,459 @@ fn scanEnviron(t: *Threaded) void {
12328test {12695test {
12329 _ = @import("Threaded/test.zig");12696 _ = @import("Threaded/test.zig");
12330}12697}
12698
12699const use_parking_futex = switch (builtin.target.os.tag) {
12700 .windows => true, // RtlWaitOnAddress is a userland implementation anyway
12701 .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now.
12702 .illumos => true, // Illumos has no futex mechanism
12703 else => false,
12704};
12705const use_parking_sleep = switch (builtin.target.os.tag) {
12706 // On Windows, we can implement sleep either with `NtDelayExecution` (which is how `SleepEx` in
12707 // kernel32 works) or `NtWaitForAlertByThreadId` (thread parking). We're already using the
12708 // latter for futex, so we may as well use it for sleeping too, to maximise code reuse. I'm
12709 // also more confident that it will always correctly handle the cancelation race (so "unpark"
12710 // before "park" causes "park" to return immediately): it *seems* like alertable sleeps paired
12711 // with `NtAlertThread` do actually do this too, but there could be some caveat (e.g. it might
12712 // fail under some specific condition), whereas `NtWaitForAlertByThreadId` must reliably trigger
12713 // this behavior because `RtlWaitOnAddress` relies on it.
12714 .windows => true,
12715
12716 // These targets have `_lwp_park`, which is superior to POSIX nanosleep because it has a better
12717 // cancelation mechanism.
12718 .netbsd,
12719 .illumos,
12720 => true,
12721
12722 else => false,
12723};
12724
12725const parking_futex = struct {
12726 comptime {
12727 assert(use_parking_futex);
12728 }
12729
12730 const Bucket = struct {
12731 /// Used as a fast check for `wake` to avoid having to acquire `mutex` to discover there are no
12732 /// waiters. It is important for `wait` to increment this *before* checking the futex value to
12733 /// avoid a race.
12734 num_waiters: std.atomic.Value(u32),
12735 /// Protects `waiters`.
12736 mutex: std.Thread.Mutex,
12737 waiters: std.DoublyLinkedList,
12738
12739 /// Prevent false sharing between buckets.
12740 _: void align(std.atomic.cache_line) = {},
12741
12742 const init: Bucket = .{ .num_waiters = .init(0), .mutex = .{}, .waiters = .{} };
12743 };
12744
12745 const Waiter = struct {
12746 node: std.DoublyLinkedList.Node,
12747 address: usize,
12748 tid: std.Thread.Id,
12749 /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread
12750 /// which atomically updates it (to `.none` or `.canceling`) is responsible for:
12751 ///
12752 /// * Removing the `Waiter` from `Bucket.waiters`
12753 /// * Decrementing `Bucket.num_waiters`
12754 /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope
12755 /// while it is still in the `Bucket`).
12756 thread_status: *std.atomic.Value(Thread.Status),
12757 };
12758
12759 fn bucketForAddress(address: usize) *Bucket {
12760 const global = struct {
12761 /// Length must be a power of two. The longer this array, the less likely contention is
12762 /// between different futexes. This length seems like it'll provide a reasonable balance
12763 /// between contention and memory usage: assuming a 128-byte `Bucket` (due to cache line
12764 /// alignment), this uses 32 KiB of memory.
12765 var buckets: [256]Bucket = @splat(.init);
12766 };
12767
12768 // Here we use Fibonacci hashing: the golden ratio can be used to evenly redistribute input
12769 // values across a range, giving a poor, but extremely quick to compute, hash.
12770
12771 // This literal is the rounded value of '2^64 / phi' (where 'phi' is the golden ratio). The
12772 // shift then converts it to '2^b / phi', where 'b' is the pointer bit width.
12773 const fibonacci_multiplier = 0x9E3779B97F4A7C15 >> (64 - @bitSizeOf(usize));
12774 const hashed = address *% fibonacci_multiplier;
12775
12776 comptime assert(std.math.isPowerOfTwo(global.buckets.len));
12777 // The high bits of `hashed` have better entropy than the low bits.
12778 const index = hashed >> (@bitSizeOf(usize) - @ctz(global.buckets.len));
12779
12780 return &global.buckets[index];
12781 }
12782
12783 fn wait(ptr: *const u32, expect: u32, uncancelable: bool, timeout: Io.Timeout) Io.Cancelable!void {
12784 const bucket = bucketForAddress(@intFromPtr(ptr));
12785
12786 // Put the threadlocal access outside of the critical section.
12787 const opt_thread = Thread.current;
12788 const self_tid = if (opt_thread) |thread| thread.id else std.Thread.getCurrentId();
12789
12790 var waiter: Waiter = .{
12791 .node = undefined, // populated by list append
12792 .address = @intFromPtr(ptr),
12793 .tid = self_tid,
12794 .thread_status = undefined, // populated in critical section
12795 };
12796
12797 var status_buf: std.atomic.Value(Thread.Status) = undefined;
12798
12799 {
12800 bucket.mutex.lock();
12801 defer bucket.mutex.unlock();
12802
12803 _ = bucket.num_waiters.fetchAdd(1, .acquire);
12804
12805 if (@atomicLoad(u32, ptr, .monotonic) != expect) {
12806 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
12807 return;
12808 }
12809
12810 // This is in the critical section to avoid marking the thread as parked until we're
12811 // certain that we're actually going to park.
12812 waiter.thread_status = status: {
12813 cancelable: {
12814 if (uncancelable) break :cancelable;
12815 const thread = opt_thread orelse break :cancelable;
12816 switch (thread.cancel_protection) {
12817 .blocked => break :cancelable,
12818 .unblocked => {},
12819 }
12820 thread.futex_waiter = &waiter;
12821 const old_status = thread.status.fetchOr(
12822 .{ .cancelation = @enumFromInt(0b001), .awaitable = .null },
12823 .release, // release `thread.futex_waiter`
12824 );
12825 switch (old_status.cancelation) {
12826 .none => {}, // status is now `.parked`
12827 .canceling => {
12828 // status is now `.canceled`
12829 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
12830 return error.Canceled;
12831 },
12832 .canceled => break :cancelable, // status is still `.canceled`
12833 .parked => unreachable,
12834 .blocked => unreachable,
12835 .blocked_windows_dns => unreachable,
12836 .blocked_canceling => unreachable,
12837 }
12838 // We could now be unparked for a cancelation at any time!
12839 break :status &thread.status;
12840 }
12841 // This is an uncancelable wait, so just use `status_buf`. Note that the value of
12842 // `status_buf.awaitable` is irrelevant because this is only visible to futex code,
12843 // while only cancelation cares about `awaitable`.
12844 status_buf.raw = .{ .cancelation = .parked, .awaitable = .null };
12845 break :status &status_buf;
12846 };
12847
12848 bucket.waiters.append(&waiter.node);
12849 }
12850
12851 if (park(timeout, ptr)) {
12852 // We were unparked by either `wake` or cancelation, so our current status is either
12853 // `.none` or `.canceling`. In either case, they've already removed `waiter` from
12854 // `bucket`, so we have nothing more to do!
12855 } else |err| switch (err) {
12856 error.Timeout => {
12857 // We're not out of the woods yet: an unpark could race with the timeout.
12858 const old_status = waiter.thread_status.fetchAnd(
12859 .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones },
12860 .monotonic,
12861 );
12862 switch (old_status.cancelation) {
12863 .parked => {
12864 // No race. It is our responsibility to remove `waiter` from `bucket`.
12865 // New status is `.none`.
12866 bucket.mutex.lock();
12867 defer bucket.mutex.unlock();
12868 bucket.waiters.remove(&waiter.node);
12869 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
12870 },
12871 .none, .canceling => {
12872 // Race condition: the timeout was reached, then `wake` or a canceler tried
12873 // to unpark us. Whoever did that will remove us from `bucket`. Wait for
12874 // that (and drop the unpark request in doing so).
12875 // New status is `.none` or `.canceling` respectively.
12876 park(.none, ptr) catch |e| switch (e) {
12877 error.Timeout => unreachable,
12878 };
12879 },
12880 .canceled => unreachable,
12881 .blocked => unreachable,
12882 .blocked_windows_dns => unreachable,
12883 .blocked_canceling => unreachable,
12884 }
12885 },
12886 }
12887 }
12888
12889 fn wake(ptr: *const u32, max_waiters: u32) void {
12890 if (max_waiters == 0) return;
12891
12892 const bucket = bucketForAddress(@intFromPtr(ptr));
12893
12894 // To ensure the store to `ptr` is ordered before this check, we effectively want a `.release`
12895 // load, but that doesn't exist in the C11 memory model, so emulate it with a non-mutating rmw.
12896 if (bucket.num_waiters.fetchAdd(0, .release) == 0) {
12897 @branchHint(.likely);
12898 return; // no waiters
12899 }
12900
12901 // Waiters removed from the linked list under the mutex so we can unpark their threads outside
12902 // of the critical section. This forms a singly-linked list of waiters using `Waiter.node.next`.
12903 var waking_head: ?*std.DoublyLinkedList.Node = null;
12904 {
12905 bucket.mutex.lock();
12906 defer bucket.mutex.unlock();
12907
12908 var num_removed: u32 = 0;
12909 var it = bucket.waiters.first;
12910 while (num_removed < max_waiters) {
12911 const waiter: *Waiter = @fieldParentPtr("node", it orelse break);
12912 it = waiter.node.next;
12913 if (waiter.address != @intFromPtr(ptr)) continue;
12914 const old_status = waiter.thread_status.fetchAnd(
12915 .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones },
12916 .monotonic,
12917 );
12918 switch (old_status.cancelation) {
12919 .parked => {}, // state updated to `.none`
12920 .none => unreachable, // if another `wake` call is unparking this thread, it should have removed it from the list
12921 .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet
12922 .canceled => unreachable,
12923 .blocked => unreachable,
12924 .blocked_windows_dns => unreachable,
12925 .blocked_canceling => unreachable,
12926 }
12927 // We're waking this waiter. Remove them from the bucket and add them to our local list.
12928 bucket.waiters.remove(&waiter.node);
12929 waiter.node.next = waking_head;
12930 waking_head = &waiter.node;
12931 num_removed += 1;
12932 // Signal to `waiter` that they're about to be unparked, in case we're racing with their
12933 // timeout. See corresponding logic in `wake`.
12934 waiter.address = 0;
12935 }
12936
12937 _ = bucket.num_waiters.fetchSub(num_removed, .monotonic);
12938 }
12939
12940 var unpark_buf: [128]UnparkTid = undefined;
12941 var unpark_len: usize = 0;
12942
12943 // Finally, unpark the threads.
12944 while (waking_head) |node| {
12945 waking_head = node.next;
12946 const waiter: *Waiter = @fieldParentPtr("node", node);
12947 unpark_buf[unpark_len] = waiter.tid;
12948 unpark_len += 1;
12949 if (unpark_len == unpark_buf.len) {
12950 unpark(&unpark_buf, ptr);
12951 unpark_len = 0;
12952 }
12953 }
12954 if (unpark_len > 0) {
12955 unpark(unpark_buf[0..unpark_len], ptr);
12956 }
12957 }
12958
12959 fn removeCanceledWaiter(waiter: *Waiter) void {
12960 const bucket = bucketForAddress(waiter.address);
12961 bucket.mutex.lock();
12962 defer bucket.mutex.unlock();
12963 bucket.waiters.remove(&waiter.node);
12964 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
12965 }
12966};
12967const parking_sleep = struct {
12968 comptime {
12969 assert(use_parking_sleep);
12970 }
12971 fn sleep(timeout: Io.Timeout) Io.Cancelable!void {
12972 const opt_thread = Thread.current;
12973 cancelable: {
12974 const thread = opt_thread orelse break :cancelable;
12975 switch (thread.cancel_protection) {
12976 .blocked => break :cancelable,
12977 .unblocked => {},
12978 }
12979 thread.futex_waiter = null;
12980 {
12981 const old_status = thread.status.fetchOr(
12982 .{ .cancelation = @enumFromInt(0b001), .awaitable = .null },
12983 .release, // release `thread.futex_waiter`
12984 );
12985 switch (old_status.cancelation) {
12986 .none => {}, // status is now `.parked`
12987 .canceling => return error.Canceled, // status is now `.canceled`
12988 .canceled => break :cancelable, // status is still `.canceled`
12989 .parked => unreachable,
12990 .blocked => unreachable,
12991 .blocked_windows_dns => unreachable,
12992 .blocked_canceling => unreachable,
12993 }
12994 }
12995 if (park(timeout, null)) {
12996 // The only reason this could possibly happen is cancelation.
12997 const old_status = thread.status.load(.monotonic);
12998 assert(old_status.cancelation == .canceling);
12999 thread.status.store(
13000 .{ .cancelation = .canceled, .awaitable = old_status.awaitable },
13001 .monotonic,
13002 );
13003 return error.Canceled;
13004 } else |err| switch (err) {
13005 error.Timeout => {
13006 // We're not out of the woods yet: an unpark could race with the timeout.
13007 const old_status = thread.status.fetchAnd(
13008 .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones },
13009 .monotonic,
13010 );
13011 switch (old_status.cancelation) {
13012 .parked => return, // No race; new status is `.none`
13013 .canceling => {
13014 // Race condition: the timeout was reached, then someone tried to unpark
13015 // us for a cancelation. Whoever did that will have called `unpark`, so
13016 // drop that unpark request by waiting for it.
13017 // Status is still `.canceling`.
13018 park(.none, null) catch |e| switch (e) {
13019 error.Timeout => unreachable,
13020 };
13021 return;
13022 },
13023 .none => unreachable,
13024 .canceled => unreachable,
13025 .blocked => unreachable,
13026 .blocked_windows_dns => unreachable,
13027 .blocked_canceling => unreachable,
13028 }
13029 },
13030 }
13031 }
13032 // Uncancelable sleep; we expect not to be manually unparked.
13033 if (park(timeout, null)) {
13034 unreachable; // unexpected unpark
13035 } else |err| switch (err) {
13036 error.Timeout => return,
13037 }
13038 }
13039};
13040
13041/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
13042fn park(timeout: Io.Timeout, addr_hint: ?*const anyopaque) error{Timeout}!void {
13043 comptime assert(use_parking_futex or use_parking_sleep);
13044 switch (builtin.target.os.tag) {
13045 .windows => {
13046 var timeout_buf: windows.LARGE_INTEGER = undefined;
13047 const raw_timeout: ?*windows.LARGE_INTEGER = timeout: switch (timeout) {
13048 .none => null,
13049 .deadline => |timestamp| continue :timeout .{ .duration = .{
13050 .clock = timestamp.clock,
13051 .raw = (nowWindows(timestamp.clock) catch unreachable).durationTo(timestamp.raw),
13052 } },
13053 .duration => |duration| {
13054 _ = duration.clock; // Windows only supports monotonic
13055 timeout_buf = @intCast(@divTrunc(-duration.raw.nanoseconds, 100));
13056 break :timeout &timeout_buf;
13057 },
13058 };
13059 // `RtlWaitOnAddress` passes the futex address in as the first argument to this call,
13060 // but it's unclear what that actually does, especially since `NtAlertThreadByThreadId`
13061 // does *not* accept the address so the kernel can't really be using it as a hint. An
13062 // old Microsoft blog post discusses a more traditional futex-like mechanism in the
13063 // kernel which definitely isn't how `RtlWaitOnAddress` works today:
13064 //
13065 // https://devblogs.microsoft.com/oldnewthing/20160826-00/?p=94185
13066 //
13067 // ...so it's possible this argument is simply a remnant which no longer does anything
13068 // (perhaps the implementation changed during development but someone forgot to remove
13069 // this parameter). However, to err on the side of caution, let's match the behavior of
13070 // `RtlWaitOnAddress` and pass the pointer, in case the kernel ever does something
13071 // stupid such as trying to dereference it.
13072 switch (windows.ntdll.NtWaitForAlertByThreadId(addr_hint, raw_timeout)) {
13073 .ALERTED => return,
13074 .TIMEOUT => return error.Timeout,
13075 else => unreachable,
13076 }
13077 },
13078 .netbsd => {
13079 var ts_buf: posix.timespec = undefined;
13080 const ts: ?*posix.timespec, const abstime: bool, const clock_real: bool = switch (timeout) {
13081 .none => .{ null, false, false },
13082 .deadline => |timestamp| timeout: {
13083 ts_buf = timestampToPosix(timestamp.raw.nanoseconds);
13084 break :timeout .{ &ts_buf, true, timestamp.clock == .real };
13085 },
13086 .duration => |duration| timeout: {
13087 ts_buf = timestampToPosix(duration.raw.nanoseconds);
13088 break :timeout .{ &ts_buf, false, duration.clock == .real };
13089 },
13090 };
13091 switch (posix.errno(std.c._lwp_park(
13092 if (clock_real) .REALTIME else .MONOTONIC,
13093 .{ .ABSTIME = abstime },
13094 ts,
13095 0,
13096 addr_hint,
13097 null,
13098 ))) {
13099 .SUCCESS, .ALREADY, .INTR => return,
13100 .TIMEDOUT => return error.Timeout,
13101 .INVAL => unreachable,
13102 .SRCH => unreachable,
13103 else => unreachable,
13104 }
13105 },
13106 .illumos => @panic("TODO: illumos lwp_park"),
13107 else => comptime unreachable,
13108 }
13109}
13110
13111const UnparkTid = switch (builtin.target.os.tag) {
13112 // `NtAlertMultipleThreadByThreadId` is weird and wants 64-bit thread handles?
13113 .windows => usize,
13114 else => std.Thread.Id,
13115};
13116/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
13117fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
13118 comptime assert(use_parking_futex or use_parking_sleep);
13119 switch (builtin.target.os.tag) {
13120 .windows => {
13121 // TODO: this condition is currently disabled because mingw-w64 does not contain this
13122 // symbol. Once it's added, enable this check to use the new bulk API where possible.
13123 if (false and (builtin.os.version_range.windows.isAtLeast(.win11_dt) orelse false)) {
13124 _ = windows.ntdll.NtAlertMultipleThreadByThreadId(tids.ptr, @intCast(tids.len), null, null);
13125 } else {
13126 for (tids) |tid| {
13127 _ = windows.ntdll.NtAlertThreadByThreadId(@intCast(tid));
13128 }
13129 }
13130 },
13131 .netbsd => {
13132 switch (posix.errno(std.c._lwp_unpark_all(@ptrCast(tids.ptr), tids.len, addr_hint))) {
13133 .SUCCESS => return,
13134 // For errors, fall through to a loop over `tids`, though this is only expected to
13135 // be possible for ENOMEM (and even that is questionable).
13136 .SRCH => recoverableOsBugDetected(),
13137 .FAULT => recoverableOsBugDetected(),
13138 .INVAL => recoverableOsBugDetected(),
13139 .NOMEM => {},
13140 else => recoverableOsBugDetected(),
13141 }
13142 for (tids) |tid| {
13143 switch (posix.errno(std.c._lwp_unpark(@bitCast(tid), addr_hint))) {
13144 .SUCCESS => {},
13145 .SRCH => recoverableOsBugDetected(),
13146 else => recoverableOsBugDetected(),
13147 }
13148 }
13149 },
13150 .illumos => @panic("TODO: illumos lwp_unpark"),
13151 else => comptime unreachable,
13152 }
13153}
lib/std/Io/Threaded/test.zig+46-1
...@@ -124,7 +124,7 @@ test "Group.async context alignment" {...@@ -124,7 +124,7 @@ test "Group.async context alignment" {
124 var group: std.Io.Group = .init;124 var group: std.Io.Group = .init;
125 var result: ByteArray512 = undefined;125 var result: ByteArray512 = undefined;
126 group.async(io, concatByteArraysResultPtr, .{ a, b, &result });126 group.async(io, concatByteArraysResultPtr, .{ a, b, &result });
127 group.awaitUncancelable(io);127 try group.await(io);
128 try std.testing.expectEqualSlices(u8, &expected.x, &result.x);128 try std.testing.expectEqualSlices(u8, &expected.x, &result.x);
129}129}
130130
...@@ -141,3 +141,48 @@ test "async with array return type" {...@@ -141,3 +141,48 @@ test "async with array return type" {
141 const result = future.await(io);141 const result = future.await(io);
142 try std.testing.expectEqualSlices(u8, &@as([32]u8, @splat(5)), &result);142 try std.testing.expectEqualSlices(u8, &@as([32]u8, @splat(5)), &result);
143}143}
144
145test "cancel blocked read from pipe" {
146 const global = struct {
147 fn readFromPipe(io: Io, pipe: Io.File) !void {
148 var buf: [1]u8 = undefined;
149 if (pipe.readStreaming(io, &.{&buf})) |_| {
150 return error.UnexpectedData;
151 } else |err| switch (err) {
152 error.Canceled => return,
153 else => |e| return e,
154 }
155 }
156 };
157
158 var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
159 defer threaded.deinit();
160 const io = threaded.io();
161
162 var read_end: Io.File = undefined;
163 var write_end: Io.File = undefined;
164 switch (builtin.target.os.tag) {
165 .wasi => return error.SkipZigTest,
166 .windows => try std.os.windows.CreatePipe(&read_end.handle, &write_end.handle, &.{
167 .nLength = @sizeOf(std.os.windows.SECURITY_ATTRIBUTES),
168 .lpSecurityDescriptor = null,
169 .bInheritHandle = std.os.windows.FALSE,
170 }),
171 else => {
172 const pipe = try std.posix.pipe();
173 read_end = .{ .handle = pipe[0] };
174 write_end = .{ .handle = pipe[1] };
175 },
176 }
177 defer {
178 read_end.close(io);
179 write_end.close(io);
180 }
181
182 var future = io.concurrent(global.readFromPipe, .{ io, read_end }) catch |err| switch (err) {
183 error.ConcurrencyUnavailable => return error.SkipZigTest,
184 };
185 defer _ = future.cancel(io) catch {};
186 try io.sleep(.fromMilliseconds(10), .awake);
187 try future.cancel(io);
188}
lib/std/Io/net/HostName.zig+9-13
...@@ -233,11 +233,12 @@ pub fn connect(...@@ -233,11 +233,12 @@ pub fn connect(
233 if (result) |stream| {233 if (result) |stream| {
234 return stream;234 return stream;
235 } else |err| switch (err) {235 } else |err| switch (err) {
236 error.Canceled => unreachable,
237
236 error.SystemResources,238 error.SystemResources,
237 error.OptionUnsupported,239 error.OptionUnsupported,
238 error.ProcessFdQuotaExceeded,240 error.ProcessFdQuotaExceeded,
239 error.SystemFdQuotaExceeded,241 error.SystemFdQuotaExceeded,
240 error.Canceled,
241 => |e| return e,242 => |e| return e,
242243
243 error.WouldBlock => return error.Unexpected,244 error.WouldBlock => return error.Unexpected,
...@@ -259,6 +260,8 @@ pub fn connect(...@@ -259,6 +260,8 @@ pub fn connect(
259/// Asynchronously establishes a connection to all IP addresses associated with260/// Asynchronously establishes a connection to all IP addresses associated with
260/// a host name, adding them to a results queue upon completion.261/// a host name, adding them to a results queue upon completion.
261///262///
263/// `error.Canceled` will never be added to the queue, but other errors may be.
264///
262/// Closes `results` before return, even on error.265/// Closes `results` before return, even on error.
263///266///
264/// Asserts `results` is not closed until this call returns.267/// Asserts `results` is not closed until this call returns.
...@@ -299,22 +302,15 @@ fn enqueueConnection(...@@ -299,22 +302,15 @@ fn enqueueConnection(
299 io: Io,302 io: Io,
300 queue: *Io.Queue(IpAddress.ConnectError!Stream),303 queue: *Io.Queue(IpAddress.ConnectError!Stream),
301 options: IpAddress.ConnectOptions,304 options: IpAddress.ConnectOptions,
302) void {
303 enqueueConnectionFallible(address, io, queue, options) catch |err| switch (err) {
304 error.Canceled => {},
305 };
306}
307fn enqueueConnectionFallible(
308 address: IpAddress,
309 io: Io,
310 queue: *Io.Queue(IpAddress.ConnectError!Stream),
311 options: IpAddress.ConnectOptions,
312) Io.Cancelable!void {305) Io.Cancelable!void {
313 const result = address.connect(io, options);306 const result = address.connect(io, options) catch |err| switch (err) {
307 error.Canceled => |e| return e,
308 else => |e| e, // other errors go in the result queue
309 };
314 errdefer if (result) |s| s.close(io) else |_| {};310 errdefer if (result) |s| s.close(io) else |_| {};
315 queue.putOne(io, result) catch |err| switch (err) {311 queue.putOne(io, result) catch |err| switch (err) {
316 error.Closed => unreachable, // `queue` must not be closed
317 error.Canceled => |e| return e,312 error.Canceled => |e| return e,
313 error.Closed => unreachable, // `queue` must not be closed
318 };314 };
319}315}
320316
lib/std/Io/test.zig+110-34
...@@ -194,7 +194,7 @@ test "Group" {...@@ -194,7 +194,7 @@ test "Group" {
194 group.async(io, count, .{ 1, 10, &results[0] });194 group.async(io, count, .{ 1, 10, &results[0] });
195 group.async(io, count, .{ 20, 30, &results[1] });195 group.async(io, count, .{ 20, 30, &results[1] });
196196
197 group.awaitUncancelable(io);197 try group.await(io);
198198
199 try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results);199 try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results);
200}200}
...@@ -207,49 +207,53 @@ fn count(a: usize, b: usize, result: *usize) void {...@@ -207,49 +207,53 @@ fn count(a: usize, b: usize, result: *usize) void {
207 result.* = sum;207 result.* = sum;
208}208}
209209
210test "Group cancelation" {210test "Group.cancel" {
211 const io = testing.io;211 const global = struct {
212 fn sleep(io: Io, result: *usize) Io.Cancelable!void {
213 defer result.* = 1;
214 io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
215 error.Canceled => |e| return e,
216 else => {},
217 };
218 }
212219
213 var group: Io.Group = .init;220 fn sleepRecancel(io: Io, result: *usize) void {
214 var results: [4]usize = .{ 0, 0, 0, 0 };221 io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
222 error.Canceled => io.recancel(),
223 else => {},
224 };
225 result.* = 1;
226 }
215227
216 // TODO when robust cancelation is available, make the sleep timeouts much228 fn sleepUncancelable(io: Io, result: *usize) void {
217 // longer so that it causes the unit test to be failed if not canceled.229 const old_prot = io.swapCancelProtection(.blocked);
218 // https://codeberg.org/ziglang/zig/issues/30049230 defer _ = io.swapCancelProtection(old_prot);
219 group.async(io, sleep, .{ io, &results[0] });231 // Short sleep interval, because this one won't be canceled (that's the point!).
220 group.async(io, sleep, .{ io, &results[1] });232 io.sleep(.fromMilliseconds(50), .awake) catch {};
221 group.async(io, sleepUncancelable, .{ io, &results[2] });233 result.* = 1;
222 group.async(io, sleepRecancel, .{ io, &results[3] });234 }
235 };
223236
224 group.cancel(io);237 const io = testing.io;
225238
226 try testing.expectEqualSlices(usize, &.{ 1, 1, 1, 1 }, &results);239 var group: Io.Group = .init;
227}240 var results: [5]usize = @splat(0);
228241
229fn sleep(io: Io, result: *usize) error{Canceled}!void {242 group.concurrent(io, global.sleep, .{ io, &results[0] }) catch |err| switch (err) {
230 defer result.* = 1;243 error.ConcurrencyUnavailable => return error.SkipZigTest,
231 io.sleep(.fromMilliseconds(1), .awake) catch |err| switch (err) {
232 error.Canceled => |e| return e,
233 else => {},
234 };244 };
235}245 try group.concurrent(io, global.sleep, .{ io, &results[1] });
246 try group.concurrent(io, global.sleepRecancel, .{ io, &results[2] });
247 try group.concurrent(io, global.sleepUncancelable, .{ io, &results[3] });
248 // Because this one doesn't block until canceled, it is safe to run asynchronously.
249 group.async(io, global.sleepUncancelable, .{ io, &results[4] });
236250
237fn sleepUncancelable(io: Io, result: *usize) void {251 group.cancel(io);
238 const old_prot = io.swapCancelProtection(.blocked);
239 defer _ = io.swapCancelProtection(old_prot);
240 io.sleep(.fromMilliseconds(1), .awake) catch {};
241 result.* = 1;
242}
243252
244fn sleepRecancel(io: Io, result: *usize) void {253 try testing.expectEqualSlices(usize, &.{ 1, 1, 1, 1, 1 }, &results);
245 io.sleep(.fromMilliseconds(1), .awake) catch |err| switch (err) {
246 error.Canceled => io.recancel(),
247 else => {},
248 };
249 result.* = 1;
250}254}
251255
252test "Group concurrent" {256test "Group.concurrent" {
253 const io = testing.io;257 const io = testing.io;
254258
255 var group: Io.Group = .init;259 var group: Io.Group = .init;
...@@ -488,3 +492,75 @@ test "swapCancelProtection" {...@@ -488,3 +492,75 @@ test "swapCancelProtection" {
488 // Because it reached the `set`, it should be too late for `sleepThenSet` to see `error.Canceled`.492 // Because it reached the `set`, it should be too late for `sleepThenSet` to see `error.Canceled`.
489 try set_future.cancel(io);493 try set_future.cancel(io);
490}494}
495
496test "cancel futex wait" {
497 const global = struct {
498 fn blockUntilCanceled(io: Io) void {
499 while (true) io.futexWait(u32, &0, 0) catch |err| switch (err) {
500 error.Canceled => return,
501 };
502 }
503 };
504
505 const io = std.testing.io;
506
507 var future = io.concurrent(global.blockUntilCanceled, .{io}) catch |err| switch (err) {
508 error.ConcurrencyUnavailable => return error.SkipZigTest,
509 };
510 defer future.cancel(io);
511
512 // Give the task some time to start so that we cancel while it is blocked.
513 try io.sleep(.fromMilliseconds(20), .awake);
514}
515
516test "cancel sleep" {
517 const global = struct {
518 fn blockUntilCanceled(io: Io) void {
519 while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
520 error.Canceled => return,
521 error.UnsupportedClock => @panic("unsupported clock"),
522 error.Unexpected => @panic("unexpected"),
523 };
524 }
525 };
526
527 const io = std.testing.io;
528
529 var future = io.concurrent(global.blockUntilCanceled, .{io}) catch |err| switch (err) {
530 error.ConcurrencyUnavailable => return error.SkipZigTest,
531 };
532 defer future.cancel(io);
533
534 // Give the task some time to start so that we cancel while it is blocked.
535 try io.sleep(.fromMilliseconds(20), .awake);
536}
537
538test "tasks spawned in group after Group.cancel are canceled" {
539 const global = struct {
540 fn waitThenSpawn(io: Io, group: *Io.Group) void {
541 _ = io.swapCancelProtection(.blocked);
542 group.concurrent(io, blockUntilCanceled, .{io}) catch {};
543 io.sleep(.fromMilliseconds(10), .awake) catch unreachable;
544 group.concurrent(io, blockUntilCanceled, .{io}) catch {};
545 group.async(io, blockUntilCanceled, .{io});
546 }
547 fn blockUntilCanceled(io: Io) Io.Cancelable!void {
548 while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
549 error.Canceled => |e| return e,
550 error.UnsupportedClock => @panic("unsupported clock"),
551 error.Unexpected => @panic("unexpected"),
552 };
553 }
554 };
555
556 const io = std.testing.io;
557
558 var group: Io.Group = .init;
559 defer group.cancel(io);
560
561 group.concurrent(io, global.blockUntilCanceled, .{io}) catch |err| switch (err) {
562 error.ConcurrencyUnavailable => return error.SkipZigTest,
563 };
564 try io.sleep(.fromMilliseconds(10), .awake); // let that first sleep start up
565 try group.concurrent(io, global.waitThenSpawn, .{ io, &group });
566}
lib/std/c.zig+3
...@@ -11399,6 +11399,9 @@ pub const vm_region_flavor_t = darwin.vm_region_flavor_t;...@@ -11399,6 +11399,9 @@ pub const vm_region_flavor_t = darwin.vm_region_flavor_t;
1139911399
11400pub const _ksiginfo = netbsd._ksiginfo;11400pub const _ksiginfo = netbsd._ksiginfo;
11401pub const _lwp_self = netbsd._lwp_self;11401pub const _lwp_self = netbsd._lwp_self;
11402pub const _lwp_park = netbsd._lwp_park;
11403pub const _lwp_unpark = netbsd._lwp_unpark;
11404pub const _lwp_unpark_all = netbsd._lwp_unpark_all;
11402pub const lwpid_t = netbsd.lwpid_t;11405pub const lwpid_t = netbsd.lwpid_t;
1140311406
11404pub const lwp_gettid = dragonfly.lwp_gettid;11407pub const lwp_gettid = dragonfly.lwp_gettid;
lib/std/c/netbsd.zig+19-1
...@@ -1,17 +1,35 @@...@@ -1,17 +1,35 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const clock_t = std.c.clock_t;2const clock_t = std.c.clock_t;
3const clockid_t = std.c.clockid_t;
3const pid_t = std.c.pid_t;4const pid_t = std.c.pid_t;
4const pthread_t = std.c.pthread_t;5const pthread_t = std.c.pthread_t;
5const sigval_t = std.c.sigval_t;6const sigval_t = std.c.sigval_t;
6const uid_t = std.c.uid_t;7const uid_t = std.c.uid_t;
8const timespec = std.c.timespec;
79
8pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: ?*anyopaque, data: c_int) c_int;10pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: ?*anyopaque, data: c_int) c_int;
911
10pub const lwpid_t = i32;12pub const lwpid_t = i32;
1113
12pub extern "c" fn _lwp_self() lwpid_t;
13pub extern "c" fn pthread_setname_np(thread: pthread_t, name: [*:0]const u8, arg: ?*anyopaque) c_int;14pub extern "c" fn pthread_setname_np(thread: pthread_t, name: [*:0]const u8, arg: ?*anyopaque) c_int;
1415
16pub extern "c" fn _lwp_self() lwpid_t;
17
18pub extern "c" fn _lwp_park(
19 clock_id: clockid_t,
20 flags: packed struct(u32) {
21 ABSTIME: bool = false,
22 unused: u31 = 0,
23 },
24 ts: ?*timespec,
25 unpark: lwpid_t,
26 hint: ?*const anyopaque,
27 unpark_hint: ?*const anyopaque,
28) c_int;
29
30pub extern "c" fn _lwp_unpark(lwp: lwpid_t, hint: ?*const anyopaque) c_int;
31pub extern "c" fn _lwp_unpark_all(targets: [*]const lwpid_t, ntargets: usize, hint: ?*const anyopaque) c_int;
32
15pub const TCIFLUSH = 1;33pub const TCIFLUSH = 1;
16pub const TCOFLUSH = 2;34pub const TCOFLUSH = 2;
17pub const TCIOFLUSH = 3;35pub const TCIOFLUSH = 3;
lib/std/debug/SelfInfo/Windows.zig+1-2
...@@ -315,8 +315,7 @@ const Module = struct {...@@ -315,8 +315,7 @@ const Module = struct {
315 );315 );
316 if (len == 0) return error.MissingDebugInfo;316 if (len == 0) return error.MissingDebugInfo;
317 const name_w = name_buffer[0 .. len + 4 :0];317 const name_w = name_buffer[0 .. len + 4 :0];
318 // TODO eliminate the reference to Io.Threaded.global_single_threaded here318 const coff_file = Io.Threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) {
319 const coff_file = Io.Threaded.global_single_threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) {
320 error.Canceled => |e| return e,319 error.Canceled => |e| return e,
321 error.Unexpected => |e| return e,320 error.Unexpected => |e| return e,
322 error.FileNotFound => return error.MissingDebugInfo,321 error.FileNotFound => return error.MissingDebugInfo,
lib/std/http/test.zig+16-3
...@@ -1139,13 +1139,26 @@ fn createTestServer(io: Io, S: type) !*TestServer {...@@ -1139,13 +1139,26 @@ fn createTestServer(io: Io, S: type) !*TestServer {
1139 }1139 }
11401140
1141 const address = try net.IpAddress.parse("127.0.0.1", 0);1141 const address = try net.IpAddress.parse("127.0.0.1", 0);
1142 const test_server = try std.testing.allocator.create(TestServer);1142
1143 const gpa = std.testing.allocator;
1144
1145 const test_server = try gpa.create(TestServer);
1146 errdefer gpa.destroy(test_server);
1147
1148 var net_server = try address.listen(io, .{ .reuse_address = true });
1149 errdefer net_server.deinit(io);
1150
1151 // populate `test_server` first so `S.run` can use it
1143 test_server.* = .{1152 test_server.* = .{
1144 .io = io,1153 .io = io,
1145 .net_server = try address.listen(io, .{ .reuse_address = true }),1154 .net_server = net_server,
1146 .shutting_down = false,1155 .shutting_down = false,
1147 .server_thread = try std.Thread.spawn(.{}, S.run, .{test_server}),1156 .server_thread = undefined, // set below
1148 };1157 };
1158
1159 test_server.server_thread = try .spawn(.{}, S.run, .{test_server});
1160 errdefer comptime unreachable;
1161
1149 return test_server;1162 return test_server;
1150}1163}
11511164
lib/std/os/windows.zig+6-66
...@@ -2253,7 +2253,7 @@ pub fn GetProcessHeap() ?*HEAP {...@@ -2253,7 +2253,7 @@ pub fn GetProcessHeap() ?*HEAP {
2253pub const OBJECT_ATTRIBUTES = extern struct {2253pub const OBJECT_ATTRIBUTES = extern struct {
2254 Length: ULONG,2254 Length: ULONG,
2255 RootDirectory: ?HANDLE,2255 RootDirectory: ?HANDLE,
2256 ObjectName: *UNICODE_STRING,2256 ObjectName: ?*UNICODE_STRING,
2257 Attributes: ATTRIBUTES,2257 Attributes: ATTRIBUTES,
2258 SecurityDescriptor: ?*anyopaque,2258 SecurityDescriptor: ?*anyopaque,
2259 SecurityQualityOfService: ?*anyopaque,2259 SecurityQualityOfService: ?*anyopaque,
...@@ -2306,6 +2306,7 @@ pub const OpenError = error{...@@ -2306,6 +2306,7 @@ pub const OpenError = error{
2306 NetworkNotFound,2306 NetworkNotFound,
2307 AntivirusInterference,2307 AntivirusInterference,
2308 BadPathName,2308 BadPathName,
2309 OperationCanceled,
2309};2310};
23102311
2311pub const OpenFileOptions = struct {2312pub const OpenFileOptions = struct {
...@@ -2405,6 +2406,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -2405,6 +2406,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
2405 continue;2406 continue;
2406 },2407 },
2407 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,2408 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
2409 .CANCELLED => return error.OperationCanceled,
2408 else => return unexpectedStatus(rc),2410 else => return unexpectedStatus(rc),
2409 }2411 }
2410 }2412 }
...@@ -2985,6 +2987,7 @@ pub const ReadLinkError = error{...@@ -2985,6 +2987,7 @@ pub const ReadLinkError = error{
2985 AntivirusInterference,2987 AntivirusInterference,
2986 UnsupportedReparsePointType,2988 UnsupportedReparsePointType,
2987 NotLink,2989 NotLink,
2990 OperationCanceled,
2988};2991};
29892992
2990/// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it2993/// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it
...@@ -3015,6 +3018,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLi...@@ -3015,6 +3018,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLi
3015 const rc = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] });3018 const rc = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] });
3016 switch (rc) {3019 switch (rc) {
3017 .SUCCESS => {},3020 .SUCCESS => {},
3021 .CANCELLED => return error.OperationCanceled,
3018 .NOT_A_REPARSE_POINT => return error.NotLink,3022 .NOT_A_REPARSE_POINT => return error.NotLink,
3019 else => return unexpectedStatus(rc),3023 else => return unexpectedStatus(rc),
3020 }3024 }
...@@ -3339,71 +3343,6 @@ pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {...@@ -3339,71 +3343,6 @@ pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {
3339 return handle;3343 return handle;
3340}3344}
33413345
3342pub const SetFilePointerError = error{
3343 Unseekable,
3344 Unexpected,
3345};
3346
3347/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
3348pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
3349 // "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
3350 // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
3351 // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
3352 const ipos = @as(LARGE_INTEGER, @bitCast(offset));
3353 if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
3354 switch (GetLastError()) {
3355 .INVALID_FUNCTION => return error.Unseekable,
3356 .NEGATIVE_SEEK => return error.Unseekable,
3357 .INVALID_PARAMETER => unreachable,
3358 .INVALID_HANDLE => unreachable,
3359 else => |err| return unexpectedError(err),
3360 }
3361 }
3362}
3363
3364/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
3365pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
3366 if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
3367 switch (GetLastError()) {
3368 .INVALID_FUNCTION => return error.Unseekable,
3369 .NEGATIVE_SEEK => return error.Unseekable,
3370 .INVALID_PARAMETER => unreachable,
3371 .INVALID_HANDLE => unreachable,
3372 else => |err| return unexpectedError(err),
3373 }
3374 }
3375}
3376
3377/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
3378pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
3379 if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
3380 switch (GetLastError()) {
3381 .INVALID_FUNCTION => return error.Unseekable,
3382 .NEGATIVE_SEEK => return error.Unseekable,
3383 .INVALID_PARAMETER => unreachable,
3384 .INVALID_HANDLE => unreachable,
3385 else => |err| return unexpectedError(err),
3386 }
3387 }
3388}
3389
3390/// The SetFilePointerEx function with parameters to get the current offset.
3391pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
3392 var result: LARGE_INTEGER = undefined;
3393 if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
3394 switch (GetLastError()) {
3395 .INVALID_FUNCTION => return error.Unseekable,
3396 .NEGATIVE_SEEK => return error.Unseekable,
3397 .INVALID_PARAMETER => unreachable,
3398 .INVALID_HANDLE => unreachable,
3399 else => |err| return unexpectedError(err),
3400 }
3401 }
3402 // Based on the docs for FILE_BEGIN, it seems that the returned signed integer
3403 // should be interpreted as an unsigned integer.
3404 return @as(u64, @bitCast(result));
3405}
3406
3407pub const QueryObjectNameError = error{3346pub const QueryObjectNameError = error{
3408 AccessDenied,3347 AccessDenied,
3409 InvalidHandle,3348 InvalidHandle,
...@@ -3562,6 +3501,7 @@ pub fn GetFinalPathNameByHandle(...@@ -3562,6 +3501,7 @@ pub fn GetFinalPathNameByHandle(
3562 error.NetworkNotFound => return error.Unexpected,3501 error.NetworkNotFound => return error.Unexpected,
3563 error.AntivirusInterference => return error.Unexpected,3502 error.AntivirusInterference => return error.Unexpected,
3564 error.BadPathName => return error.Unexpected,3503 error.BadPathName => return error.Unexpected,
3504 error.OperationCanceled => @panic("TODO: better integrate cancelation"),
3565 else => |e| return e,3505 else => |e| return e,
3566 };3506 };
3567 defer CloseHandle(mgmt_handle);3507 defer CloseHandle(mgmt_handle);
lib/std/os/windows/ntdll.zig+27
...@@ -554,3 +554,30 @@ pub extern "ntdll" fn RtlWakeConditionVariable(...@@ -554,3 +554,30 @@ pub extern "ntdll" fn RtlWakeConditionVariable(
554pub extern "ntdll" fn RtlWakeAllConditionVariable(554pub extern "ntdll" fn RtlWakeAllConditionVariable(
555 ConditionVariable: *CONDITION_VARIABLE,555 ConditionVariable: *CONDITION_VARIABLE,
556) callconv(.winapi) void;556) callconv(.winapi) void;
557
558pub extern "ntdll" fn NtWaitForAlertByThreadId(
559 Address: ?*const anyopaque,
560 Timeout: ?*const LARGE_INTEGER,
561) callconv(.winapi) NTSTATUS;
562pub extern "ntdll" fn NtAlertThreadByThreadId(
563 ThreadId: DWORD,
564) callconv(.winapi) NTSTATUS;
565pub extern "ntdll" fn NtAlertMultipleThreadByThreadId(
566 ThreadIds: [*]const ULONG_PTR,
567 ThreadCount: ULONG,
568 Unknown1: ?*const anyopaque,
569 Unknown2: ?*const anyopaque,
570) callconv(.winapi) NTSTATUS;
571
572pub extern "ntdll" fn NtOpenThread(
573 ThreadHandle: *HANDLE,
574 DesiredAccess: ACCESS_MASK,
575 ObjectAttributes: *const OBJECT_ATTRIBUTES,
576 ClientId: *const windows.CLIENT_ID,
577) callconv(.winapi) NTSTATUS;
578
579pub extern "ntdll" fn NtCancelSynchronousIoFile(
580 ThreadHandle: HANDLE,
581 RequestToCancel: ?*IO_STATUS_BLOCK,
582 IoStatusBlock: *IO_STATUS_BLOCK,
583) callconv(.winapi) NTSTATUS;
lib/std/posix.zig+1
...@@ -1124,6 +1124,7 @@ pub fn mkdirW(dir_path_w: []const u16, mode: mode_t) MakeDirError!void {...@@ -1124,6 +1124,7 @@ pub fn mkdirW(dir_path_w: []const u16, mode: mode_t) MakeDirError!void {
1124 error.NoDevice => return error.Unexpected,1124 error.NoDevice => return error.Unexpected,
1125 error.WouldBlock => return error.Unexpected,1125 error.WouldBlock => return error.Unexpected,
1126 error.AntivirusInterference => return error.Unexpected,1126 error.AntivirusInterference => return error.Unexpected,
1127 error.OperationCanceled => return error.Unexpected,
1127 else => |e| return e,1128 else => |e| return e,
1128 };1129 };
1129 windows.CloseHandle(sub_dir_handle);1130 windows.CloseHandle(sub_dir_handle);
lib/std/process/Child.zig+2-2
...@@ -778,6 +778,7 @@ fn spawnWindows(self: *Child, io: Io) SpawnError!void {...@@ -778,6 +778,7 @@ fn spawnWindows(self: *Child, io: Io) SpawnError!void {
778 error.WouldBlock => return error.Unexpected, // not possible for "NUL"778 error.WouldBlock => return error.Unexpected, // not possible for "NUL"
779 error.NetworkNotFound => return error.Unexpected, // not possible for "NUL"779 error.NetworkNotFound => return error.Unexpected, // not possible for "NUL"
780 error.AntivirusInterference => return error.Unexpected, // not possible for "NUL"780 error.AntivirusInterference => return error.Unexpected, // not possible for "NUL"
781 error.OperationCanceled => return error.Unexpected, // we're not canceling the operation
781 else => |e| return e,782 else => |e| return e,
782 }783 }
783 else784 else
...@@ -1129,8 +1130,7 @@ fn windowsCreateProcessPathExt(...@@ -1129,8 +1130,7 @@ fn windowsCreateProcessPathExt(
1129 defer dir_buf.shrinkRetainingCapacity(dir_path_len);1130 defer dir_buf.shrinkRetainingCapacity(dir_path_len);
1130 const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0];1131 const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
1131 const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z);1132 const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z);
1132 // TODO eliminate this reference1133 break :dir Io.Threaded.dirOpenDirWindows(.cwd(), prefixed_path.span(), .{
1133 break :dir Io.Threaded.global_single_threaded.dirOpenDirWindows(.cwd(), prefixed_path.span(), .{
1134 .iterate = true,1134 .iterate = true,
1135 }) catch return error.FileNotFound;1135 }) catch return error.FileNotFound;
1136 };1136 };
src/codegen/wasm/CodeGen.zig+13-2
...@@ -6973,9 +6973,20 @@ fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6973,9 +6973,20 @@ fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6973 return cg.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits});6973 return cg.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits});
6974 }6974 }
69756975
6976 const lhs = try cg.resolveInst(bin_op.lhs);
6977 const rhs = try cg.resolveInst(bin_op.rhs);
6978 const wasm_bits = toWasmBits(int_info.bits).?;6976 const wasm_bits = toWasmBits(int_info.bits).?;
6977
6978 const lhs = try cg.resolveInst(bin_op.lhs);
6979 const rhs = rhs: {
6980 const rhs = try cg.resolveInst(bin_op.rhs);
6981 const rhs_ty = cg.typeOf(bin_op.rhs);
6982 // The type of `rhs` is the log2 int of the type of `lhs`, but WASM wants the lhs and rhs types to match.
6983 if (toWasmBits(@intCast(rhs_ty.bitSize(zcu))).? == wasm_bits) {
6984 break :rhs rhs; // the WASM types match, so no cast necessary
6985 }
6986 const casted = try cg.intcast(rhs, rhs_ty, ty);
6987 break :rhs try casted.toLocal(cg, ty);
6988 };
6989
6979 const result = try cg.allocLocal(ty);6990 const result = try cg.allocLocal(ty);
69806991
6981 if (wasm_bits == int_info.bits) {6992 if (wasm_bits == int_info.bits) {
tools/incr-check.zig+51-43
...@@ -6,6 +6,27 @@ const Cache = std.Build.Cache;...@@ -6,6 +6,27 @@ const Cache = std.Build.Cache;
66
7const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-log foo] [--preserve-tmp] [--zig-cc-binary /path/to/zig]";7const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-log foo] [--preserve-tmp] [--zig-cc-binary /path/to/zig]";
88
9pub const std_options: std.Options = .{
10 .logFn = logImpl,
11};
12var log_cur_update: ?struct { *const Case.Target, *const Case.Update } = null;
13fn logImpl(
14 comptime level: std.log.Level,
15 comptime scope: @EnumLiteral(),
16 comptime format: []const u8,
17 args: anytype,
18) void {
19 const target, const update = log_cur_update orelse {
20 return std.log.defaultLog(level, scope, format, args);
21 };
22 std.log.defaultLog(
23 level,
24 scope,
25 "[{s}-{t} '{s}'] " ++ format,
26 .{ target.query, target.backend, update.name } ++ args,
27 );
28}
29
9pub fn main() !void {30pub fn main() !void {
10 const fatal = std.process.fatal;31 const fatal = std.process.fatal;
1132
...@@ -225,6 +246,9 @@ pub fn main() !void {...@@ -225,6 +246,9 @@ pub fn main() !void {
225 std.log.scoped(.status).info("update: '{s}'", .{update.name});246 std.log.scoped(.status).info("update: '{s}'", .{update.name});
226 }247 }
227248
249 log_cur_update = .{ &target, &update };
250 defer log_cur_update = null;
251
228 eval.write(update);252 eval.write(update);
229 try eval.requestUpdate();253 try eval.requestUpdate();
230 try eval.check(&poller, update, update_node);254 try eval.check(&poller, update, update_node);
...@@ -295,9 +319,9 @@ const Eval = struct {...@@ -295,9 +319,9 @@ const Eval = struct {
295 if (stderr.bufferedLen() > 0) {319 if (stderr.bufferedLen() > 0) {
296 const stderr_data = try poller.toOwnedSlice(.stderr);320 const stderr_data = try poller.toOwnedSlice(.stderr);
297 if (eval.allow_stderr) {321 if (eval.allow_stderr) {
298 std.log.info("error_bundle included stderr:\n{s}", .{stderr_data});322 std.log.info("error_bundle stderr:\n{s}", .{stderr_data});
299 } else {323 } else {
300 eval.fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data});324 eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr_data});
301 }325 }
302 }326 }
303 if (result_error_bundle.errorMessageCount() != 0) {327 if (result_error_bundle.errorMessageCount() != 0) {
...@@ -312,9 +336,9 @@ const Eval = struct {...@@ -312,9 +336,9 @@ const Eval = struct {
312 if (stderr.bufferedLen() > 0) {336 if (stderr.bufferedLen() > 0) {
313 const stderr_data = try poller.toOwnedSlice(.stderr);337 const stderr_data = try poller.toOwnedSlice(.stderr);
314 if (eval.allow_stderr) {338 if (eval.allow_stderr) {
315 std.log.info("emit_digest included stderr:\n{s}", .{stderr_data});339 std.log.info("emit_digest stderr:\n{s}", .{stderr_data});
316 } else {340 } else {
317 eval.fatal("emit_digest included unexpected stderr:\n{s}", .{stderr_data});341 eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr_data});
318 }342 }
319 }343 }
320344
...@@ -344,14 +368,14 @@ const Eval = struct {...@@ -344,14 +368,14 @@ const Eval = struct {
344368
345 if (stderr.bufferedLen() > 0) {369 if (stderr.bufferedLen() > 0) {
346 if (eval.allow_stderr) {370 if (eval.allow_stderr) {
347 std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr.buffered() });371 std.log.info("stderr:\n{s}", .{stderr.buffered()});
348 } else {372 } else {
349 eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr.buffered() });373 eval.fatal("unexpected stderr:\n{s}", .{stderr.buffered()});
350 }374 }
351 }375 }
352376
353 waitChild(eval.child, eval);377 waitChild(eval.child, eval);
354 eval.fatal("update '{s}': compiler failed to send error_bundle or emit_bin_path", .{update.name});378 eval.fatal("compiler failed to send error_bundle or emit_bin_path", .{});
355 }379 }
356380
357 fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void {381 fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void {
...@@ -361,7 +385,7 @@ const Eval = struct {...@@ -361,7 +385,7 @@ const Eval = struct {
361 .compile_errors => |ce| ce,385 .compile_errors => |ce| ce,
362 .stdout, .exit_code => {386 .stdout, .exit_code => {
363 try error_bundle.renderToStderr(io, .{}, .auto);387 try error_bundle.renderToStderr(io, .{}, .auto);
364 eval.fatal("update '{s}': unexpected compile errors", .{update.name});388 eval.fatal("unexpected compile errors", .{});
365 },389 },
366 };390 };
367391
...@@ -370,30 +394,29 @@ const Eval = struct {...@@ -370,30 +394,29 @@ const Eval = struct {
370 for (error_bundle.getMessages()) |err_idx| {394 for (error_bundle.getMessages()) |err_idx| {
371 if (expected_idx == expected.errors.len) {395 if (expected_idx == expected.errors.len) {
372 try error_bundle.renderToStderr(io, .{}, .auto);396 try error_bundle.renderToStderr(io, .{}, .auto);
373 eval.fatal("update '{s}': more errors than expected", .{update.name});397 eval.fatal("more errors than expected", .{});
374 }398 }
375 try eval.checkOneError(update, error_bundle, expected.errors[expected_idx], false, err_idx);399 try eval.checkOneError(error_bundle, expected.errors[expected_idx], false, err_idx);
376 expected_idx += 1;400 expected_idx += 1;
377401
378 for (error_bundle.getNotes(err_idx)) |note_idx| {402 for (error_bundle.getNotes(err_idx)) |note_idx| {
379 if (expected_idx == expected.errors.len) {403 if (expected_idx == expected.errors.len) {
380 try error_bundle.renderToStderr(io, .{}, .auto);404 try error_bundle.renderToStderr(io, .{}, .auto);
381 eval.fatal("update '{s}': more error notes than expected", .{update.name});405 eval.fatal("more error notes than expected", .{});
382 }406 }
383 try eval.checkOneError(update, error_bundle, expected.errors[expected_idx], true, note_idx);407 try eval.checkOneError(error_bundle, expected.errors[expected_idx], true, note_idx);
384 expected_idx += 1;408 expected_idx += 1;
385 }409 }
386 }410 }
387411
388 if (!std.mem.eql(u8, error_bundle.getCompileLogOutput(), expected.compile_log_output)) {412 if (!std.mem.eql(u8, error_bundle.getCompileLogOutput(), expected.compile_log_output)) {
389 try error_bundle.renderToStderr(io, .{}, .auto);413 try error_bundle.renderToStderr(io, .{}, .auto);
390 eval.fatal("update '{s}': unexpected compile log output", .{update.name});414 eval.fatal("unexpected compile log output", .{});
391 }415 }
392 }416 }
393417
394 fn checkOneError(418 fn checkOneError(
395 eval: *Eval,419 eval: *Eval,
396 update: Case.Update,
397 eb: std.zig.ErrorBundle,420 eb: std.zig.ErrorBundle,
398 expected: Case.ExpectedError,421 expected: Case.ExpectedError,
399 is_note: bool,422 is_note: bool,
...@@ -423,7 +446,7 @@ const Eval = struct {...@@ -423,7 +446,7 @@ const Eval = struct {
423 !std.mem.eql(u8, expected.msg, msg))446 !std.mem.eql(u8, expected.msg, msg))
424 {447 {
425 eb.renderToStderr(io, .{}, .auto) catch {};448 eb.renderToStderr(io, .{}, .auto) catch {};
426 eval.fatal("update '{s}': compile error did not match expected error", .{update.name});449 eval.fatal("compile error did not match expected error", .{});
427 }450 }
428 }451 }
429452
...@@ -444,7 +467,7 @@ const Eval = struct {...@@ -444,7 +467,7 @@ const Eval = struct {
444 .cbe => bin: {467 .cbe => bin: {
445 const rand_int = std.crypto.random.int(u64);468 const rand_int = std.crypto.random.int(u64);
446 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);469 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
447 try eval.buildCOutput(update, emitted_path, out_bin_name, prog_node);470 try eval.buildCOutput(emitted_path, out_bin_name, prog_node);
448 break :bin out_bin_name;471 break :bin out_bin_name;
449 },472 },
450 };473 };
...@@ -521,8 +544,7 @@ const Eval = struct {...@@ -521,8 +544,7 @@ const Eval = struct {
521 if (is_foreign) {544 if (is_foreign) {
522 // Chances are the foreign executor isn't available. Skip this evaluation.545 // Chances are the foreign executor isn't available. Skip this evaluation.
523 if (eval.allow_stderr) {546 if (eval.allow_stderr) {
524 std.log.warn("update '{s}': skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{547 std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{
525 update.name,
526 binary_path,548 binary_path,
527 try eval.target.resolved.zigTriple(eval.arena),549 try eval.target.resolved.zigTriple(eval.arena),
528 err,550 err,
...@@ -530,16 +552,14 @@ const Eval = struct {...@@ -530,16 +552,14 @@ const Eval = struct {
530 }552 }
531 return;553 return;
532 }554 }
533 eval.fatal("update '{s}': failed to run the generated executable '{s}': {t}", .{555 eval.fatal("failed to run the generated executable '{s}': {t}", .{ binary_path, err });
534 update.name, binary_path, err,
535 });
536 };556 };
537557
538 // Some executors (looking at you, Wine) like throwing some stderr in, just for fun.558 // Some executors (looking at you, Wine) like throwing some stderr in, just for fun.
539 // Therefore, we'll ignore stderr when using a foreign executor.559 // Therefore, we'll ignore stderr when using a foreign executor.
540 if (!is_foreign and result.stderr.len != 0) {560 if (!is_foreign and result.stderr.len != 0) {
541 std.log.err("update '{s}': generated executable '{s}' had unexpected stderr:\n{s}", .{561 std.log.err("generated executable '{s}' had unexpected stderr:\n{s}", .{
542 update.name, binary_path, result.stderr,562 binary_path, result.stderr,
543 });563 });
544 }564 }
545565
...@@ -548,18 +568,14 @@ const Eval = struct {...@@ -548,18 +568,14 @@ const Eval = struct {
548 .unknown, .compile_errors => unreachable,568 .unknown, .compile_errors => unreachable,
549 .stdout => |expected_stdout| {569 .stdout => |expected_stdout| {
550 if (code != 0) {570 if (code != 0) {
551 eval.fatal("update '{s}': generated executable '{s}' failed with code {d}", .{571 eval.fatal("generated executable '{s}' failed with code {d}", .{ binary_path, code });
552 update.name, binary_path, code,
553 });
554 }572 }
555 try std.testing.expectEqualStrings(expected_stdout, result.stdout);573 try std.testing.expectEqualStrings(expected_stdout, result.stdout);
556 },574 },
557 .exit_code => |expected_code| try std.testing.expectEqual(expected_code, result.term.Exited),575 .exit_code => |expected_code| try std.testing.expectEqual(expected_code, result.term.Exited),
558 },576 },
559 .Signal, .Stopped, .Unknown => {577 .Signal, .Stopped, .Unknown => {
560 eval.fatal("update '{s}': generated executable '{s}' terminated unexpectedly", .{578 eval.fatal("generated executable '{s}' terminated unexpectedly", .{binary_path});
561 update.name, binary_path,
562 });
563 },579 },
564 }580 }
565581
...@@ -597,7 +613,7 @@ const Eval = struct {...@@ -597,7 +613,7 @@ const Eval = struct {
597 }613 }
598 }614 }
599615
600 fn buildCOutput(eval: *Eval, update: Case.Update, c_path: []const u8, out_path: []const u8, prog_node: std.Progress.Node) !void {616 fn buildCOutput(eval: *Eval, c_path: []const u8, out_path: []const u8, prog_node: std.Progress.Node) !void {
601 std.debug.assert(eval.cc_child_args.items.len > 0);617 std.debug.assert(eval.cc_child_args.items.len > 0);
602618
603 const child_prog_node = prog_node.start("build cbe output", 0);619 const child_prog_node = prog_node.start("build cbe output", 0);
...@@ -612,28 +628,20 @@ const Eval = struct {...@@ -612,28 +628,20 @@ const Eval = struct {
612 .cwd = eval.tmp_dir_path,628 .cwd = eval.tmp_dir_path,
613 .progress_node = child_prog_node,629 .progress_node = child_prog_node,
614 }) catch |err| {630 }) catch |err| {
615 eval.fatal("update '{s}': failed to spawn zig cc for '{s}': {t}", .{ update.name, c_path, err });631 eval.fatal("failed to spawn zig cc for '{s}': {t}", .{ c_path, err });
616 };632 };
617 switch (result.term) {633 switch (result.term) {
618 .Exited => |code| if (code != 0) {634 .Exited => |code| if (code != 0) {
619 if (result.stderr.len != 0) {635 if (result.stderr.len != 0) {
620 std.log.err("update '{s}': zig cc stderr:\n{s}", .{636 std.log.err("zig cc stderr:\n{s}", .{result.stderr});
621 update.name, result.stderr,
622 });
623 }637 }
624 eval.fatal("update '{s}': zig cc for '{s}' failed with code {d}", .{638 eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code });
625 update.name, c_path, code,
626 });
627 },639 },
628 .Signal, .Stopped, .Unknown => {640 .Signal, .Stopped, .Unknown => {
629 if (result.stderr.len != 0) {641 if (result.stderr.len != 0) {
630 std.log.err("update '{s}': zig cc stderr:\n{s}", .{642 std.log.err("zig cc stderr:\n{s}", .{result.stderr});
631 update.name, result.stderr,
632 });
633 }643 }
634 eval.fatal("update '{s}': zig cc for '{s}' terminated unexpectedly", .{644 eval.fatal("zig cc for '{s}' terminated unexpectedly", .{c_path});
635 update.name, c_path,
636 });
637 },645 },
638 }646 }
639 }647 }