LMMS / LMMS/lmms

Possible deadlock involving m_patternMutex in AutomationPattern

Open
#6,013 5 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C++
Stars
10.4k
Forks
1.3k
Avg merge
2d 13h
Merged PRs (30d)
7

Description

It seems I found a deadlock in `AutomationPattern`. Or rather, it found me, because I wasn't looking for it.

The setup was: TrippleOsc set on noise and with volume and cutoff envelopes active, playing in a BB pattern. While it was playing, I was holding the left mouse button on PianoView and wildly moving the cursor around, so randomly spamming a lot of notes. After a while everything froze. There was only one empty (unused) automation track in the project.

I attached gdb to the process, but I never tried to find the cause of a deadlock in code I don't know much about, so it took me a few hours (to re-learn parts of gdb and) to even realize _which_ thread is the one truly making everything stuck. But even then, I can't see any way the deadlock could happen. The mutex in question is always used with `QMutexLocker` and it is recursive, so it can be called repeatedly within one thread.

Worse still, so far I wasn't able to reproduce the problem with the same build and the same setup, so at this point I'm willing to accept it just may have been caused by a cosmic ray or something.

If someone has a clue what could be happening and wants to dive into it, be my guest, I give up. Here are backtraces of the more interesting threads and some notes. Otherwise this may be closed just as a note for the future, that this is a thing that could potentially happen and we don't really know why.

```
thread 1: "lmms" (user interface)

#0 0x00007f9e841a8db5 in futex_wait_cancelable (private=0, expected=0, futex_word=0x55fff5d966c4) at ../sysdeps/unix/sysv/linux/futex-internal.h:88
#1 0x00007f9e841a8db5 in __pthread_cond_wait_common (abstime=0x0, mutex=0x55fff5d96670, cond=0x55fff5d96698) at pthread_cond_wait.c:502
#2 0x00007f9e841a8db5 in __pthread_cond_wait (cond=0x55fff5d96698, mutex=0x55fff5d96670) at pthread_cond_wait.c:655
#3 0x00007f9e81cab21b in QWaitCondition::wait(QMutex*, unsigned long) () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#4 0x000055fff389b3bd in Mixer::requestChangeInModel() ()
#5 0x000055fff3a0ee89 in InstrumentTrack::processInEvent(MidiEvent const&, TimePos const&, int) ()
#6 0x000055fff395cd0a in PianoView::mouseMoveEvent(QMouseEvent*) ()
#7 0x00007f9e83ce04d8 in QWidget::event(QEvent*) () at /lib/x86_64-linux-gnu/libQt5Widgets.so.5
...
- Mixer::requestChangeInModel() is waiting in m_changesRequestCondition.wait( &m_waitChangesMutex );
- i.e. waiting for the current buffer to finish processing
- InstrumentTrack::processInEvent() did not lock anything before that
- neither did PianoView::mouseMoveEvent()
- the rest is Qt stuff

threads 7, 8, 9: mixer workers

#0 0x00007f9e841a8db5 in futex_wait_cancelable (private=0, expected=0, futex_word=0x55fff5dcdc10) at ../sysdeps/unix/sysv/linux/futex-internal.h:88
#1 0x00007f9e841a8db5 in __pthread_cond_wait_common (abstime=0x0, mutex=0x55fff5dcdbc0, cond=0x55fff5dcdbe8) at pthread_cond_wait.c:502
#2 0x00007f9e841a8db5 in __pthread_cond_wait (cond=0x55fff5dcdbe8, mutex=0x55fff5dcdbc0) at pthread_cond_wait.c:655
#3 0x00007f9e81cab21b in QWaitCondition::wait(QMutex*, unsigned long) () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#4 0x000055fff38a0eb7 in MixerWorkerThread::run() ()
...
- waiting for something to do at queueReadyWaitCond->wait( &m );

thread 11: mixer output

#0 0x00007f9e819350b9 in syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
#1 0x00007f9e81c9d9d5 in QBasicMutex::lockInternal() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#2 0x00007f9e81c9dcd3 in () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#3 0x000055fff382d536 in AutomationPattern::valueAt(TimePos const&) const ()
#4 0x000055fff38a525e in NotePlayHandle::processTimePos(TimePos const&) ()
#5 0x000055fff3a0d295 in InstrumentTrack::play(TimePos const&, short, int, int) ()
#6 0x000055fff383babd in BBTrackContainer::play(TimePos, short, int, int) ()
#7 0x000055fff3a08ec7 in BBTrack::play(TimePos const&, short, int, int) ()
#8 0x000055fff38d819f in Song::processNextBuffer() ()
#9 0x000055fff389df98 in Mixer::renderNextBuffer() ()
#10 0x000055fff389e494 in Mixer::fifoWriter::run() ()
...
- AutomationPattern::valueAt() is stuck at QMutexLocker m(&m_patternMutex);
- and there is no apparent reason why it shouldn't get the lock..
- so someone else must have it.. but the lock is private..
- and AutomationPattern isn't mentioned in any other thread backtrace..
- and the mutex is QMutex::Recursive, so thread 11 can lock it multiple times..
```
:man_shrugging:

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with AutomationPattern::valueAt() and its m_patternMutex, then trace the call paths shown through NotePlayHandle::processTimePos(), InstrumentTrack::play(), and Mixer::requestChangeInModel(). Compare those paths with MixerWorkerThread::run() and the supplied backtraces; done means identifying the lock owner and cause, or documenting why the deadlock cannot be reproduced.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
audio-video-rtc, desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.