cogciprocate / qutex

Synchronization mechanisms based on lock-free queues and Rust futures
MIT License
36 stars 8 forks source link

Detecting deadlock with single qutex/qrwlock #1

Open yjh0502 opened 6 years ago

yjh0502 commented 6 years ago

Here's example code which causes deadlock without unsafe code.

extern crate futures;
extern crate qutex;

use qutex::*;
use futures::prelude::*;

fn main() {
    let lock = QrwLock::from(0i32);

    let f = lock.clone().write().and_then(move |mut guard_w| {
        eprintln!("write lock acquired");
        lock.read().and_then(move |guard_r| {
            eprintln!("read lock acquired");

            let r0 = *guard_r;
            *guard_w += 1;
            let r1 = *guard_r;
            assert_eq!(r0, r1);
            Ok(())
        })
    });

    assert_eq!(Ok(()), f.wait());
}

Is there any way to detect the deadlock? It would be nice for FutureGuard to return Deadlocked error (just like Canceled error) if there's any prior FutureGuard which belongs to current task. In that case, above example will return Error(Deadlocked) on f.wait() instead of hanging on deadlock.

c0gent commented 6 years ago

As someone who has struggled plenty with deadlocks, I have often considered ways of implementing some sort of detection system. It wouldn't be too hard to add a feature-gated system to detect deadlocks caused by other Qutex/QrwLocks (as in your example) and I would be completely open to adding one. Most deadlocks, however, come from interactions with other, outside, queues and scheduling systems (threads and event loops). Developing a system to detect deadlocks caused by interactions with those systems would be quite an undertaking.

What I often do in my applications is to use a dependency graph to plan out ahead of time, in which order things will be enqueued. Here is a simple example of this. Here is a more complicated example which is rebuildable. If implemented correctly, using graphs like this to schedule tasks which require exclusive access to resources (usually a region of memory, aka. a buffer) will prevent deadlocks from happening in the first place.

I'm interested to hear any other ideas you may have as to what else could/should be done and how they could be implemented.