fix: possible crash in UBThumbnailScene#1241
Conversation
- fix access to still not loaded thumbnails - either check for nullptr - or load thumbnail before accessing
|
I don't know if related, and I didn't find a reproducible scenario, but I have a segfault error on the UBDocumentThumbnailsView::resizeEvent, on line 435 ( Maybe It's happening during tests on deletion, duplication of scenes, after moving them, etc. edit : So it's not related at all to a deleted scene, nor the issue you identified. Not fully reproducible, but I'm able to reproduce it by simply selecting a tuhmbnail of a document A, then a thumbnail of a document B. No need to go in Board Mode or to do some operations. So the issue is really related to mLastSelectedThumbnail which is probably pointing to a non existing thumbnail. |
Thanks for reporting. However, I could not find a scenario to reproduce it.
I don't want to do this. The thumbnails are owned by the
Do you need to do some resize operation after this to trigger the
Yes. I will look into this and ask: can we avoid storing a pointer to a Edit: The easiest way to invalidate this variable would be to set it to |
|
I added another commit to this PR superseding the previous one. I now introduced a new class This new class is a guarded pointer and not a shared pointer. Thumbnails are still owned and managed by the The advantage over the previous fix is that this does not require explicit invalidation. It now covers all, even unforeseen cases when a thumbnail could be deleted. Additionally the |
You're right for ownership, I should have written smart pointer instead of shared pointer. Also, a simple index of the last selected scene, and calling of
It's much simpler to store an index instead isn't it ? If we want to use smart pointers, can't we use weak_ptr for this case (shared_pointer but with no ownership) instead of creating a custom one ? https://en.cppreference.com/w/cpp/memory/weak_ptr I don't think it is a good idea to introduce this Please reconsider your last fix to use an index instead, unless you think it cannot solve the current issue.
Nope. Just clicking on a doc A with no PDF, click on thumbnail 1, click on document B containing a PDF -> crash (I'm on a Ubuntu machinw with Qt 6.7.2)
I'm not against the idea itself. I remember when C++11 was discussed, Loïc Joly wanted it to introduce a smart pointer to encapsulate raw pointers, as he considered it was a mistake to let devs maintaining their direct use of raw/unguarderd pointers as-is. That said, I don't think it's necessary to fix this issue |
At the situation causing the crash, the
No. The index changes when a page is added, moved or deleted in between. So we would have to be very careful whether the index is still valid when we use it later. This also does not cover the case when the page indicated by the index is deleted. And a
C++ smart pointers do not have a guarded pointer. And even a
As I said, Indexes do not solve the issue. They also do not carry the information to which document they belong. So if the document is changed, the index would still be the same.
Seems that resize is triggered internally by some means.
IMHO the alternative is to find ALL possibilities where the |
f85602c to
6e0a50a
Compare
UBDocumentThumbnailsView has a void UBDocumentThumbnailsView::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
if (document())
{
document()->thumbnailScene()->arrangeThumbnails();
if (mLastSelectedThumbnail)
{
//qDebug() << "mLastSelectedThumbnail" << mLastSelectedThumbnail;
ensureVisible(mLastSelectedThumbnail);
}
}
emit resized();
} |
|
I now improved the I still think some kind of smart pointer is the best idea, if you want to store and use a pointer owned and managed by somebody else. You need a safe way to know whether the object still exists. |
I should have been more precise: the The I analyzed the crash and here is what happens:
With the guarded pointer, the |
|
What I do not want to use to solve the problem:
|
But do we actually want that ? I think it's better that the view asks the scene for what thumbnail is the last one selected. So we architecturally avoid situations like that. When you look at mLastSelectedThumbnail use, it's for three things :
I don't see any good reason to store a pointer to the thumbnail in the view. What I think should be done (not tested, just to give an idea of what I try to say) : ensureVisible calls become if (document() && document()->thumbnailScene())
{
ensureVisible(document()->thumbnailScene()->lastSelectedThunbnail();
}for view's mouse and key events handling selection : UBThumbnail* sceneItem = dynamic_cast<UBThumbnail*>(itemAt(mMousePressPos));
//...
if (!sceneItem->isSelected())
{
selectItemAt(sceneItem->sceneIndex(), Qt::ControlModifier & event->modifiers());
}
void UBDocumentThumbnailsView::selectItemAt(int pIndex, bool extend)
{
document()->thumbnailScene()->selectItemAt(pIndex, extend); //rename highlightItem
ensureVisible(thumbnailScene->thumbnailAt(pIndex));
}and store int mLastSelectedThumbnailSceneIndex{0}
/*public*/ lastSelectedThunbnail()
{ //we can add safety checks of course
return thumbnailAt(mLastSelectedThumbnailSceneIndex);
}
/*private*/ setLastSelectedThumnail(index)
{
mLastSelectedThumbnailSceneIndex = index;
}
void UBThumbnailScene::hightlightItem(int index, bool only)
{
if (only)
{
for (auto item : selectedItems())
{
auto thumbnail = dynamic_cast<UBThumbnail*>(item);
if (thumbnail && thumbnail->sceneIndex() != index)
{
thumbnail->setSelected(false);
}
}
}
if (index >= 0 && index < mThumbnailItems.size())
{
thumbnailAt(index)->setSelected(true);
setLastSelectedThumnail(index);
}
}By giving the responsibility to the thumbnailScene to handle that, I don't think we can end in the selection of a wrong index. Of course what I wrote is probably not perfect, it's to illustrate my point.
I agree
I agree if we suppose the index is handled by the view, disagree as we can give the thumbnail scene the responsibility to handle that information |
Very good point!!! Yes, I agree. Let me see whether the scene always has the information which thumbnail was selected last, and if not, it should get that information. I will create another approach in this direction (and save my guarded pointer for some later usage, if required ;). |
6e0a50a to
6041e2e
Compare
|
Here the approach where management of the last selected thumbnail is moved to the In contrast to your sketch above I'm still storing the pointer to the thumbnail instead of the index as this moves automatically when thumbnails are added/removed/moved. I only have to invalidate this pointer when exactly that thumbnail is deleted. |
src/gui/UBDocumentThumbnailsView.cpp
Outdated
| if (document()) | ||
| { | ||
| document()->thumbnailScene()->arrangeThumbnails(); | ||
| auto tumbnailScene = document()->thumbnailScene(); |
- avoid pointers to thumbnails not owned by UBDocumentThumbnailsView to be stored in that class - move management of last selected thumbnail from UBDocumentThumbnailsView to UBThumbnailScene
6041e2e to
8e200ab
Compare
nullptrFixes letsfindaway#209