| 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 | 2160 | |
| 2161 | 2161 | /// For doing application-level writes to the standard error stream. |
| 2162 | 2162 | /// Coordinates also with debug-level writes that are ignorant of Io interface |
| 2163 | /// and implementations. When this returns, `std.process.stderr_thread_mutex` | |
| 2164 | /// will be locked. | |
| 2163 | /// and implementations. | |
| 2165 | 2164 | /// |
| 2166 | 2165 | /// See also: |
| 2167 | 2166 | /// * `tryLockStderr` |
lib/std/Io/IoUring.zig+1-1| ... | ... | @@ -10,7 +10,7 @@ const IoUring = std.os.linux.IoUring; |
| 10 | 10 | |
| 11 | 11 | /// Must be a thread-safe allocator. |
| 12 | 12 | gpa: Allocator, |
| 13 | mutex: std.Thread.Mutex, | |
| 13 | mutex: Io.Mutex, | |
| 14 | 14 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), |
| 15 | 15 | threads: Thread.List, |
| 16 | 16 |
lib/std/Io/Kqueue.zig+1-1| ... | ... | @@ -15,7 +15,7 @@ const posix = std.posix; |
| 15 | 15 | |
| 16 | 16 | /// Must be a thread-safe allocator. |
| 17 | 17 | gpa: Allocator, |
| 18 | mutex: std.Thread.Mutex, | |
| 18 | mutex: Io.Mutex, | |
| 19 | 19 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), |
| 20 | 20 | threads: Thread.List, |
| 21 | 21 |
lib/std/Io/Threaded.zig+34-5| ... | ... | @@ -67,6 +67,9 @@ stderr_writer: File.Writer = .{ |
| 67 | 67 | }, |
| 68 | 68 | stderr_mode: Io.Terminal.Mode = .no_color, |
| 69 | 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 | 74 | argv0: Argv0, |
| 72 | 75 | environ: Environ, |
| ... | ... | @@ -689,6 +692,13 @@ const Thread = struct { |
| 689 | 692 | |
| 690 | 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 | 702 | /// The thread is neither in a syscall nor entering one, but we want to check for cancelation |
| 693 | 703 | /// anyway. If there is a pending cancel request, acknowledge it and return `error.Canceled`. |
| 694 | 704 | fn checkCancel() Io.Cancelable!void { |
| ... | ... | @@ -13502,15 +13512,29 @@ fn netLookupFallible( |
| 13502 | 13512 | |
| 13503 | 13513 | fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { |
| 13504 | 13514 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 13505 | // Only global mutex since this is Threaded. | |
| 13506 | process.stderr_thread_mutex.lock(); | |
| 13515 | const current_thread_id = Thread.currentId(); | |
| 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 | 13524 | return initLockedStderr(t, terminal_mode); |
| 13508 | 13525 | } |
| 13509 | 13526 | |
| 13510 | 13527 | fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr { |
| 13511 | 13528 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 13512 | // Only global mutex since this is Threaded. | |
| 13513 | if (!process.stderr_thread_mutex.tryLock()) return null; | |
| 13529 | const current_thread_id = Thread.currentId(); | |
| 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 | 13538 | return try initLockedStderr(t, terminal_mode); |
| 13515 | 13539 | } |
| 13516 | 13540 | |
| ... | ... | @@ -13541,7 +13565,12 @@ fn unlockStderr(userdata: ?*anyopaque) void { |
| 13541 | 13565 | }; |
| 13542 | 13566 | t.stderr_writer.interface.end = 0; |
| 13543 | 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 | 13576 | fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize { |
lib/std/Thread.zig+2-12| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | //! This struct represents a kernel thread, and acts as a namespace for | |
| 2 | //! concurrency primitives that operate on kernel threads. For concurrency | |
| 3 | //! primitives that interact with the I/O interface, see `std.Io`. | |
| 1 | //! This struct represents a kernel thread. | |
| 2 | const Thread = @This(); | |
| 4 | 3 | |
| 5 | 4 | const builtin = @import("builtin"); |
| 6 | 5 | const target = builtin.target; |
| ... | ... | @@ -14,13 +13,8 @@ const posix = std.posix; |
| 14 | 13 | const windows = std.os.windows; |
| 15 | 14 | const testing = std.testing; |
| 16 | 15 | |
| 17 | pub const Mutex = struct { | |
| 18 | pub const Recursive = @import("Thread/Mutex/Recursive.zig"); | |
| 19 | }; | |
| 20 | ||
| 21 | 16 | pub const use_pthreads = native_os != .windows and native_os != .wasi and builtin.link_libc; |
| 22 | 17 | |
| 23 | const Thread = @This(); | |
| 24 | 18 | const Impl = if (native_os == .windows) |
| 25 | 19 | WindowsThreadImpl |
| 26 | 20 | else if (use_pthreads) |
| ... | ... | @@ -1604,10 +1598,6 @@ test "setName, getName" { |
| 1604 | 1598 | thread.join(); |
| 1605 | 1599 | } |
| 1606 | 1600 | |
| 1607 | test { | |
| 1608 | _ = Mutex; | |
| 1609 | } | |
| 1610 | ||
| 1611 | 1601 | fn testIncrementNotify(io: Io, value: *usize, event: *Io.Event) void { |
| 1612 | 1602 | value.* += 1; |
| 1613 | 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 | 20 | pub const Environ = @import("process/Environ.zig"); |
| 21 | 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 | 23 | /// A standard set of pre-initialized useful APIs for programs to take |
| 31 | 24 | /// advantage of. This is the type of the first parameter of the main function. |
| 32 | 25 | /// Applications wanting more flexibility can accept `Init.Minimal` instead. |