Skip to content

(fix) avoid crash after removing Quick Link#14556

Merged
daschuer merged 13 commits intomixxxdj:2.5from
ronso0:quick-links-remove-crash-fix
Apr 25, 2025
Merged

(fix) avoid crash after removing Quick Link#14556
daschuer merged 13 commits intomixxxdj:2.5from
ronso0:quick-links-remove-crash-fix

Conversation

@ronso0
Copy link
Copy Markdown
Member

@ronso0 ronso0 commented Mar 27, 2025

workaround for #8270, happening with Qt 6.2.3

(... and other minor fixes for BrowseFeature, please see individual commits)

Apparently, the checks for item != nullptr and item->getData().isValid() don't suffice.
The crash happens afterwards with qDebug() << item->getLabel() << item->getData().toString(), so apparently the item has been removed in the meantime.

Simply don't call onLazyChildExpandation() after the context menu was closed and a Quick Link might have been removed.
Tbh I don't understand reasoning to expand the item after the menu closed in the first place. Regardless if the menu is used or closed right away, this is basically expand on right-click.

I removed those calls entirely, simply to avoid any further surprises.


Another bug I noticed while working on #14514 is that "Refresh directory tree" doesn't work for items other than first-level child items (and probably never did 😬 since #12908).
Fixed by using the last right-clicked index, too, as we do in other library features.

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Mar 27, 2025

I can split the commit into the actual workaround and other tweaks if desired.

edit Done.

@ronso0 ronso0 force-pushed the quick-links-remove-crash-fix branch from 3ed110b to 2107c57 Compare March 27, 2025 11:37
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (!(item && item->getData().isValid())) {
TreeItem* pItem = static_cast<TreeItem*>(index.internalPointer());
if (!(pItem && pItem->getData().isValid())) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Interestingly, if I add various qDebug() calls (log item data and label etc.), the item pointer seems to remain intact and the existing qDebug() call below doesn't crash anymore 🤷

Would the delete management of std::shared_ptr/unique_ptr instead of plain * keep the item intact?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have no clue what could be the reason. The internalPointer() is however a void* only without smart pointer capabilities. You get here only the address where the object has been when stored. There is no smart grantee, that the object TreeItem is still there. The pointer is only "borrowed", someone else need to take care that the object is kept valid.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Okay.
I removed the call as it was unnecessary in the first place, so I think this is ready.
We just need to make sure not to call it after/while modifying the tree.

@ronso0 ronso0 force-pushed the quick-links-remove-crash-fix branch from 0feb1f4 to 0c42129 Compare April 2, 2025 05:26
@ronso0 ronso0 force-pushed the quick-links-remove-crash-fix branch from 0c42129 to 2abde9b Compare April 2, 2025 08:57
@ronso0 ronso0 mentioned this pull request Apr 2, 2025
@ronso0 ronso0 force-pushed the quick-links-remove-crash-fix branch from 2abde9b to 848e0b8 Compare April 3, 2025 11:45
@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Apr 23, 2025

Can someone reprodue this bug or does it only happen with my outdated Qt 6.2.3?

If the former, shall I move the other (non-critical) fixes to another PR so this one has a better chance to get into 2.5.1?

@daschuer
Copy link
Copy Markdown
Member

I have already a review in progress.

Copy link
Copy Markdown
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.

For my understanding is that the root cause if the crash is the borrowed InternalPointer() in QModelIndex.

All later checks are pointless, because they succeed either (the pointer will never be null) or are also crashing because of call to the internal printer hidden behind the QT API.

The solution presented here to clear the stored value after using works only if it is actually used.
But there is no guarantee that such an action happens.

It would be better, if we effectively clear the pointer whenever the side bar tree structure is edited and there's a chance for a dangling pointer.

This seems to be more safe than all other not working checks.

I have not looked into a possible implementation but if you like, I can do a PR to your branch.

  1. Use only m_lastRightClickIndex
  2. Listen to a model changes we signal or such and reset the value
  3. Temove misleading checks (make them asserts)

What do you think?

Comment thread src/library/browse/browsefeature.h Outdated
@@ -77,6 +77,7 @@ class BrowseFeature : public LibraryFeature {
// Caution: Make sure this is reset whenever the library tree is updated,
// so that the internalPointer() does not become dangling
TreeItem* m_pLastRightClickedItem;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a "borrowed" pointer that becomes dangling if the tree item is removed.

Comment thread src/library/browse/browsefeature.h Outdated
// Caution: Make sure this is reset whenever the library tree is updated,
// so that the internalPointer() does not become dangling
TreeItem* m_pLastRightClickedItem;
QModelIndex m_pLastRightClickedIndex;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is the index, containing the borrowed pointer above as InternalPointer().

I think the name "InternalPointer is already a hint, that the it shall not be used externally.

But anyway with a bit of control we can.

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Apr 23, 2025

Thanks for taking a look!

I have not looked into a possible implementation but if you like, I can do a PR to your branch.

Yes, please. I think I get the idea, but I'm not sure about the QModelIndex <--> TreeItem relation.

IIUC the issue would be fixed if we'd listen to the tree model's signals rowsAboutToBe[Removed|Inserted|Moved] and clear the lastRightClickedItem/Index. Only if it is affected? (it is not if rows are added below, but that's currently cumbersome to figure out (it's a bit easier with TreeItem::childLevel() from #14508 but still..)

@daschuer
Copy link
Copy Markdown
Member

Here it is:
ronso0#92

Copy link
Copy Markdown
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.

LGTM, Thank you.

@daschuer daschuer merged commit bb2767a into mixxxdj:2.5 Apr 25, 2025
3 checks passed
@ronso0 ronso0 deleted the quick-links-remove-crash-fix branch April 26, 2025 00:45
connect(m_pSidebarModel,
&QAbstractItemModel::modelAboutToBeReset,
this,
&BrowseFeature::invalidateRightClickIndex);
Copy link
Copy Markdown
Member Author

@ronso0 ronso0 Sep 3, 2025

Choose a reason for hiding this comment

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

This breaks slotRefreshDirectoryTree() where we call
onLazyChildExpandation(m_lastRightClickedIndex);
TreeItemModel::insertTreeItemRows() has now an invalid index (invalid parent TreeItem) and inserts the new rows at row 0.

Apparently it's not required to reset the QModelIndex in that case since onLazyChildExpandation(index) works flawlessly even tough this also alters the model (below the index).

Possible fixes:

  1. revert this commit -- and test all cases that caused crashes earlier
    (see 1.12 removing a quick link can lead to crash #8270 / crash when deleting directory from Computer -> Quick Links #12817)
  2. in slotRefreshDirectoryTree(), copy m_lastRightClickedIndex and call onLazyChildExpandation(copy) -- works, no crash

@daschuer What do you think?

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.

1.12 removing a quick link can lead to crash

2 participants