Skip to content

Commit 9b7dac3

Browse files
authored
Deadlock between Watcher and Debounce threads (fix #187) (#189)
There are two threads involved in `Watcher.cc` and `Debounce.cc` each calling into respective methods of the other and thus each potentially holding onto locks of the other. This can lead to a deadlock in the following scenario: While the `Debounce.cc` thread is processing callbacks in the `Debounce::notify()` method, it holds its own lock. The method loops over callbacks to process in `Watcher.cc` which itself requires a lock in `Watcher.cc`. If an event gets reported while the debouncer is in `Watcher.triggerCallbacks()`, a deadlock is present, because: - assume the event thread is thread A - assume the debouncer thread is thread B - A holds its own lock in `Watcher::notify()` and calls into `Debounce.trigger()` which requires the debouncer lock - B at this time is inside `Debounce::notify()` holding its own lock processing callbacks and about to call into `Watcher.triggerCallbacks()` - A deadlocks waiting for B to release the debouncer lock - B deadlocks waiting for A to release the watcher lock
1 parent 99cf43b commit 9b7dac3

File tree

1 file changed

+5
-0
lines changed

1 file changed

+5
-0
lines changed

src/Watcher.cc

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ void Watcher::notify() {
6666
mCond.notify_all();
6767

6868
if (mCallbacks.size() > 0 && mEvents.size() > 0) {
69+
// We must release our lock before calling into the debouncer
70+
// to avoid a deadlock: the debouncer thread itself will require
71+
// our lock from its thread when calling into `triggerCallbacks`
72+
// while holding its own debouncer lock.
73+
lk.unlock();
6974
mDebounce->trigger();
7075
}
7176
}

0 commit comments

Comments
 (0)