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
Selective playlist name updates. Fixing Lp1837315 #2214
Conversation
…playlist table on every update. This fixes lp1837315.
src/library/baseplaylistfeature.cpp
Outdated
|
||
int row = 0; | ||
for (QList<QPair<int, QString> >::const_iterator it = m_playlistList.begin(); | ||
it != m_playlistList.end(); ++it, ++row) { | ||
int playlist_id = it->first; | ||
QString playlist_name = it->second; | ||
|
||
if (selected_id == playlist_id) { | ||
if (playlistId == playlist_id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same name in camel-case and snake case notation. Who should understand this code?
Modifying the database concurrently is not supported. Why should we fix undefined behaviour for just this single use case and ignore all others? Without a generic concept how to deal with this situation I don't see no value in such an attempt. Even worse it requires additional comments and introduces inconsistencies on the code level. |
src/library/playlistfeature.cpp
Outdated
break; | ||
} | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very strange way to fetch just the first element from the result set. It took me some time until I noticed that the outer loop is actually not a loop at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think it is my leak of knowledge how to use the Qt API correct in this case. I we haves such constructs in other places where we loop through a table even though we expect only one or no rows. How can I fix this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We expect at most a single row here. This can easily be expressed through the code.
DEBUG_ASSERT(playlistTableModel.rowCount() <= 1);
if (playlistTableModel.rowCount() > 0) {
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes that's easy. Can we also fix the while loop above?
src/library/playlistfeature.cpp
Outdated
for (auto it = m_playlistList.begin(); | ||
it != m_playlistList.end(); ++it) { | ||
if (it->first == playlist_id) { | ||
it->second = QString("%1 (%2) %3").arg(name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This formatting code is now duplicated.
src/library/playlistfeature.h
Outdated
void buildPlaylistList(); | ||
void decorateChild(TreeItem *pChild, int playlist_id); | ||
void buildPlaylistList() override; | ||
void updatePlaylistList(int playlist_id) override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what the difference between buildPlaylistList and updatePlaylistList is. Looking at their implementation both functions do fundamentally different things despite their very similar names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buildPlaylistList(), the original function clears the list of playlists m_playlistList and populates it again with fresh data from the database. This function was also used to refresh single tree view items, without refreshing the whole tree view with the recently fetched database data.
updatePlaylistList(int playlist_id) now only fetches the requested playlist and updates this in m_playlistList and GUI.
This way both lists are still consistent and immune against external changes, the root cause of the crash.
Should I rename updatePlaylistList(int playlist_id) to refreshPlaylistInPlaylistList((int playlist_id)?
Or do you have a better idea to rename both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole naming is confusing, i.e. "Playlist" vs. "List". But leave it as it is to keep the impact low.
refresh/reloadPlaylistInPlaylistList works for me.
List -> Listing, Summary, Overview, ... someone else should wrap their head around this.
src/library/setlogfeature.cpp
Outdated
break; | ||
} | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again a non-loop in disguise
Because of the way this bug happens to me. First it was just a crash that strikes me and I had no Idea why. It took me a while to figure out that this is related to two Mixxx instances. I often start accidantally two Mixxx instances. Since I know now why it crashed, I like to have that fixed.
I wanted to keep the scope of this original crash fix limited. The concept is: Mixxx is the a the single DB user but it should at least not crash in the exceptional case of two concurrent access. What are your ideas? |
OK, I hope it is better now. |
src/library/playlistfeature.cpp
Outdated
} | ||
} | ||
|
||
void PlaylistFeature::reloadPlaylistInPlaylistList(int playlist_id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use camel case for new code, that's what the style guide recommends.
src/library/playlistfeature.cpp
Outdated
|
||
DEBUG_ASSERT(playlistTableModel.rowCount() <= 1); | ||
if (playlistTableModel.rowCount() > 0) { | ||
for (auto it = m_playlistList.begin(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This loop now appears multiple times. Can't we extract it in a function named replacePlaylistLabel(id, label)? That would improve readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A better name for m_playlistList
would actually be m_playlistLabels
.
I noticed this only after your added the function for formatting the label. Before it wasn't obvious and hidden behind a pile of code. Inspecting the actual type in the header was just too far away.
And out of a sudden an appropriate naming appears ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buildPlaylistList
-> createPlaylistLabels()
reloadPlaylistInPlaylistList
-> updatePlaylistLabel(int playlistId)
How much effort would it be to change the naming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eclipse has a rename feature, so it should be no deal.
done |
src/library/baseplaylistfeature.cpp
Outdated
void BasePlaylistFeature::updateChildModel(int selected_id) { | ||
buildPlaylistList(); | ||
void BasePlaylistFeature::updateChildModel(int changedPlaylistId) { | ||
// the following call fetches whole m_playlistList. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comments is outdated and doesn't really help:
- m_playlistList
- How updatePlaylistLabel() actually works should not matter here
src/library/baseplaylistfeature.cpp
Outdated
void BasePlaylistFeature::updateChildModel(int changedPlaylistId) { | ||
// the following call fetches whole m_playlistList. | ||
// Anything may have change due to a concurrent Mixxx instance. | ||
updatePlaylistLabel(changedPlaylistId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter changedPlaylistId
is too verbose. This functions doesn't need to care what happened to the playlist before. It is instructed to update the tree item of a particular playlistId
and that's it.
The functions should actually be named updatePlaylistItem()
, because it updates the entire item and not only the label.
The code is still more complex than it needs to be:
QString updatedLabel = updatePlaylistLabel(playlistId);
TreeItem* playlistItem = findPlaylistItem(playlistId);
VERIFY_OR_DEBUG_ASSERT(playlistItem) {
return;
}
DEBUG_ASSERT(playlistItem->getData() == playlistId);
item->setLabel(updatedLabel);
decorateChild(playlistItem, playlistId);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is m_playlistLabels
actually needed if all this information is stored redundantly in the TreeItem
s?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See also: CrateFeature
and CrateSummary
, no redundant and error-prone extra bookkeeping needed.
OK, I have managed to dispose m_playlistLable finally. |
src/library/baseplaylistfeature.cpp
Outdated
void BasePlaylistFeature::updateChildModel(int selected_id) { | ||
buildPlaylistList(); | ||
void BasePlaylistFeature::updateChildModel(int playlistId) { | ||
QString playlistLable = fetchPlaylistLabel(playlistId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: Lable -> Label
Please check all occurrences
src/library/baseplaylistfeature.h
Outdated
virtual QModelIndex constructChildModel(int selected_id); | ||
virtual void updateChildModel(int selected_id); | ||
virtual void clearChildModel(); | ||
virtual void buildPlaylistList() = 0; | ||
virtual void decorateChild(TreeItem *pChild, int playlist_id) = 0; | ||
virtual void createPlaylistLabels(QList<IdAndLabel>* pPlaylistLabels) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The QList can be returned by-value
done |
src/library/baseplaylistfeature.cpp
Outdated
if (!pTreeItem->hasChildren()) { // leaf node | ||
bool ok; | ||
int playlistId = pTreeItem->getData().toInt(&ok); | ||
if (ok) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conversion should never fail: VERIFY_OR_DEBUG_ASSERT(ok) { continue; }
src/library/baseplaylistfeature.cpp
Outdated
it != m_playlistList.end(); ++it, ++row) { | ||
int playlist_id = it->first; | ||
QString playlist_name = it->second; | ||
for (auto it = playlistLabels.constBegin(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shorter and simpler variant:
for (const IdAndLabel& idAndLabel : createPlaylistLabels()) {
The actual container type doesn't matter for the iteration, so why depend on it.
I would even use const auto&&
here and let the compiler do all the type checking. But I know that you prefer old-school explicit type declarations ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But don't forget to the row++
in the loop body. That's the only drawback.
Patching an unsuitable design by piling up more code on top of it doesn't work out in the long term. What started as an attempt to fix a rare bug by adding more code has become a real improvement that should prevent future errors. That's the right approach! |
done |
Thank you for review. |
This fixes https://bugs.launchpad.net/mixxx/+bug/1837315
The bug only occurs in the rare case, if two instances of Mixxx are running on the same DB.