17

Is there a C++ std::lock() like facility in Rust to prevent deadlocking in code like this:

type Type0 = Arc<Mutex<u8>>;
type Type1 = Arc<Mutex<u16>>;

fn foo(a: Type0, b: Type1) {
    let a_guard = a.lock().unwrap();
    let b_guard = b.lock().unwrap();
}

fn bar(a: Type0, b: Type1) {
    let b_guard = b.lock().unwrap();
    let a_guard = a.lock().unwrap();
}

If foo is called by thread-0 and bar by thread-1 there is a chance of deadlock. Is there something, hopefully variadic because I can have more than 2, to help me with this or am I all on my own verifying the correctness of order of locking?

From the documentation for std::lock:

Locks the given Lockable objects lock1, lock2, ..., lockn using a deadlock avoidance algorithm to avoid deadlock.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ustulation
  • 3,600
  • 25
  • 50
  • 1
    Admittedly I don't know how `std::lock` works, but (unless you follow mmstick's answer in lumping the data together and locking/unlocking it at once) I don't see how you could ever *guarantee* no deadlocks. Sure, you could write a macro to do them in the right order -- but then you've just mutated the problem of *checking that you always lock them in the right order* into the barely-easier problem of *checking that you always use the macro and never call .lock() directly*. Right? – trent Sep 08 '16 at 16:22

2 Answers2

5

This is easily solved if the Mutex is a tuple with the contained values, so that locking the tuple locks both values simultaneously.

let tuple_mutex = Arc::new(Mutex::new((A, B)));
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 5
    Excellent point, but might want to retain the flexibility of independent values. – Matthieu M. Sep 08 '16 at 15:26
  • We can view tuple is just a single type - so that is basically Mutexing a single type. What i am looking for though is a solution to different Mutexs. For granularity you might not want to _always_ lump them into a single tuple/type - then you are sacrificing cases where they could function independent of each other which abound in my code. It's really a difference betwen `Arc – ustulation Sep 08 '16 at 22:19
  • Does this really allow us to lock the mutextes simultaneously, or are they in reality locked one after the other? If so, what order are the mutextes unlocked in? – Simon Elliott Sep 09 '16 at 11:06
  • 2
    @SimonElliott There is only one `Mutex` in this solution; it is used to lock a compound value. The values are locked simultaneously because they are controlled by the same mutex. – trent Sep 09 '16 at 14:23
5

No, Rust does not have a function equivalent to C++'s std::lock.

Based on the fact that it doesn't seem to be in the std::sync documentation and Googling brings up nothing useful, I'm pretty confident in this assertion.

Why not? Well, if I may editorialize a bit, std::lock isn't as broadly useful as you might hope. Deadlock avoidance is nontrivial and every algorithm will have plausible corner cases that result in poor performance or even livelock. There is no one-size-fits-all deadlock avoidance algorithm.¹ (See Is std::lock() ill-defined, unimplementable, or useless?) Putting a deadlock-avoiding lock function in the standard library suggests that it's a good default choice, and perhaps encourages its use without regard for its implementation. Most real-life applications probably would do as well with a simpler (and less general-purpose) algorithm.

There are crates that provide deadlock avoidance by other means. For instance, tracing-mutex provides locking types that create a dependency graph at runtime and will panic instead of deadlocking if the dependency graph contains a cycle. parking_lot has an experimental deadlock_detection feature (but I'm not sure how it works). Curiously, I didn't find any crates that provide a C++ std::sort equivalent with backing off.

Anyway, nothing stops you writing your own "backing off" algorithm to solve this problem; it's just not part of the standard library.


¹ In fairness, you could make the same argument for other functions Rust does have, like [T]::sort. But there are many applications where sorting is not a bottleneck and any reasonably fast algorithm is good enough. Deadlock avoidance is both less likely to be necessary in general, and more likely to be performance-sensitive when it does appear.

trent
  • 25,033
  • 7
  • 51
  • 90