Deadlock freedom

Related tags

Utilities with_lock
Overview

with_lock

Deadlock freedom

Docs

Example

Say you have this code:

use std::sync::Mutex;

fn main() {
    let a = Mutex::new(2);
    let b = Mutex::new(3);
    let a_lock = a.lock().unwrap();
    let b_lock = b.lock().unwrap();
    println!("{:?}", *a_lock + *b_lock);
    let a_lock_2 = a.lock().unwrap();
    let b_lock_2 = b.lock().unwrap();
    println!("{:?}", *a_lock_2 + *b_lock_2);
}

That code will log 5 once, when it should log twice. As you can see here, it is deadlocking.

However, we can prevent this by replacing our manual calls of .lock with .with_lock. Code that wouldn't error would look something like:

use std::sync::Mutex;
use with_lock::WithLock;

fn main() {
    let a = WithLock::<i64>::new(Mutex::new(2));
    let b = WithLock::<i64>::new(Mutex::new(3));
    let a_lock = a.with_lock(|s| *s);
    let b_lock = b.with_lock(|s| *s);
    println!("{:?}", a_lock + b_lock);
    let a_lock_2 = a.with_lock(|s| *s);
    let b_lock_2 = b.with_lock(|s| *s);
    println!("{:?}", a_lock_2 + b_lock_2);
}

That code would log 5 twice. This is an example of how it can prevent deadlocks.

Minimal code changes

For the people that want little to no code changes, with_lock exposes a custom MutexCell type.

Code that would produce deadlocks would look like this:

use std::sync::Mutex;

fn main() {
    let a = Mutex::new(2);
    let b = Mutex::new(3);
    let a_lock = a.lock().unwrap();
    let b_lock = b.lock().unwrap();
    println!("{:?}", *a_lock + *b_lock);
    let a_lock_2 = a.lock().unwrap();
    let b_lock_2 = b.lock().unwrap();
    println!("{:?}", *a_lock_2 + *b_lock_2);
}

And using the custom MutexCell type that wouldn't deadlock would look like:

use with_lock::MutexCell;

fn main() {
    let a = MutexCell::new(2);
    let b = MutexCell::new(3);
    let a_locked = a.get();
    let b_locked = b.get();
    println!("{:?}", a_locked + b_locked);
    let a_lock_2 = a.get();
    let b_lock_2 = b.get();
    println!("{:?}", a_lock_2 + b_lock_2);
}
You might also like...
Comments
  • fn with_lock<F, U> where  F: FnMut

    fn with_lock where F: FnMut

    Current definition is

    	pub fn with_lock<F, U>(&self, function: F) -> U
    	where
    		F: FnOnce(&mut T) -> U,
    

    EDIT:

    FnOnce will not permit Closures/Lambdas with captured mutable variables/state. Declaring 'function' as FnOnce, captured variables will be "consumed" by the invokation. The 'function" will not be able to share state between invocations., for example counting the number of calls.

    The following test is failing, as the function "action_and_get" does not hold the state of "count", being of type FnOnce. Instead being 11 the final sum is 10. It seems, Rust is creating a new instance for every invocation of action_and_get, and afterwards destroying the FnOnce-Instance including the state of "count".

    
    	#[test]
    	fn test_with_lock_capture_mut() {
    		let a = WithLock::<SharedData>::new(Mutex::new(SharedData { a: 2, b: 2 }));
    		let mut count : i32 = 0;
    
    		let action_and_get = move |s: &mut SharedData| {
    			count += 1;
    			let sum = (*s).a + (*s).b + count;
    			sum
    		};
    		let a_lock = a.with_lock(action_and_get);
    		let a_lock_2 = a.with_lock(action_and_get);
    
    		assert_eq!(a_lock + a_lock_2, 11);
    	}
    
    

    Instead of FnOnce, the 'function' should be declared as FnMut, so that the function would be able to modify other mutable captured data, such as the captured "count" variable.

    opened by frehberg 4
Releases(v0.5.4)
Owner
Milo
being unproductive
Milo
MASQ Network 121 Dec 20, 2022
Freedom Planet 2 resolution scaling fixer.

What is contained in this repository? This is an utility to patch Freedom Planet 2's internal resolution from 640x360 to a multiple of it, considerabl

João Henrique 6 Nov 23, 2022