What is the use and meaning of the nosuspend keyword?
Should I consider doing clearWrittenWithEscapeCodes() of std.Progress right after the lock as does the std.debug.lockStdErr()?
Why zig libraries call it lockStdErr(), e.g. in std.debug or std.Progress, when it seems there is nothing to do with stderr and it is a rather generic std.Thread.Mutex.lock()?
Everyone that is trying to access the stream need to compete for the same lock, otherwise the lock is meaningless. If only your code is using the stream, and you consistly use the same lock, than it’s fine. But the standard library doesn’t know about your custom lock, so you can’t use the std’s logging function concurrently with your own code.
Any mutation to the stream needs to be protected by a lock. If you’re asking which function to call to clear the stream, I don’t know.
is to be sure that if I’m going to use std.Progress as part of my process, I won’t get a race condition? (ie. I don’t write to stderr at the same time as Progress when it is active in another thread.)
Yes. Well, the point of any lock is to prevent race conditions, but std.Progress.lockStdErr just locks the normal lock that whole library is using, so you’re preventing a race condition with the whole std library, not just std.Progress.
pub fn getStderrMutex() *std.Thread.Mutex {
@compileError("deprecated. call std.debug.lockStdErr() and std.debug.unlockStdErr() instead which will integrate properly with std.Progress");
}
If you look at my original post, I pointed out the place in the log which you’re probably referring to. However, I’m interested exactly in the mutex address and if it isn’t available (ie. intentionally exposed), I wonder why…
Yup, I glossed over that line when I read your post - I see that now. We are talking about the same thing.
From a design standpoint, I can understand why they would choose to not expose the lock itself. One potential reason is actually somewhat related to proper uses of getters/setters. They can change the implementation and details of the lock (or locking procedure) in future versions without people depending on the exact type of lock itself. They probably have other reasons, but if you’re looking for at least one plausible reason then I’d say that’s a possibility.
Wasn’t there a flag to force single-threaded mode, maybe it would then just default to do nothing? But that is just an idea, haven’t checked.
I was thinking of Mutex - SingleThreadedImpl
So because that already has a SingleThreadedImpl you still could use the mutex without it causing overhead, so not really a reason to avoid mutex / make it possible to switch implementations.
But it could still be useful if future versions need to lock other things.
Ok, thank you guys. Your answers helped a lot. Indeed, as per zig 0.13.0, the stderr_mutex was defined as:
var stderr_mutex: std.Thread.Mutex = .{};
and as for now, it is std.Thread.Mutex.Recursive. So the implementation indeed has changed. Also, using defer unlockStdErr() instead of just running it at the end of the fn is a type of mistake that led me to a deadlock, as I exited somewhere before reaching the end of fn.