| ... | ... | @@ -29,23 +29,7 @@ join_requested: bool = false, |
| 29 | 29 | stack_size: usize, |
| 30 | 30 | /// All threads are spawned detached; this is how we wait until they all exit. |
| 31 | 31 | wait_group: std.Thread.WaitGroup = .{}, |
| 32 | | /// Maximum thread pool size (excluding main thread) when dispatching async |
| 33 | | /// tasks. Until this limit, calls to `Io.async` when all threads are busy will |
| 34 | | /// cause a new thread to be spawned and permanently added to the pool. After |
| 35 | | /// this limit, calls to `Io.async` when all threads are busy run the task |
| 36 | | /// immediately. |
| 37 | | /// |
| 38 | | /// Defaults to a number equal to logical CPU cores. |
| 39 | | /// |
| 40 | | /// Protected by `mutex` once the I/O instance is already in use. See |
| 41 | | /// `setAsyncLimit`. |
| 42 | 32 | async_limit: Io.Limit, |
| 43 | | /// Maximum thread pool size (excluding main thread) for dispatching concurrent |
| 44 | | /// tasks. Until this limit, calls to `Io.concurrent` will increase the thread |
| 45 | | /// pool size. |
| 46 | | /// |
| 47 | | /// concurrent tasks. After this number, calls to `Io.concurrent` return |
| 48 | | /// `error.ConcurrencyUnavailable`. |
| 49 | 33 | concurrent_limit: Io.Limit = .unlimited, |
| 50 | 34 | /// Error from calling `std.Thread.getCpuCount` in `init`. |
| 51 | 35 | cpu_count_error: ?std.Thread.CpuCountError, |
| ... | ... | @@ -55,17 +39,7 @@ cpu_count_error: ?std.Thread.CpuCountError, |
| 55 | 39 | busy_count: usize = 0, |
| 56 | 40 | main_thread: Thread, |
| 57 | 41 | pid: Pid = .unknown, |
| 58 | | /// When a cancel request is made, blocking syscalls can be unblocked by |
| 59 | | /// issuing a signal. However, if the signal arrives after the check and before |
| 60 | | /// the syscall instruction, it is missed. |
| 61 | | /// |
| 62 | | /// This option solves the race condition by retrying the signal delivery |
| 63 | | /// until it is acknowledged, with an exponential backoff. |
| 64 | | /// |
| 65 | | /// Unfortunately, trying again until the cancellation request is acknowledged |
| 66 | | /// has been observed to be relatively slow, and usually strong cancellation |
| 67 | | /// guarantees are not needed, so this defaults to off. |
| 68 | | robust_cancel: RobustCancel = .disabled, |
| 42 | robust_cancel: RobustCancel, |
| 69 | 43 | |
| 70 | 44 | wsa: if (is_windows) Wsa else struct {} = .{}, |
| 71 | 45 | |
| ... | ... | @@ -86,6 +60,32 @@ stderr_writer: File.Writer = .{ |
| 86 | 60 | stderr_mode: Io.Terminal.Mode = .no_color, |
| 87 | 61 | stderr_writer_initialized: bool = false, |
| 88 | 62 | |
| 63 | environ: Environ, |
| 64 | args: Args, |
| 65 | |
| 66 | pub const Environ = switch (native_os) { |
| 67 | .openbsd, .haiku => struct { |
| 68 | PATH: ?[]const u8, |
| 69 | |
| 70 | pub const empty: @This() = .{ |
| 71 | .PATH = null, |
| 72 | }; |
| 73 | }, |
| 74 | else => struct { |
| 75 | pub const empty: @This() = .{}; |
| 76 | }, |
| 77 | }; |
| 78 | |
| 79 | pub const Args = switch (native_os) { |
| 80 | .openbsd, .haiku => struct { |
| 81 | list: []const []const u8, |
| 82 | pub const empty: @This() = .{ .list = &.{} }; |
| 83 | }, |
| 84 | else => struct { |
| 85 | pub const empty: @This() = .{}; |
| 86 | }, |
| 87 | }; |
| 88 | |
| 89 | 89 | pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { |
| 90 | 90 | enabled, |
| 91 | 91 | disabled, |
| ... | ... | @@ -557,6 +557,54 @@ const Closure = struct { |
| 557 | 557 | } |
| 558 | 558 | }; |
| 559 | 559 | |
| 560 | pub const InitOptions = struct { |
| 561 | /// Affects how many bytes are memory-mapped for threads. |
| 562 | stack_size: usize = std.Thread.SpawnConfig.default_stack_size, |
| 563 | /// Maximum thread pool size (excluding main thread) when dispatching async |
| 564 | /// tasks. Until this limit, calls to `Io.async` when all threads are busy will |
| 565 | /// cause a new thread to be spawned and permanently added to the pool. After |
| 566 | /// this limit, calls to `Io.async` when all threads are busy run the task |
| 567 | /// immediately. |
| 568 | /// |
| 569 | /// Defaults to a number equal to logical CPU cores. |
| 570 | /// |
| 571 | /// Protected by `Threaded.mutex` once the I/O instance is already in use. See |
| 572 | /// `setAsyncLimit`. |
| 573 | async_limit: ?Io.Limit = null, |
| 574 | /// Maximum thread pool size (excluding main thread) for dispatching concurrent |
| 575 | /// tasks. Until this limit, calls to `Io.concurrent` will increase the thread |
| 576 | /// pool size. |
| 577 | /// |
| 578 | /// concurrent tasks. After this number, calls to `Io.concurrent` return |
| 579 | /// `error.ConcurrencyUnavailable`. |
| 580 | concurrent_limit: Io.Limit = .unlimited, |
| 581 | /// When a cancel request is made, blocking syscalls can be unblocked by |
| 582 | /// issuing a signal. However, if the signal arrives after the check and before |
| 583 | /// the syscall instruction, it is missed. |
| 584 | /// |
| 585 | /// This option solves the race condition by retrying the signal delivery |
| 586 | /// until it is acknowledged, with an exponential backoff. |
| 587 | /// |
| 588 | /// Unfortunately, trying again until the cancellation request is acknowledged |
| 589 | /// has been observed to be relatively slow, and usually strong cancellation |
| 590 | /// guarantees are not needed, so this defaults to off. |
| 591 | robust_cancel: RobustCancel = .disabled, |
| 592 | /// Affects the following operations: |
| 593 | /// * `processExecutablePath` on OpenBSD and Haiku. |
| 594 | /// |
| 595 | /// The default value causes this to be a compile error on systems that need to |
| 596 | /// initialize this field. `Environ.empty` can be used to omit this field on |
| 597 | /// all targets. |
| 598 | environ: Environ = .{}, |
| 599 | /// Affects the following operations: |
| 600 | /// * `processExecutablePath` on OpenBSD and Haiku. |
| 601 | /// |
| 602 | /// The default value causes this to be a compile error on systems that need to |
| 603 | /// initialize this field. `Args.empty` can be used to omit this field on all |
| 604 | /// targets. |
| 605 | args: Args = .{}, |
| 606 | }; |
| 607 | |
| 560 | 608 | /// Related: |
| 561 | 609 | /// * `init_single_threaded` |
| 562 | 610 | pub fn init( |
| ... | ... | @@ -568,6 +616,7 @@ pub fn init( |
| 568 | 616 | /// If these functions are avoided, then `Allocator.failing` may be passed |
| 569 | 617 | /// here. |
| 570 | 618 | gpa: Allocator, |
| 619 | options: InitOptions, |
| 571 | 620 | ) Threaded { |
| 572 | 621 | if (builtin.single_threaded) return .init_single_threaded; |
| 573 | 622 | |
| ... | ... | @@ -575,8 +624,9 @@ pub fn init( |
| 575 | 624 | |
| 576 | 625 | var t: Threaded = .{ |
| 577 | 626 | .allocator = gpa, |
| 578 | | .stack_size = std.Thread.SpawnConfig.default_stack_size, |
| 579 | | .async_limit = if (cpu_count) |n| .limited(n - 1) else |_| .nothing, |
| 627 | .stack_size = options.stack_size, |
| 628 | .async_limit = options.async_limit orelse if (cpu_count) |n| .limited(n - 1) else |_| .nothing, |
| 629 | .concurrent_limit = options.concurrent_limit, |
| 580 | 630 | .cpu_count_error = if (cpu_count) |_| null else |e| e, |
| 581 | 631 | .old_sig_io = undefined, |
| 582 | 632 | .old_sig_pipe = undefined, |
| ... | ... | @@ -586,6 +636,9 @@ pub fn init( |
| 586 | 636 | .current_closure = null, |
| 587 | 637 | .cancel_protection = undefined, |
| 588 | 638 | }, |
| 639 | .environ = options.environ, |
| 640 | .args = options.args, |
| 641 | .robust_cancel = options.robust_cancel, |
| 589 | 642 | }; |
| 590 | 643 | |
| 591 | 644 | if (posix.Sigaction != void) { |
| ... | ... | @@ -624,6 +677,9 @@ pub const init_single_threaded: Threaded = .{ |
| 624 | 677 | .current_closure = null, |
| 625 | 678 | .cancel_protection = undefined, |
| 626 | 679 | }, |
| 680 | .robust_cancel = .disabled, |
| 681 | .environ = .empty, |
| 682 | .args = .empty, |
| 627 | 683 | }; |
| 628 | 684 | |
| 629 | 685 | var global_single_threaded_instance: Threaded = .init_single_threaded; |
| ... | ... | @@ -7186,18 +7242,17 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex |
| 7186 | 7242 | } |
| 7187 | 7243 | }, |
| 7188 | 7244 | .openbsd, .haiku => { |
| 7189 | | // OpenBSD doesn't support getting the path of a running process, so try to guess it |
| 7190 | | if (std.os.argv.len == 0) |
| 7191 | | return error.FileNotFound; |
| 7192 | | |
| 7193 | | const argv0 = std.mem.span(std.os.argv[0]); |
| 7245 | // The best we can do on these operating systems is check based on CLI args. |
| 7246 | const argv = t.args.list; |
| 7247 | if (argv.len == 0) return error.OperationUnsupported; |
| 7248 | const argv0 = argv[0]; |
| 7194 | 7249 | if (std.mem.findScalar(u8, argv0, '/') != null) { |
| 7195 | 7250 | // argv[0] is a path (relative or absolute): use realpath(3) directly |
| 7196 | 7251 | const current_thread = Thread.getCurrent(t); |
| 7197 | 7252 | var resolved_buf: [std.c.PATH_MAX]u8 = undefined; |
| 7198 | 7253 | try current_thread.beginSyscall(); |
| 7199 | 7254 | while (true) { |
| 7200 | | if (std.c.realpath(std.os.argv[0], &resolved_buf)) |p| { |
| 7255 | if (std.c.realpath(argv[0], &resolved_buf)) |p| { |
| 7201 | 7256 | assert(p == &resolved_buf); |
| 7202 | 7257 | break current_thread.endSyscall(); |
| 7203 | 7258 | } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) { |
| ... | ... | @@ -7229,12 +7284,12 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex |
| 7229 | 7284 | } else if (argv0.len != 0) { |
| 7230 | 7285 | // argv[0] is not empty (and not a path): search PATH |
| 7231 | 7286 | const current_thread = Thread.getCurrent(t); |
| 7232 | | const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound; |
| 7287 | const PATH = t.environ.PATH orelse return error.FileNotFound; |
| 7233 | 7288 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); |
| 7234 | 7289 | it: while (it.next()) |dir| { |
| 7235 | 7290 | var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined; |
| 7236 | 7291 | const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{ |
| 7237 | | dir, std.os.argv[0], |
| 7292 | dir, argv[0], |
| 7238 | 7293 | }, 0) catch continue; |
| 7239 | 7294 | |
| 7240 | 7295 | var resolved_buf: [std.c.PATH_MAX]u8 = undefined; |