Ronen
Ronen

Reputation: 311

std::mutex in C++ versus std::sync::Mutex in rust

I've always found std::mutex is C++ to be manual and error-prone. I need to manually attribute a certain std::mutex to specific variables and I need to remember to always lock the std::mutex before I access those specific variables.

Then I started learning Rust and I see that in Rust, std::sync::Mutex is a wrapper for objects, similar to how std::tuple in C++ is a wrapper for objects. std::sync::Mutex can be locked, and then it returns an object to access the members. As long as that handle object exists, the std::sync::Mutex stays locked, and it gets unlocked in the destructor (or something, not sure how it works in Rust). My first impression is that that's such a better way to tie a mutex to the data it's supposed to protect!

So my thought was: how can I get this feature into C++? I know that C++ is a powerful choice for creating custom features (e.g. boost) so here's the question:

Are there issues with the way Rust does things? (deadlocking concerns etc.)

Is there a reason that Rust can get away with what it does, that C++ cannot match? (preventing deadlock, borrow checker to keep the mutex locked etc.)

Is there an existing implementation of Rust-style std::mutex wrapper for C++?

Is Rust's implementation broken? (because of deadlock or something)

Or, am I doomed? Is C++ so fundamentally broken?

Upvotes: 2

Views: 1078

Answers (2)

Francis Gagné
Francis Gagné

Reputation: 65907

Are there issues with the way Rust does things? (deadlocking concerns etc.)

MutexGuard can hand out a mutable/exclusive reference (&mut) to the value protected by the mutex. In order to guarantee that a MutexGuard has exclusive access to the protected value, mutexes cannot be reentrant. A reentrant mutex could hand out more than one MutexGuard, each of which would be able to hand out a mutable reference to the same value, violating the rule that there cannot be more than one active mutable reference to the same memory location at the same time.

Is there a reason that Rust can get away with what it does, that C++ cannot match? (preventing deadlock, borrow checker to keep the mutex locked etc.)

The references handed out by MutexGuard have a lifetime that is linked to the MutexGuard. This means that when the MutexGuard is dropped (i.e., the lock is released), the references to the protected value can no longer be used.

If you were to implement the same pattern in C++, you could prevent the user from accessing the protected value until the lock is taken, but you couldn't prevent them from keeping a pointer or reference to the protected value after the lock is released, unless the lock guard never handed out pointers or references to the protected value in the first place.

Is Rust's implementation broken? (because of deadlock or something)

Rust's mutexes don't prevent deadlocks. The classic case of thread 1 locking A then B and thread 2 locking B then A can very well cause a deadlock in Rust. I wouldn't consider Rust broken because of that, because most other languages have the same weakness.

Upvotes: 4

battlmonstr
battlmonstr

Reputation: 6300

In C++ you can use std::scoped_lock to get the automatic unlocking.

It is decoupled from std::mutex, because there are different mutex types (recursive, non-recursive, pthread-based, spin locks etc.). In theory you can substitute the mutex type without affecting your code if its written in a type-agnostic way.

In Rust (1.58) there's no common interface for mutexes of different types, and guard types returned can also be different in theory (although quite similar in practice).

To get the data encapsulation aspect in C++, you might want to implement a custom smart pointer. There are some libraries that do that, for example cpp-mutex-guard.

There's no magic in the Rust implementation besides the poisoning idea, so it is possible to implement in C++.

Upvotes: 2

Related Questions