peace_lock

A Mutex/RwLock that will panic if there's contention!

peace_lock helps you sanity check your concurrent algorithm and becomes zero-cost outside the check mode.

Motivation

A lock that expects no contention seems counter-intuitive: the reason to use a lock in the first place is that there are contentions, right? Yes and no.

Sometimes you implement a contention-free algorithm on your data structure. You think having concurrent write to the data structure is OK, but the compiler is unhappy about it. To make it worse, you must ensure the algorithm is free from bugs to be truly contention-free. This makes you choose RwLock by unnecessarily sacrificing some performance. Later, to make it performant, you end up with a solution using UnsafeCell and making unsafe code everywhere.

Let's think about this example:

You want to change the content of the values in a hashmap concurrently. ```rust let map: HashMap = ...; let shared_map = Arc::new(map);

// do these two concurrently in different threads thread::spawn(|| { *sharedmap.getmut(&1).unwrap() = 3; }); thread::spawn(|| { *sharedmap.getmut(&2).unwrap() = 4; }); ``` The above code won't work because you cannot get mutable references inside the Arc. But "Hey, compiler, this is safe, I guarantee!" because you have invented a scheduling algorithm so that different threads will always change the values of different keys.

To circumvent compiler, you use RwLock: ```rust let map: HashMap> = ...; let shared_map = Arc::new(map);

// do these two concurrently in different threads thread::spawn(|| { *sharedmap.get(&1).unwrap().write() = 3; }); thread::spawn(|| { *sharedmap.get(&2).unwrap().write() = 4; }); `` But since you know there's no conflict, usingRwLock` harms the performance.

Now you decide to do some black magic: ```rust let map: HashMap> = ...; let shared_map = Arc::new(map);

// do these two concurrently in different threads thread::spawn(|| { unsafe { *sharedmap.get(&1).unwrap().get() = 3; } }); thread::spawn(|| { unsafe { *sharedmap.get(&2).unwrap().get() = 4; } }); `` The code is running correctly, until it doesn't. Now you wonder: am I implementing the scheduling right? Could there be a contention bug? I really want to useRwLock` to sanity check but I don't want to have the performance hit.

This crate is coming to save you.

peace_lock

peacelock is a drop-in replacement for std/parkinglot's Mutex and RwLock. ```rust use peace_lock::RwLock;

let map: HashMap> = ...; let shared_map = Arc::new(map);

// do these two concurrently in different threads // The write call will panic if there's another thread is writing to the value. // The panic behavior can be disabled by feature flags so that write becomes // zero-cost. thread::spawn(|| { *sharedmap.get(&1).unwrap().write() = 3; }); thread::spawn(|| { *sharedmap.get(&2).unwrap().write() = 4; }); ```

You can disable the check by peace_lock = { version = "0.1", default-features = false }.

In case of contention, calling write or read will just panic, which let's you know the scheduling algorithm has a bug!

Help Wanted

I'm not that proficient in atomics. It will be super helpful if someone can help me check if the atomic ordering is used correctly and is not too tight.

Licence

MIT