Live data from Hacker News

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

blog.cuongle.dev

81–90 of 134 posts

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

#81

> Poisoning: Panic Safety in Mutexes This is one of the biggest design flaws in Rust's std, in my opinion. Poisoning mutexes can have its use, but it's very rare in practice. Usually it's a huge misfeature that only introduces problems. More often than not panicking in a critical section is fine [1], but on the other hand poisoning a Mutex is a very convenient avenue for a denial-of-service attack, since a poisoned M…

To the contrary, the projects I've been part of have had no end of issues related to being cancelled in the middle of a critical section [1]. I consider poisoning to be table stakes for a mutex. [1] https://sunshowers.io/posts/cancelling-async-rust/#the-pain-...

Worth noting that this is not `std::mutex` or `parking_lot::mutex` as discussed in the article, but `tokio::sync::Mutex` in cancellable async code.

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

#82
post #19

I will personally recommend that unless you are writing performance sensitive code*, don’t use mutexes at all because they are too low-level an abstraction. Use MPSC queues for example, or something like RCU. I find these abstractions much more developer friendly. *: You may be, since you are using Rust.

Channels and RCU can often be better for performance as well . I have run into scaling issues due to contention way too many times. Sometimes just because two things were in different parts of the same cache line. Sharing as little mutable state as possible is often the best way to scale to a large number of CPU cores. RCU can help with that if you have data that is mostly read but rarely written. If your workload is…

Very interesting, I want to learn more. How do you diagnose contention on atomic counters? And how do you diagnose cache line contention?

And how do you rewrite that code away from Arc-s?

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

#83

Earlier quoted context omitted.

As I said in the article, we avoid Tokio mutexes entirely for the exact reason that being cancelled in the middle of a critical section is bad. In Rust, there are two sources of cancellations in the middle of a critical section: async cancellations and panics. Ergo, panicking in the middle of a critical section is also bad, and mutexes ought to detect that and mark their internal state as corrupted as a result.

> Ergo, panicking in the middle of a critical section is also bad, and mutexes ought to detect that and mark their internal state as corrupted as a result. I fundamentally disagree with this. Panicking in the middle of an operation that is supposed to be atomic is bad. If it's not supposed to be atomic then it's totally fine, just as panicking when you hold a plain old `&mut` is fine. Not every use of a `Mutex` is pr…

If you’re not looking to scope out an atomic section, why are you taking the lock?

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

#84

Earlier quoted context omitted.

I'm very disappointed at this. The path of least resistance ought to be the right thing to do.

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) if I'm already cleaning up after a panic.

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

#85

Earlier quoted context omitted.

Mutex doesn't promise to uphold any more invariants than `&mut T` does. If the state can be corrupted by a panic while holding `&mut T`, I don't think there's any good reason to expect that obtaining it through `MutexGuard` should make any difference. Panic propagation is typically handled much better at thread `join()` boundaries.

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.

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

#86
post #3

There was a giant super-long GitHub issue about improving Rust std mutexes a few years back. Prior to that issue Rust was using something much worse, pthread_mutex_t. It explained the main reason why the standard library could not just adopt parking_lot mutexes: From https://github.com/rust-lang/rust/issues/93740 > One of the problems with replacing std's lock implementations by parking_lot is that parking_lot alloca…

I dunno, it seems to me that the standard mutex performs very well on all scenarios, and doesn't have any significant downsides, except for the hogging case, which could be fixed by assigning the non-hogging threads a higher priority.

Whereas parking_lot has a ton of problematic scenarios, where after the spinlock times out, and it yields the thread to the OS, which has no idea to wake up the thread after the resource is unblocked.

It could be even argued that preventing starvation is outside the design scope of the Mutex as a construct, as it only guarantees mutual exclusion and that the highest priority waiting thread should get access to it.

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

#87
post #7

Earlier quoted context omitted.

Seems like the simple solution to this problem would be to have both, no? A simple native lock in the standard library along with a nicer implementation (also in the standard library) that depends on the simple lock?

The simplest solution is for `std::mutex` to provide a simple, efficient mutex which is a good choice for almost any program. And it does. Niche programs can pull in a crate. I doubt `parking_lot` would have been broadly used—maybe wouldn't even have been written—if `std` had this implementation from the start. What specifically in this comparison made you think that `parking_lot` is broadly needed? They had to work…

This. the standard library has a responsibility to provide an implementation that performs well enough in every possible use case, while trying to be generally as fast as possible.

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

#88

Author of the original WTF::ParkingLot here (what rust’s parking_lot is based on). I’m surprised that this only compared to std on one platform (Linux). The main benefit of parking lot is that it makes locks very small, which then encourages the use of fine grained locking. For example, in JavaScriptCore (ParkingLot’s first customer), we stuff a 2-bit lock into every object header - so if there is ever a need to do s…

> The main benefit of parking lot is that it makes locks very small, which then encourages the use of fine grained locking. For example, in JavaScriptCore (ParkingLot’s first customer), we stuff a 2-bit lock into every object header - so if there is ever a need to do some locking for internal VM reasons on any object we can do that without increasing the size of the object IMHO that's a very cool feature which is ess…

I think he meant 1 byte on the heap for the shared state, on the stack it's larger.

Which is fine since in Rust we almost always have the mutex in function scope as long as we're using it.

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

#89
post #3

There was a giant super-long GitHub issue about improving Rust std mutexes a few years back. Prior to that issue Rust was using something much worse, pthread_mutex_t. It explained the main reason why the standard library could not just adopt parking_lot mutexes: From https://github.com/rust-lang/rust/issues/93740 > One of the problems with replacing std's lock implementations by parking_lot is that parking_lot alloca…

> This means SRW locks on Windows, and futex-based locks on Linux, some BSDs, and Wasm. Note that the SRW Locks are gone, except if you're on a very old Windows. So today the Rust built-in std mutex for your platform is almost certainly basically a futex though if it is on Windows it is not called a futex and from some angles is better - the same core ideas of the futex apply, we only ask the OS to do any work when w…

> if it is on Windows it is not called a futex

What is it called?

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

#90
post #42
post #19

I will personally recommend that unless you are writing performance sensitive code*, don’t use mutexes at all because they are too low-level an abstraction. Use MPSC queues for example, or something like RCU. I find these abstractions much more developer friendly. *: You may be, since you are using Rust.

I have found out that mutex solutions are more maintainable and amendable without big redesigns compared with channels or RCU. Consider a simple case of single producer-single consumer. While one can use bounded channels to implement back-pressure, in practice when one wants to either drop messages or apply back-pressure based on message priority any solution involving channels will lead to pile of complex multi-chan…

A channel can be backed by a priority queue if you wish. It’s just an abstraction. The channel internally probably uses mutexes too; it’s just that it’s helpful not to see mutexes in application code.
Post reply on HN