Live data from Hacker News

Inside Rust's std and parking_lot mutexes – who wins?

blog.cuongle.dev

101–110 of 134 posts

Re: Inside Rust's std and parking_lot mutexes – who wins?

#101

Earlier quoted context omitted.

On a typical modern 64-bit Linux for example they're 40 bytes ie they are 320 bits. So yeah, unnecessarily bulky. On my Linux system today Rust's Mutex > is smaller than the pthread mutex type whether it is locked and has the text "pthread_mutex_t is awful" inside it or maybe unlocked with explicitly no text (not an empty string), either would only take like 30-odd bytes, the pthread_mutex_t is 40 bytes. On Windows t…

> On Windows the discrepancy is even bigger, their OS native mutex type is this sprawling 80 byte monster I guess you are referring to CRITICAL_SECTION? SRWLock, which has the size of a pointer, has been introduced in Windows Vista. Since Windows 8 you can use WaitOnAddress to build even smaller locks.

Yes, CRITICAL_SECTION is far too large. Mara asked some years ago whether SRWLock could guarantee what Rust actually needs for this purpose (the documentation at that time refused to clarify whether we can move it for example) and that's why her change was to SRWLock from CRITICAL_SECTION.

And yes, the newer change uses WaitOnAddress to provide the same API as the futex from the various Unix platforms. Raymond Chen's description of the differences is perhaps rather exaggerated, which isn't to say there's no difference, but it's well within what's practical for an adaptor layer.

Also although the SRWLock itself is the same size as a pointer (thus, 64 bits on a modern computer, compared to a 32-bit Futex) there's a reason it's the same size as a pointer - it actually is basically a pointer, and so in some cases it's pointing at a data structure which we should reasonably say is also part of the overhead.

The pointer is to a large aligned object, which means the bottom bits would be zero and so SRWLock uses those for flag bits. It's a nice trick but we should remember that it isn't really comparable to a Futex though it's certainly cheaper than CRITICAL_SECTION.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#102

Earlier quoted context omitted.

Vague sketch: imagine if we had scoped panic hooks, unhooked via RAII. So, for use cases that today use unwinding for cleanup (e.g. "switch the terminal back out of curses mode"), you do that cleanup in a panic hook instead. The hard use case to handle without unwinding is an HTTP server that wants to allow for panics in a request handler without panicking the entire process. Unwinding is a janky way to handle that,…

I am not sure that I understand what scoped panic hooks would or might look like. Are they maybe similar to something like try-catch-finally in Java? Would the language force the programmer to include them in certain cases somehow? If a request handler for example has at some point in time 7 nested calls, in call no. 2 and call no. 6 have resources and partial computation that needs clean-up somehow and somewhere, an…

For the simple case, suppose that you're writing a TUI application that takes over the terminal. When it exits, even by panic, you want to clean up the terminal state so the user doesn't have to blindly type "reset".

Today, people sometimes do that by using `panic = "unwind"`, and writing a `catch_unwind` around their program, and using that to essentially implement a "finally" block. Or, they do it by having an RAII type that cleans up on `Drop`, and then they count on unwinding to ensure their `Drop` gets called even on panic. (That leaves aside the issue that something called from `Drop` is not allowed to fail or panic itself.) The question is, how would you do that without unwinding?

We have a panic hook mechanism, where on panic the standard library will call a user-supplied function. However, there is only one panic hook; if you set it, it replaces the old hook. If you have only one cleanup to do, that works fine. For more than one, you can follow the semantic of having your panic hook call the previous hook, but that does not allow unregistering hooks out of order; it only really works if you register a panic hook once for the whole program and never unregister it (e.g. "here's the hook for cleaning up tracing", "here's the hook for cleaning up the terminal state").

Suppose, instead, we had a mechanism that allowed registering arbitrary panic hooks, and unregistering them when no longer needed, in any order. Then, we could do RAII-style resource handling: you could have a `CursesTerminal` type, which is responsible for cleaning up the terminal, and it cleans up the terminal on `Drop` and on panic. To do the latter, it would register a panic hook, and deregister that hook on `Drop`.

With such a mechanism, panic hooks could replace anything that uses `catch_unwind` to do cleanup before going on to exit the program. That wouldn't fully solve the problem of doing cleanup and then swallowing the panic and continuing, but it'd be a useful component for that.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#103
post #91

Earlier quoted context omitted.

In the entire history of the standard library, we have never once seen a single report of anyone attempting to recover from poison.

That’s not surprising to me, but it’s not much of an argument for changing the default to be less safe. Most people want poisoning to propagate fatal errors and avoid reading corrupted data, not to recover from panics. Edit: isn’t that an argument not to change the default? If people were recovering from poison a lot and that was painful, that’s one thing. But if people aren’t doing that, why is this a problem?

Because right now everyone writes `.lock().unwrap()` everywhere without really thinking about it, and it just makes Mutex more painful to work with.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#104

Earlier quoted context omitted.

A panic in single-threaded, non-parallel code will either terminate the program or be recovered cleanly, so the potential for side effects to be silently observed in a way that breaks invariants is unique to Mutex . This is the reason for mutex poisoning,

I fail to see that there is any material difference. Whether you catch-unwind within a single thread or in a separate thread such that the panic can be resumed on join makes zero difference. Heck, you can have Drop impls observing the state while unwinding . A true panic-safe data structure requires serious thought, and mutex poisoning does nothing here - it is neither necessary nor sufficient.

This is exactly the problem. Poison is enough to be painful but not enough to fully solve the problem.

> Heck, you can have Drop impls observing the state while unwinding.

Yeah, this is really painful and regularly forgotten. And one reason it'd be nice to not have unwinding.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#105
post #84

Earlier quoted context omitted.

In the entire history of the standard library, we have never once seen a single report of anyone attempting to recover from poison.

I've used recovering from poisoned state in impl Drop in quite a few places. In my case it's usually waiting for the GPU to finish some asynchronous work that's been spun up by CPU threads that may have panicked while holding the lock. This is necessary to avoid freeing resources that the GPU may still be using. I usually prefix this with `if !std::thread::panicking() {}`, so I don't end up waiting (possibly forever)…

Thank you for mentioning this; I'd be really interested in hearing more about this, and seeing some examples.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#106
post #91

Earlier quoted context omitted.

That’s not surprising to me, but it’s not much of an argument for changing the default to be less safe. Most people want poisoning to propagate fatal errors and avoid reading corrupted data, not to recover from panics. Edit: isn’t that an argument not to change the default? If people were recovering from poison a lot and that was painful, that’s one thing. But if people aren’t doing that, why is this a problem?

Because right now everyone writes `.lock().unwrap()` everywhere without really thinking about it, and it just makes Mutex more painful to work with.

If the issue is that everyone has to write an extra unwrap, then a good step would be to make lock panic automatically in the 2027 edition, and add a lock_or_poison method for the current behavior. But I think removing poisoning altogether from the default mutex, such that it silently unlocks on panic, would be very bad. The silent-unlock behavior is terrible with async cancellations and terrible with panics.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#107

Earlier quoted context omitted.

I understand what you mean, but you're saying has not been true for me in practice. Mutexes absolutely are used to uphold invariants in a way that &mut T is much less often. There's something to be said here about what I've sometimes called the cancellation blast radius. The issues with cancellation happen when the data corruption/invariant violation is externally visible (if the corrupt data is torn down, who cares.…

In projects I've worked on, this just hasn't been the case. Mutexes, especially in Rust, can grant you a `&mut T` when what you have is `&Mutex `, and that's it - failing to uphold invariants in the API surface of `T` is a bug whether or not it lives inside a mutex. Lots of data structures need to care about panic-safety. Inserting a node in a tree must leave the tree in a valid state if allocating memory for the new…

Acknowledging that panic safety is a real issue with data structures that mutex poisoning does not solve, I don't think we're going to agree on anything else here, unfortunately. We probably have entirely different experiences writing software -- mutex poisoning is very valuable in higher-level code.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#108

Earlier quoted context omitted.

That is a different topic from what I wrote about. The article wrote: > Automatic unlock: When you lock, you receive a guard. When the guard goes out of scope, it automatically unlocks. No manual cleanup needed. And presented Rust as being different from C++ regarding that, and the C++ example was not idiomatic, since it did not use something like std::lock_guard. I have not addressed the rest of your comment, since…

Fair point with respect to the separate topic. My apologies. As for the automatic cleanup bit, perhaps the article is trying to focus purely on the mutex types themselves? Or maybe they included the " when you lock " bit to emphasize that you can't forget to unlock the mutex (i.e., no reliance on unenforced idioms). Hard to say given the brevity/nature of the section, and in the end I think it's not that much of a pr…

When comparing languages, posting unidiomatic code, and then making claims based on that unidiomatic code, is generally not fair nor correct.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#109

Earlier quoted context omitted.

I am not sure that I understand what scoped panic hooks would or might look like. Are they maybe similar to something like try-catch-finally in Java? Would the language force the programmer to include them in certain cases somehow? If a request handler for example has at some point in time 7 nested calls, in call no. 2 and call no. 6 have resources and partial computation that needs clean-up somehow and somewhere, an…

For the simple case, suppose that you're writing a TUI application that takes over the terminal. When it exits, even by panic, you want to clean up the terminal state so the user doesn't have to blindly type "reset". Today, people sometimes do that by using `panic = "unwind"`, and writing a `catch_unwind` around their program, and using that to essentially implement a "finally" block. Or, they do it by having an RAII…

I have not given it much thought, but it would primarily be for the subset of Rust programs that do not need zero-cost abstractions as much, right? Since, even in the case of no panics, one would be paying at runtime for registering panic hooks, if I understand correctly.

Re: Inside Rust's std and parking_lot mutexes – who wins?

#110

Earlier quoted context omitted.

Fair point with respect to the separate topic. My apologies. As for the automatic cleanup bit, perhaps the article is trying to focus purely on the mutex types themselves? Or maybe they included the " when you lock " bit to emphasize that you can't forget to unlock the mutex (i.e., no reliance on unenforced idioms). Hard to say given the brevity/nature of the section, and in the end I think it's not that much of a pr…

When comparing languages, posting unidiomatic code, and then making claims based on that unidiomatic code, is generally not fair nor correct.

That's true if the claims hinge on said unidiomatic code. As I said, it's not clear to me that that is the case here.
Post reply on HN