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

Eject crash fix #1924

Merged
merged 27 commits into from
Dec 14, 2018
Merged

Eject crash fix #1924

merged 27 commits into from
Dec 14, 2018

Conversation

daschuer
Copy link
Member

This PRs should fix some crashes that may occur during switching or ejecting tracks.
https://bugs.launchpad.net/mixxx/+bug/1801874

The main fix is that we now that we obtain a strong reference to the track and beat pointers changed by the engine worker thread.
I have also included some related changes that was helpful to have a clear look on the code during development.

@daschuer daschuer added this to the 2.1.6 milestone Nov 28, 2018
@uklotzde
Copy link
Contributor

This looks like a minefield, completely broken by design 🙈


TapFilter m_tapFilter;

TrackPointer m_pTrack;
BeatsPointer m_pBeats;
TrackPointer m_pTrack; // is witten from an engine worker thread
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo. Use a single line comment for both elements. What about the other concurrently accessed members? We should at least group them together.

@uklotzde
Copy link
Contributor

OMG, what an ugly patch for evil code! Not your fault, @daschuer ;)

I didn't notice any obvious mistakes while quickly scrolling over all the changes.

@rryan
Copy link
Member

rryan commented Nov 29, 2018 via email

@uklotzde
Copy link
Contributor

Sorry for the comment, @rryan, it was not my intention to blame anyone!

Every project gets bigger with time and design decisions that were reasonable at the start might turn out as unhandy later. That's daily business, no matter how experienced you (think you) are. Primarily I struggle with the code that I have written myself in the past ;)

What I actually should have expressed is that @daschuer's fixes are very brittle and I'm uncomfortable with this code or coding style. But unfortunately we have no choice other than to mitigate the issues.

@mixxxdj mixxxdj deleted a comment Nov 30, 2018
@daschuer
Copy link
Member Author

daschuer commented Dec 1, 2018

OK, improved even more,

@mixxxdj mixxxdj deleted a comment Dec 1, 2018
@uklotzde
Copy link
Contributor

uklotzde commented Dec 2, 2018

I vote for 2.2 instead of master, because the threat is real and the successor of 2.2 is months away. Due to the conflicts I would like to avoid a fix for 2.1 that will go out of support soon.

@daschuer
Copy link
Member Author

daschuer commented Dec 2, 2018

Most changes in enginebuffer.cpp are white space changes due to the function extraction.
If you use Meld as diff tool, you can filter out these changes and it becomes less scary, hopefully.

Here is a eject crash report:
https://www.mixxx.org/forums/viewtopic.php?f=3&t=12218&p=40200&hilit=crash#p40200
But it is unclear if this PR will actually fix this.

I think the crash condition is not very likely for normal users, because you need to to use controls during ejecting the track. I can only imagine that this happens when using AUTO DJ, or some automation in scripts,

@daschuer
Copy link
Member Author

daschuer commented Dec 2, 2018

It only conflicts with some typo fixes when merging to 2.2. The conflict is displayed that big, because of surrounding white space changes.

m_sGroup(group) {
UserSettingsPointer pConfig)
: EngineControl(group, pConfig),
m_tapFilter(this, kFilterLength, kMaxInterval),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't hurt to explicitly initialize the atomic values. Here they are used on a higher level of abstraction. We should not rely on the implicit default values that are pure numbers without any meaning in the context of BPM control.

@@ -834,9 +841,9 @@ void BpmControl::setInstantaneousBpm(double instantaneousBpm) {

void BpmControl::resetSyncAdjustment() {
// Immediately edit the beat distance to reflect the new reality.
double new_distance = m_pThisBeatDistance->get() + m_dUserOffset;
double new_distance = m_pThisBeatDistance->get() + m_dUserOffset.getValue();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why getValue/setValue instead of fetchAndStore?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is ControlValueAtomic, which has no fetchAndStore().

// Note: dThisBeatLength is fractional frames count * 2 (stereo samples)
pGroupFeatures->beat_length_sec = dThisBeatLength / kSamplesPerFrame
/ m_pTrack->getSampleRate() / calcRateRatio();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might getTrackSampleRate() return 0.0 due to race conditions if the track has just been unloaded? We better should add a check for this.

Unrelated (or maybe related): Is calcRateRatio() guaranteed to always return a value that is strictly != 0 in any situation?

@@ -171,8 +171,7 @@ EngineBuffer::EngineBuffer(QString group, UserSettingsPointer pConfig,
m_pRepeat = new ControlPushButton(ConfigKey(m_group, "repeat"));
m_pRepeat->setButtonMode(ControlPushButton::TOGGLE);

// Sample rate
m_pSampleRate = new ControlProxy("[Master]", "samplerate", this);
m_pMasterSampleRate = new ControlProxy("[Master]", "samplerate", this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_pKeylockEngine is also bound to "[Master]" and doesn't contain a prefix in its name. Do we really need to add "Master" just for this member?

@@ -372,7 +371,7 @@ double EngineBuffer::getLocalBpm() {
}

void EngineBuffer::setEngineMaster(EngineMaster* pEngineMaster) {
foreach (EngineControl* pControl, m_engineControls) {
for (const auto& pControl: m_engineControls) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use qAsConst(m_engineControls) to avoid a copy.

for (QList<EngineControl*>::iterator it = m_engineControls.begin();
it != m_engineControls.end(); ++it) {
EngineControl *pControl = *it;
for (const auto& pControl: m_engineControls) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use qAsConst(m_engineControls) to avoid a copy.

TrackPointer pNewTrack, TrackPointer pOldTrack) {
// First inform engineControls directly
// Note: we are still in a worker thread.
for (const auto& pControl: m_engineControls) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qAsConst is missing

double getCurrentSample() const;
double getTotalSamples() const;
double getTrackSampleRate() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 3 accessors should be replaced by a single function that returns the whole thing atomically. I haven't check how many callers are affected.

@@ -11,6 +11,7 @@ TapFilter::~TapFilter() {
}

void TapFilter::tap() {
QMutexLocker locker(&m_mutex);
mixxx::Duration elapsed = m_timer.restart();
if (elapsed <= m_maxInterval) {
emit(tapped(m_mean.insert(elapsed.toDoubleMillis()), m_mean.size()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Emitting signals from an event loop thread while holding a lock may cause deadlocks.

@uklotzde
Copy link
Contributor

uklotzde commented Dec 8, 2018

TODO: Just some technical code fixes, optimizations, and clarifications.

The changes in EngineBuffer are hard to review. I'm silently assuming that the implementation hasn't changed substantially and only code has been reordered, reformatted, and updated.

Are we able to agree on a target? The threats are real and at least 2.2 should receive these fixes.

@mixxxdj mixxxdj deleted a comment Dec 12, 2018
@mixxxdj mixxxdj deleted a comment Dec 12, 2018
@daschuer
Copy link
Member Author

Ready!

@mixxxdj mixxxdj deleted a comment Dec 13, 2018
@uklotzde
Copy link
Contributor

LGTM.

A huge change set. Should we still merge this into 2.1?

@daschuer
Copy link
Member Author

This was getting bigger due to fixing the usefully review comments. We can go back to an earlier version with less changes. But that feels odd.

2.1 and 2.2 can considered as stable.
So if we decide for one, we should merge the other too.
Since this fixes a crasher, I think we should add it. We have many tests around this code. So I a confident to merge it.

@uklotzde
Copy link
Contributor

Excluding some of the changes is not an option. I'm confident that we don't introduce regressions, all changes are straightforward. Only the reformatting causes some noise.

Fixing 2.2 is mandatory, fixing 2.1 is optional.

@daschuer
Copy link
Member Author

OK, so let's merge it. Thank you for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants