Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deadlock between Watcher and Debounce threads #187

Closed
bpasero opened this issue Sep 5, 2024 · 2 comments · Fixed by #189
Closed

Deadlock between Watcher and Debounce threads #187

bpasero opened this issue Sep 5, 2024 · 2 comments · Fixed by #189

Comments

@bpasero
Copy link
Contributor

bpasero commented Sep 5, 2024

Ever since 9b7c657 landed, we have tried to update to newer versions but in VS Code events would stop at random and not recover. Now I had some time to diagnose this further and add logging into the various places where std::unique_lock<std::mutex> are used because I was suspecting a deadlock between threads.

VS Code is probably making it easier to uncover this issue though I would think it could happen otherwise too: in VS Code it is possible for the same folder being watched with different set of ignore rules, so you can end up with N events firing from fsevents for the same path.

In master...bpasero:watcher:ben/fork-2, logging is added through out Watcher.cc and Debounce.cc, in this pattern:

  • ...LOCK 1: try to acquire a lock
  • ...LOCK 2: lock acquired
  • ...LOCK 3: lock released

Steps to Reproduce:

watcher.subscribe('/Users/bpasero/Development/Microsoft/vscode', (err, events) => {
    console.log(1, events, err);
});

watcher.subscribe('/Users/bpasero/Development/Microsoft/vscode', (err, events) => {
    console.log(2, events, err);
}, { ignore: ['aaa']});

Run above with correct paths to any folder (does not have to be vscode). Open a file from that folder and simply keep Cmd+S pressed in the editor to trigger a ton of saves (e.g. using VS Code).

=> 🐛 at one point, no more events are printed

Deadlock

With the logging in my fork, I get the following output:

Log Output
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f867000] Debounce::notify(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f867000] Debounce::notify(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 1
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks - LOCK 3
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 1
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 2
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks - LOCK 3
[2024-09-05 21:19:52, thread: 0x16f867000] Debounce::notify(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:52, thread: 0x16f867000] Debounce::notify(): LOCK 1
[2024-09-05 21:19:52, thread: 0x16f867000] Debounce::notify(): LOCK 2
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 1
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 2
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks - LOCK 3
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 1
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 2
[2024-09-05 21:19:52, thread: 0x16f867000] Watcher::triggerCallbacks - LOCK 3
[2024-09-05 21:19:52, thread: 0x16f867000] Debounce::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 3
[2024-09-05 21:19:53, thread: 0x16f867000] Debounce::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f867000] Debounce::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1


You can see that 2 threads are involved here: the debounce thread and the watcher thread. Both have the chance of locking the mutex in Watcher.cc as well as Debounce.cc. If the one thread was successful in locking the one mutex while the other was successful in locking the other and then they both need the others lock to proceed, the watcher deadlocks.

Lets focus on the last log messages, separating threads with newlines:

...

[2024-09-05 21:19:53, thread: 0x16f867000] Debounce::notify(): LOCK 1

[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 1
[2024-09-05 21:19:53, thread: 0x16f8f3000] Watcher::notify(): LOCK 2

[2024-09-05 21:19:53, thread: 0x16f867000] Debounce::notify(): LOCK 2
[2024-09-05 21:19:53, thread: 0x16f867000] Watcher::triggerCallbacks() - LOCK 1

[2024-09-05 21:19:53, thread: 0x16f8f3000] Debounce::trigger(): LOCK 1

<NO MORE OUTPUT DUE TO DEADLOCK>

Threads:

  • 0x16f867000: debouncer
  • 0x16f8f3000: watcher

The deadlock is:

  • the debouncer wakes up to process the events that have accumulated, it locks the Debouncer.cc lock successfully
  • meanwhile an event is coming from the watcher and it locks the Watcher.cc lock successfully
  • the debouncer now triggers callbacks which is going into Watcher.cc that requires the lock in Watcher.cc that the other thread holds
  • the watcher wants to trigger the debouncer but needs the lock of Debouncer.cc that the other thread holds

Note: I have seen other variations of this deadlock

@devongovett I would need your advice here and would appreciate if you could have a look. I have a fix in mind that seems to help but I am not sure if its addressing all the issues. Specifically before this line:

mDebounce->trigger();

call lk.unlock(); so that the Watcher.cc lock is free when the debouncer thread needs it.

@bpasero
Copy link
Contributor Author

bpasero commented Sep 7, 2024

Seeing how it was working in 2.1.0, I noticed a second mutex that was only used for triggerCallbacks and restoring that seems to fix the deadlock in 2.4.x 🤞

PR: #189

bpasero added a commit to bpasero/watcher that referenced this issue Sep 11, 2024
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
@bpasero
Copy link
Contributor Author

bpasero commented Sep 11, 2024

I updated #189 and now have more understanding when exactly this issue happens. The fix is relatively simple.

bpasero added a commit to bpasero/watcher that referenced this issue Sep 12, 2024
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
bpasero added a commit to bpasero/watcher that referenced this issue Sep 12, 2024
the `@parcel/watcher` in VSCode:
- Deadlock between Watcher and Debounce threads (fix parcel-bundler#187)
- Allow to compile the native components from sources
- Prefer `stat` first before calling `pathExists`

Deadlock between Watcher and Debounce threads (fix parcel-bundler#187)

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
bpasero added a commit to bpasero/watcher that referenced this issue Sep 13, 2024
the `@parcel/watcher` in VSCode:
- Deadlock between Watcher and Debounce threads (fix parcel-bundler#187)
- Allow to compile the native components from sources
- macOS: Prefer `stat` first before calling `pathExists`
- Windows: disable WER UI in case of crashes

Notes on the deadlock:

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant