| author | |
| committer | |
| log | d45f9aca14bd36a710293b3d0092039fd91a571b |
| tree | ea084f371d318a53665e10f1d8b0decde5b7ce29 |
| parent | d1e01e94311ce4423f65d0d102d169ed59f1ca9d |
Replaced by the lockStderr functions of std.Io. Trying to make
`std.process.stderr_thread_mutex` be a bridge across different Io
implementations didn't work in practice.7 files changed, 39 insertions(+), 100 deletions(-)
lib/std/Io.zig+1-2| ... | @@ -2160,8 +2160,7 @@ pub const LockedStderr = struct { | ... | @@ -2160,8 +2160,7 @@ pub const LockedStderr = struct { |
| 2160 | 2160 | ||
| 2161 | /// For doing application-level writes to the standard error stream. | 2161 | /// For doing application-level writes to the standard error stream. |
| 2162 | /// Coordinates also with debug-level writes that are ignorant of Io interface | 2162 | /// Coordinates also with debug-level writes that are ignorant of Io interface |
| 2163 | /// and implementations. When this returns, `std.process.stderr_thread_mutex` | 2163 | /// and implementations. |
| 2164 | /// will be locked. | ||
| 2165 | /// | 2164 | /// |
| 2166 | /// See also: | 2165 | /// See also: |
| 2167 | /// * `tryLockStderr` | 2166 | /// * `tryLockStderr` |
lib/std/Io/IoUring.zig+1-1| ... | @@ -10,7 +10,7 @@ const IoUring = std.os.linux.IoUring; | ... | @@ -10,7 +10,7 @@ const IoUring = std.os.linux.IoUring; |
| 10 | 10 | ||
| 11 | /// Must be a thread-safe allocator. | 11 | /// Must be a thread-safe allocator. |
| 12 | gpa: Allocator, | 12 | gpa: Allocator, |
| 13 | mutex: std.Thread.Mutex, | 13 | mutex: Io.Mutex, |
| 14 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), | 14 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), |
| 15 | threads: Thread.List, | 15 | threads: Thread.List, |
| 16 | 16 |
lib/std/Io/Kqueue.zig+1-1| ... | @@ -15,7 +15,7 @@ const posix = std.posix; | ... | @@ -15,7 +15,7 @@ const posix = std.posix; |
| 15 | 15 | ||
| 16 | /// Must be a thread-safe allocator. | 16 | /// Must be a thread-safe allocator. |
| 17 | gpa: Allocator, | 17 | gpa: Allocator, |
| 18 | mutex: std.Thread.Mutex, | 18 | mutex: Io.Mutex, |
| 19 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), | 19 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), |
| 20 | threads: Thread.List, | 20 | threads: Thread.List, |
| 21 | 21 |
lib/std/Io/Threaded.zig+34-5| ... | @@ -67,6 +67,9 @@ stderr_writer: File.Writer = .{ | ... | @@ -67,6 +67,9 @@ stderr_writer: File.Writer = .{ |
| 67 | }, | 67 | }, |
| 68 | stderr_mode: Io.Terminal.Mode = .no_color, | 68 | stderr_mode: Io.Terminal.Mode = .no_color, |
| 69 | stderr_writer_initialized: bool = false, | 69 | stderr_writer_initialized: bool = false, |
| 70 | stderr_mutex: Io.Mutex = .init, | ||
| 71 | stderr_mutex_locker: std.Thread.Id = Thread.invalid_id, | ||
| 72 | stderr_mutex_lock_count: usize = 0, | ||
| 70 | 73 | ||
| 71 | argv0: Argv0, | 74 | argv0: Argv0, |
| 72 | environ: Environ, | 75 | environ: Environ, |
| ... | @@ -689,6 +692,13 @@ const Thread = struct { | ... | @@ -689,6 +692,13 @@ const Thread = struct { |
| 689 | 692 | ||
| 690 | threadlocal var current: ?*Thread = null; | 693 | threadlocal var current: ?*Thread = null; |
| 691 | 694 | ||
| 695 | /// A value that does not alias any other thread id. | ||
| 696 | const invalid_id: std.Thread.Id = std.math.maxInt(std.Thread.Id); | ||
| 697 | |||
| 698 | fn currentId() std.Thread.Id { | ||
| 699 | return if (current) |t| t.id else std.Thread.getCurrentId(); | ||
| 700 | } | ||
| 701 | |||
| 692 | /// The thread is neither in a syscall nor entering one, but we want to check for cancelation | 702 | /// The thread is neither in a syscall nor entering one, but we want to check for cancelation |
| 693 | /// anyway. If there is a pending cancel request, acknowledge it and return `error.Canceled`. | 703 | /// anyway. If there is a pending cancel request, acknowledge it and return `error.Canceled`. |
| 694 | fn checkCancel() Io.Cancelable!void { | 704 | fn checkCancel() Io.Cancelable!void { |
| ... | @@ -13502,15 +13512,29 @@ fn netLookupFallible( | ... | @@ -13502,15 +13512,29 @@ fn netLookupFallible( |
| 13502 | 13512 | ||
| 13503 | fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { | 13513 | fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { |
| 13504 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 13514 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 13505 | // Only global mutex since this is Threaded. | 13515 | const current_thread_id = Thread.currentId(); |
| 13506 | process.stderr_thread_mutex.lock(); | 13516 | |
| 13517 | if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) { | ||
| 13518 | mutexLock(&t.stderr_mutex); | ||
| 13519 | assert(t.stderr_mutex_lock_count == 0); | ||
| 13520 | @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered); | ||
| 13521 | } | ||
| 13522 | t.stderr_mutex_lock_count += 1; | ||
| 13523 | |||
| 13507 | return initLockedStderr(t, terminal_mode); | 13524 | return initLockedStderr(t, terminal_mode); |
| 13508 | } | 13525 | } |
| 13509 | 13526 | ||
| 13510 | fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr { | 13527 | fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr { |
| 13511 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 13528 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 13512 | // Only global mutex since this is Threaded. | 13529 | const current_thread_id = Thread.currentId(); |
| 13513 | if (!process.stderr_thread_mutex.tryLock()) return null; | 13530 | |
| 13531 | if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) { | ||
| 13532 | if (!t.stderr_mutex.tryLock()) return null; | ||
| 13533 | assert(t.stderr_mutex_lock_count == 0); | ||
| 13534 | @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered); | ||
| 13535 | } | ||
| 13536 | t.stderr_mutex_lock_count += 1; | ||
| 13537 | |||
| 13514 | return try initLockedStderr(t, terminal_mode); | 13538 | return try initLockedStderr(t, terminal_mode); |
| 13515 | } | 13539 | } |
| 13516 | 13540 | ||
| ... | @@ -13541,7 +13565,12 @@ fn unlockStderr(userdata: ?*anyopaque) void { | ... | @@ -13541,7 +13565,12 @@ fn unlockStderr(userdata: ?*anyopaque) void { |
| 13541 | }; | 13565 | }; |
| 13542 | t.stderr_writer.interface.end = 0; | 13566 | t.stderr_writer.interface.end = 0; |
| 13543 | t.stderr_writer.interface.buffer = &.{}; | 13567 | t.stderr_writer.interface.buffer = &.{}; |
| 13544 | process.stderr_thread_mutex.unlock(); | 13568 | |
| 13569 | t.stderr_mutex_lock_count -= 1; | ||
| 13570 | if (t.stderr_mutex_lock_count == 0) { | ||
| 13571 | @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, Thread.invalid_id, .unordered); | ||
| 13572 | mutexUnlock(&t.stderr_mutex); | ||
| 13573 | } | ||
| 13545 | } | 13574 | } |
| 13546 | 13575 | ||
| 13547 | fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize { | 13576 | fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize { |
lib/std/Thread.zig+2-12| ... | @@ -1,6 +1,5 @@ | ... | @@ -1,6 +1,5 @@ |
| 1 | //! This struct represents a kernel thread, and acts as a namespace for | 1 | //! This struct represents a kernel thread. |
| 2 | //! concurrency primitives that operate on kernel threads. For concurrency | 2 | const Thread = @This(); |
| 3 | //! primitives that interact with the I/O interface, see `std.Io`. | ||
| 4 | 3 | ||
| 5 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 6 | const target = builtin.target; | 5 | const target = builtin.target; |
| ... | @@ -14,13 +13,8 @@ const posix = std.posix; | ... | @@ -14,13 +13,8 @@ const posix = std.posix; |
| 14 | const windows = std.os.windows; | 13 | const windows = std.os.windows; |
| 15 | const testing = std.testing; | 14 | const testing = std.testing; |
| 16 | 15 | ||
| 17 | pub const Mutex = struct { | ||
| 18 | pub const Recursive = @import("Thread/Mutex/Recursive.zig"); | ||
| 19 | }; | ||
| 20 | |||
| 21 | pub const use_pthreads = native_os != .windows and native_os != .wasi and builtin.link_libc; | 16 | pub const use_pthreads = native_os != .windows and native_os != .wasi and builtin.link_libc; |
| 22 | 17 | ||
| 23 | const Thread = @This(); | ||
| 24 | const Impl = if (native_os == .windows) | 18 | const Impl = if (native_os == .windows) |
| 25 | WindowsThreadImpl | 19 | WindowsThreadImpl |
| 26 | else if (use_pthreads) | 20 | else if (use_pthreads) |
| ... | @@ -1604,10 +1598,6 @@ test "setName, getName" { | ... | @@ -1604,10 +1598,6 @@ test "setName, getName" { |
| 1604 | thread.join(); | 1598 | thread.join(); |
| 1605 | } | 1599 | } |
| 1606 | 1600 | ||
| 1607 | test { | ||
| 1608 | _ = Mutex; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | fn testIncrementNotify(io: Io, value: *usize, event: *Io.Event) void { | 1601 | fn testIncrementNotify(io: Io, value: *usize, event: *Io.Event) void { |
| 1612 | value.* += 1; | 1602 | value.* += 1; |
| 1613 | event.set(io); | 1603 | event.set(io); |
lib/std/Thread/Mutex/Recursive.zig deleted-72| ... | @@ -1,72 +0,0 @@ | ||
| 1 | //! A synchronization primitive enforcing atomic access to a shared region of | ||
| 2 | //! code known as the "critical section". | ||
| 3 | //! | ||
| 4 | //! Equivalent to `std.Mutex` except it allows the same thread to obtain the | ||
| 5 | //! lock multiple times. | ||
| 6 | //! | ||
| 7 | //! A recursive mutex is an abstraction layer on top of a regular mutex; | ||
| 8 | //! therefore it is recommended to use instead `std.Mutex` unless there is a | ||
| 9 | //! specific reason a recursive mutex is warranted. | ||
| 10 | const Recursive = @This(); | ||
| 11 | |||
| 12 | const std = @import("../../std.zig"); | ||
| 13 | const Io = std.Io; | ||
| 14 | const assert = std.debug.assert; | ||
| 15 | |||
| 16 | mutex: Io.Mutex, | ||
| 17 | thread_id: std.Thread.Id, | ||
| 18 | lock_count: usize, | ||
| 19 | |||
| 20 | pub const init: Recursive = .{ | ||
| 21 | .mutex = .init, | ||
| 22 | .thread_id = invalid_thread_id, | ||
| 23 | .lock_count = 0, | ||
| 24 | }; | ||
| 25 | |||
| 26 | /// Acquires the `Mutex` without blocking the caller's thread. | ||
| 27 | /// | ||
| 28 | /// Returns `false` if the calling thread would have to block to acquire it. | ||
| 29 | /// | ||
| 30 | /// Otherwise, returns `true` and the caller should `unlock()` the Mutex to release it. | ||
| 31 | pub fn tryLock(r: *Recursive) bool { | ||
| 32 | const current_thread_id = std.Thread.getCurrentId(); | ||
| 33 | if (@atomicLoad(std.Thread.Id, &r.thread_id, .unordered) != current_thread_id) { | ||
| 34 | if (!r.mutex.tryLock()) return false; | ||
| 35 | assert(r.lock_count == 0); | ||
| 36 | @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .unordered); | ||
| 37 | } | ||
| 38 | r.lock_count += 1; | ||
| 39 | return true; | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Acquires the `Mutex`, blocking the current thread while the mutex is | ||
| 43 | /// already held by another thread. | ||
| 44 | /// | ||
| 45 | /// The `Mutex` can be held multiple times by the same thread. | ||
| 46 | /// | ||
| 47 | /// Once acquired, call `unlock` on the `Mutex` to release it, regardless | ||
| 48 | /// of whether the lock was already held by the same thread. | ||
| 49 | pub fn lock(r: *Recursive) void { | ||
| 50 | const current_thread_id = std.Thread.getCurrentId(); | ||
| 51 | if (@atomicLoad(std.Thread.Id, &r.thread_id, .unordered) != current_thread_id) { | ||
| 52 | Io.Threaded.mutexLock(&r.mutex); | ||
| 53 | assert(r.lock_count == 0); | ||
| 54 | @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .unordered); | ||
| 55 | } | ||
| 56 | r.lock_count += 1; | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Releases the `Mutex` which was previously acquired with `lock` or `tryLock`. | ||
| 60 | /// | ||
| 61 | /// It is undefined behavior to unlock from a different thread that it was | ||
| 62 | /// locked from. | ||
| 63 | pub fn unlock(r: *Recursive) void { | ||
| 64 | r.lock_count -= 1; | ||
| 65 | if (r.lock_count == 0) { | ||
| 66 | @atomicStore(std.Thread.Id, &r.thread_id, invalid_thread_id, .unordered); | ||
| 67 | Io.Threaded.mutexUnlock(&r.mutex); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /// A value that does not alias any other thread id. | ||
| 72 | const invalid_thread_id: std.Thread.Id = std.math.maxInt(std.Thread.Id); | ||
lib/std/process.zig-7| ... | @@ -20,13 +20,6 @@ pub const Args = @import("process/Args.zig"); | ... | @@ -20,13 +20,6 @@ pub const Args = @import("process/Args.zig"); |
| 20 | pub const Environ = @import("process/Environ.zig"); | 20 | pub const Environ = @import("process/Environ.zig"); |
| 21 | pub const Preopens = @import("process/Preopens.zig"); | 21 | pub const Preopens = @import("process/Preopens.zig"); |
| 22 | 22 | ||
| 23 | /// This is the global, process-wide protection to coordinate stderr writes. | ||
| 24 | /// | ||
| 25 | /// The primary motivation for recursive mutex here is so that a panic while | ||
| 26 | /// stderr mutex is held still dumps the stack trace and other debug | ||
| 27 | /// information. | ||
| 28 | pub var stderr_thread_mutex: std.Thread.Mutex.Recursive = .init; | ||
| 29 | |||
| 30 | /// A standard set of pre-initialized useful APIs for programs to take | 23 | /// A standard set of pre-initialized useful APIs for programs to take |
| 31 | /// advantage of. This is the type of the first parameter of the main function. | 24 | /// advantage of. This is the type of the first parameter of the main function. |
| 32 | /// Applications wanting more flexibility can accept `Init.Minimal` instead. | 25 | /// Applications wanting more flexibility can accept `Init.Minimal` instead. |