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

(fix) DlgTrackInfo: prevent wiping metadata when applying twice quickly #12965

Merged
merged 4 commits into from
Mar 28, 2024

Conversation

ronso0
Copy link
Member

@ronso0 ronso0 commented Mar 13, 2024

Not sure what exactly is the root cause, but preventing what should be a no-op in Track already in DlgTrackInfo seems to suffice. Can't reproduce anymore.

With my sparse basic c++ knowledge, I suspected the cause is that the track record has been moved to Track (is null in DlgTrackRecord?) when applying again before the Track::changed signal triggered DlgTrackInfo to fetch the new track record.
But tracing that turned out this is not it since now the changed signal is received before trying to save again (maybe it's just delays caused by the new m_pLoadedTrack->getRecord(), idk 🤷‍♂️ )

With symptoms fixed this good enough for 2.4.1 I guess.

Update:

As clarified by @uklotzde the issue was indeed passing the record and the beats to track via std::move.

Fixes #12963

@ronso0
Copy link
Member Author

ronso0 commented Mar 13, 2024

@zfhrp6 Please test this. Either build from source yoursel or pick a CI build (info is here https://github.com/mixxxdj/mixxx/wiki/Testing).

@ronso0 ronso0 changed the title DlgTrackInfo: prevent wiping metadata when applying twice quickly (fix) DlgTrackInfo: prevent wiping metadata when applying twice quickly Mar 14, 2024
@zfhrp6
Copy link

zfhrp6 commented Mar 14, 2024

@ronso0 I've tested the CI build (actions/runs/8273014776). The steps to repro in issue can't reproduce, and repeating Alt+A also can't. 👍

Comment on lines 624 to 625
// The dialog is updated and repopulated by the Track::changed() signal.
m_pLoadedTrack->replaceRecord(std::move(m_trackRecord), std::move(m_pBeatsClone));
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, my bad.

Suggested change
// The dialog is updated and repopulated by the Track::changed() signal.
m_pLoadedTrack->replaceRecord(std::move(m_trackRecord), std::move(m_pBeatsClone));
// Update the cached track
//
// If replaceRecord() returns true then both m_trackRecord and m_pBeatsClone
// will be updated by the subsequent Track::changed() signal to keep them
// synchronized with the track. Otherwise the track has not been modified and
// both members must remain valid. Do not use std::move() for passing arguments!
m_pLoadedTrack->replaceRecord(m_trackRecord, m_pBeatsClone);

Without warranty. Consider refactoring the API to prevent such misunderstandings in the future.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for jumping in!

Okay, so this is the actual fix.
IIUC the no-op test I added above just hides the issue because it's unlikely the record (in DlgTrackInfo) has changed in between two quick save() calls. Correct?

Copy link
Member Author

@ronso0 ronso0 Mar 15, 2024

Choose a reason for hiding this comment

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

Also, IIUC this

mixxx/src/track/track.cpp

Lines 308 to 310 in e2d4199

if (pOptionalBeats) {
bpmUpdatedFlag = trySetBeatsWhileLocked(std::move(pOptionalBeats));
if (recordUnchanged && !bpmUpdatedFlag) {
would already move the beats even if applying them fails and replaceRecord() returns false?

Copy link
Contributor

Choose a reason for hiding this comment

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

It only moves out of the argument which is passed by value. No side effects on the outer context.

@ronso0 ronso0 marked this pull request as draft March 15, 2024 09:17
@ronso0 ronso0 marked this pull request as ready for review March 15, 2024 09:55
Comment on lines 617 to 618
if (m_trackRecord == m_pLoadedTrack->getRecord() &&
m_pBeatsClone == m_pLoadedTrack->getBeats()) {
Copy link
Member Author

Choose a reason for hiding this comment

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

In case metadata or beats have been changed elsewhere we'd already have updated here as well, so this scheck is safe IMO (i.e. does not undo those changes by re-applying this dialog's (unchanged) record and beats).

Admittedly, since the update is triggered by signals there is a slight chance users revert their own manual changes, but I'd consider that a "user error" because working on metadata in multiple places at once (at inhuman high rate) may inevitably create incosistencies.

Any thoughts?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe an additional dirty flag is useful.
== + dirty -> reverted edit
!= + dirty -> actual edit
!= + !dirty -> outside edit
== + !dirty -> no edit

So we have a fast exit option in case !dirty

@ronso0
Copy link
Member Author

ronso0 commented Mar 28, 2024

Has someone time to review?

Comment on lines 617 to 618
if (m_trackRecord == m_pLoadedTrack->getRecord() &&
m_pBeatsClone == m_pLoadedTrack->getBeats()) {
Copy link
Member

Choose a reason for hiding this comment

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

m_trackRecord == m_pLoadedTrack->getRecord() is a deep compare, which should be OK, but slow.
m_pBeatsClone == m_pLoadedTrack->getBeats() is a pointer compare, which will probably always false.

replaceRecord() has already a good equality check inside. In terms of avoiding deep copies and compares optimal, I think we need to split replaceRecord(). But I am not sure if it is worth the efforts.
At least is should be renamed to compareAndReplaceRecord()

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll revert that commit. I don't want to complicate DlgTrackInfo with a dirty flag, this is just a compact, effecient bugfix after all.

The reason I considered this check in DlgTrackInfo is that Track::replaceRecord() does std::move(optionalBeats) too early (before it's clarified if beats can be applied), betas are moved later on in Track::setBeatsWhileLocked() only if applicable.
So beats might be moved even though they're not applied and Track::updated would not be emitted and we might get the same situation with the beats which this PR fixes for the other metadata.

Comment on lines 617 to 618
if (m_trackRecord == m_pLoadedTrack->getRecord() &&
m_pBeatsClone == m_pLoadedTrack->getBeats()) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe an additional dirty flag is useful.
== + dirty -> reverted edit
!= + dirty -> actual edit
!= + !dirty -> outside edit
== + !dirty -> no edit

So we have a fast exit option in case !dirty

Comment on lines 627 to 628
// both members must remain valid. Do not use std::move() for passing arguments!
// See https://github.com/mixxxdj/mixxx/issues/12963
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// both members must remain valid. Do not use std::move() for passing arguments!
// See https://github.com/mixxxdj/mixxx/issues/12963
// both members must remain valid, so not use std::move() for passing arguments!

I don't think that the reference to the bug will help much later.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, it describes the symptoms, but I can do that here as well.

Copy link
Member

Choose a reason for hiding this comment

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

I am already fine without it. Then let's keep it.
No need for extra work.

Copy link
Member

@daschuer daschuer left a comment

Choose a reason for hiding this comment

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

Thank you. LGTM

@daschuer daschuer merged commit fb16d84 into mixxxdj:2.4 Mar 28, 2024
14 checks passed
@ronso0 ronso0 deleted the dlgtrackinfo-prevent-wipe branch March 28, 2024 18:41
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.

5 participants