Otherwise, setLayout function might be called for
already destructed instance of ItemContainer,
leading to double reference counter decrement of m_layout QPointer,
eventually invalidating such pointers prematurely.
BUG: 417603
Otherwise, setLayout function might be called for
already destructed instance of ItemContainer,
leading to double reference counter decrement of m_layout QPointer,
eventually invalidating such pointers prematurely.
BUG: 417603
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
This function takes a plain pointer and wraps it into weak shared pointer.
That's QWeakPointer.
QPointer is something else that doesn't have an equivalent in stdlib.
It takes a QObject, then QObject has a special hook to unset watching QPointers to nullptr in QObject::~QObject()
I don't understand how this could fix anything.
If this is null before, then after this patch m_layout would just be dangling which is worse.
QPointer actually uses QWeakPointer to work:
https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qpointer.h#n59
https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qsharedpointer_impl.h#n309
https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qsharedpointer_impl.h#n450
https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qsharedpointer_impl.h#n569
And this used QWeakPointer leads to premature destruction of object m_layout points to.
I don't understand how this could fix anything.
If this is null before, then after this patch m_layout would just be dangling which is worse.
I've debugged this issue a bit more. Here's what I found.
Initial setup from gdb:
(gdb) break ItemContainer::setLayout Breakpoint 1 at 0x7fffba5e2ea0: file /usr/src/debug/plasma-workspace-5.18.1/components/containmentlayoutmanager/itemcontainer.cpp, line 177. (gdb) run --replace Thread 1 "plasmashell" hit Breakpoint 1, ItemContainer::setLayout (this=this@entry=0xc01f80, layout=0x0) at /usr/src/debug/plasma-workspace-5.18.1/components/containmentlayoutmanager/itemcontainer.cpp:177 177 { (gdb) c Continuing. Thread 1 "plasmashell" hit Breakpoint 1, ItemContainer::setLayout (this=0xc01f80, layout=0xbe0a60) at /usr/src/debug/plasma-workspace-5.18.1/components/containmentlayoutmanager/itemcontainer.cpp:177 177 { (gdb) n 178 if (m_layout == layout) { (gdb) n 198 if (parentItem() != layout) { (gdb) n 210 }); (gdb) print m_layout $1 = {wp = {d = 0xc01bc0, value = 0xbe0a60}} (gdb) print m_layout.wp.d $2 = (QWeakPointer<QObject>::Data *) 0xc01bc0 (gdb) print *m_layout.wp.d $3 = {weakref = {_q_value = {<std::__atomic_base<int>> = {static _S_alignment = 4, _M_i = 4}, <No data fields>}}, strongref = {_q_value = {<std::__atomic_base<int>> = {static _S_alignment = 4, _M_i = -1}, <No data fields>}}, destroyer = 0x7ffff57ada40 <main_arena+96>} (gdb) watch ((QWeakPointer<QObject>::Data *) 0xc01bc0)->weakref._q_value._M_i Hardware watchpoint 2: ((QWeakPointer<QObject>::Data *) 0xc01bc0)->weakref._q_value._M_i (gdb) watch ((QWeakPointer<QObject>::Data *) 0xc01bc0)->strongref._q_value._M_i Hardware watchpoint 3: ((QWeakPointer<QObject>::Data *) 0xc01bc0)->strongref._q_value._M_i (gdb) break QObject::~QObject if (this == 0xbe0a60) Breakpoint 4 at 0x41c3e0 (75 locations) (gdb) c Continuing.
I've added some widgets like described in test plan. Now I'm removing one of widgets:
Thread 1 "plasmashell" hit Hardware watchpoint 2: ((QWeakPointer<QObject>::Data *) 0xc01bc0)->weakref._q_value._M_i Old value = 5 New value = 4 0x00007fffba5e4445 in QWeakPointer<QObject>::~QWeakPointer (this=0x10c2fc0, __in_chrg=<optimized out>) at /usr/include/c++/9/bits/atomic_base.h:326 326 operator--() noexcept (gdb) bt #0 0x00007fffba5e4445 in QWeakPointer<QObject>::~QWeakPointer (this=0x10c2fc0, __in_chrg=<optimized out>) at /usr/include/c++/9/bits/atomic_base.h:326 #1 QPointer<AppletsLayout>::~QPointer (this=0x10c2fc0, __in_chrg=<optimized out>) at /usr/include/qt5/QtCore/qpointer.h:53 #2 ItemContainer::~ItemContainer (this=0x10c2f60, __in_chrg=<optimized out>) at /usr/src/debug/plasma-workspace-5.18.1/components/containmentlayoutmanager/itemcontainer.cpp:64 #3 0x00007fffba5d30a5 in QQmlPrivate::QQmlElement<AppletContainer>::~QQmlElement (this=0x10c2f60, __in_chrg=<optimized out>) at /usr/include/qt5/QtQml/qqmlprivate.h:106 #4 QQmlPrivate::QQmlElement<AppletContainer>::~QQmlElement (this=0x10c2f60, __in_chrg=<optimized out>) at /usr/include/qt5/QtQml/qqmlprivate.h:108 #5 0x00007ffff5ce2380 in QObject::event (this=this@entry=0x10c2f60, e=e@entry=0x2b76cc0) at kernel/qobject.cpp:1252 #6 0x00007ffff77c2ae3 in QQuickItem::event (this=0x10c2f60, ev=0x2b76cc0) at items/qquickitem.cpp:8105 #7 0x00007ffff6711c42 in QApplicationPrivate::notify_helper (this=this@entry=0x4c14c0, receiver=receiver@entry=0x10c2f60, e=e@entry=0x2b76cc0) at kernel/qapplication.cpp:3700 #8 0x00007ffff671b1c0 in QApplication::notify (this=0x7fffffffd730, receiver=0x10c2f60, e=0x2b76cc0) at kernel/qapplication.cpp:3446 #9 0x00007ffff5cb7212 in QCoreApplication::notifyInternal2 (receiver=0x10c2f60, event=0x2b76cc0) at ../../src/corelib/kernel/qobject.h:142 #10 0x00007ffff5cb9e08 in QCoreApplicationPrivate::sendPostedEvents (receiver=0x0, event_type=0, data=0x4b6330) at kernel/qcoreapplication.cpp:1825 #11 0x00007ffff5d0d963 in postEventSourceDispatch (s=0x51a7c0) at kernel/qeventdispatcher_glib.cpp:276 #12 0x00007ffff3b86c6d in g_main_dispatch (context=0x7fffe8005010) at ../glib/gmain.c:3179 #13 g_main_context_dispatch (context=context@entry=0x7fffe8005010) at ../glib/gmain.c:3844 #14 0x00007ffff3b86ef0 in g_main_context_iterate (context=context@entry=0x7fffe8005010, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3917 #15 0x00007ffff3b86f7f in g_main_context_iteration (context=0x7fffe8005010, may_block=may_block@entry=1) at ../glib/gmain.c:3978 #16 0x00007ffff5d0cfa1 in QEventDispatcherGlib::processEvents (this=0x51b110, flags=...) at kernel/qeventdispatcher_glib.cpp:422 #17 0x00007ffff5cb5e9b in QEventLoop::exec (this=this@entry=0x7fffffffd5f0, flags=..., flags@entry=...) at ../../src/corelib/global/qflags.h:140 #18 0x00007ffff5cbd942 in QCoreApplication::exec () at ../../src/corelib/global/qflags.h:120 #19 0x00007ffff608ecbc in QGuiApplication::exec () at kernel/qguiapplication.cpp:1784 #20 0x00007ffff6711bb5 in QApplication::exec () at kernel/qapplication.cpp:2856 #21 0x000000000041f4f1 in main (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/plasma-workspace-5.18.1/shell/main.cpp:228 (gdb) c Continuing. Thread 1 "plasmashell" hit Breakpoint 1, ItemContainer::setLayout (this=0x10c2f60, layout=0x0) at /usr/src/debug/plasma-workspace-5.18.1/components/containmentlayoutmanager/itemcontainer.cpp:177 177 { (gdb) bt #0 ItemContainer::setLayout (this=0x10c2f60, layout=0x0) at /usr/src/debug/plasma-workspace-5.18.1/components/containmentlayoutmanager/itemcontainer.cpp:177 #1 0x00007ffff5ce1c98 in QtPrivate::QSlotObjectBase::call (a=0x7fffffffcee0, r=0x10c2f60, this=0x2dcf4b0) at ../../src/corelib/kernel/qobjectdefs_impl.h:394 #2 QMetaObject::activate (sender=0x10c2f60, signalOffset=<optimized out>, local_signal_index=<optimized out>, argv=<optimized out>) at kernel/qobject.cpp:3784 #3 0x00007ffff77aedb2 in QQuickItem::parentChanged (this=this@entry=0x10c2f60, _t1=<optimized out>) at .moc/moc_qquickitem.cpp:1100 #4 0x00007ffff77c3fdd in QQuickItem::setParentItem (this=this@entry=0x10c2f60, parentItem=parentItem@entry=0x0) at items/qquickitem.cpp:2808 #5 0x00007ffff77c4c38 in QQuickItem::~QQuickItem (this=0x10c2f60, __in_chrg=<optimized out>) at items/qquickitem.cpp:2390 #6 0x00007fffba5d30a5 in QQmlPrivate::QQmlElement<AppletContainer>::~QQmlElement (this=0x10c2f60, __in_chrg=<optimized out>) at /usr/include/qt5/QtQml/qqmlprivate.h:106 #7 QQmlPrivate::QQmlElement<AppletContainer>::~QQmlElement (this=0x10c2f60, __in_chrg=<optimized out>) at /usr/include/qt5/QtQml/qqmlprivate.h:108 #8 0x00007ffff5ce2380 in QObject::event (this=this@entry=0x10c2f60, e=e@entry=0x2b76cc0) at kernel/qobject.cpp:1252 #9 0x00007ffff77c2ae3 in QQuickItem::event (this=0x10c2f60, ev=0x2b76cc0) at items/qquickitem.cpp:8105 #10 0x00007ffff6711c42 in QApplicationPrivate::notify_helper (this=this@entry=0x4c14c0, receiver=receiver@entry=0x10c2f60, e=e@entry=0x2b76cc0) at kernel/qapplication.cpp:3700 #11 0x00007ffff671b1c0 in QApplication::notify (this=0x7fffffffd730, receiver=0x10c2f60, e=0x2b76cc0) at kernel/qapplication.cpp:3446 #12 0x00007ffff5cb7212 in QCoreApplication::notifyInternal2 (receiver=0x10c2f60, event=0x2b76cc0) at ../../src/corelib/kernel/qobject.h:142 #13 0x00007ffff5cb9e08 in QCoreApplicationPrivate::sendPostedEvents (receiver=0x0, event_type=0, data=0x4b6330) at kernel/qcoreapplication.cpp:1825 #14 0x00007ffff5d0d963 in postEventSourceDispatch (s=0x51a7c0) at kernel/qeventdispatcher_glib.cpp:276 #15 0x00007ffff3b86c6d in g_main_dispatch (context=0x7fffe8005010) at ../glib/gmain.c:3179 #16 g_main_context_dispatch (context=context@entry=0x7fffe8005010) at ../glib/gmain.c:3844 #17 0x00007ffff3b86ef0 in g_main_context_iterate (context=context@entry=0x7fffe8005010, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3917 #18 0x00007ffff3b86f7f in g_main_context_iteration (context=0x7fffe8005010, may_block=may_block@entry=1) at ../glib/gmain.c:3978 #19 0x00007ffff5d0cfa1 in QEventDispatcherGlib::processEvents (this=0x51b110, flags=...) at kernel/qeventdispatcher_glib.cpp:422 #20 0x00007ffff5cb5e9b in QEventLoop::exec (this=this@entry=0x7fffffffd5f0, flags=..., flags@entry=...) at ../../src/corelib/global/qflags.h:140 #21 0x00007ffff5cbd942 in QCoreApplication::exec () at ../../src/corelib/global/qflags.h:120 #22 0x00007ffff608ecbc in QGuiApplication::exec () at kernel/qguiapplication.cpp:1784 #23 0x00007ffff6711bb5 in QApplication::exec () at kernel/qapplication.cpp:2856 #24 0x000000000041f4f1 in main (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/plasma-workspace-5.18.1/shell/main.cpp:228 (gdb) c Continuing. Thread 1 "plasmashell" hit Hardware watchpoint 2: ((QWeakPointer<QObject>::Data *) 0xc01bc0)->weakref._q_value._M_i Old value = 4 New value = 3 0x00007fffba5e2fd7 in QWeakPointer<QObject>::~QWeakPointer (this=<synthetic pointer>, __in_chrg=<optimized out>) at /usr/include/c++/9/bits/atomic_base.h:326 326 operator--() noexcept (gdb) bt #0 0x00007fffba5e2fd7 in QWeakPointer<QObject>::~QWeakPointer (this=<synthetic pointer>, __in_chrg=<optimized out>) at /usr/include/c++/9/bits/atomic_base.h:326 #1 QWeakPointer<QObject>::operator= (other=..., other=..., this=0x10c2fc0) at /usr/include/qt5/QtCore/qsharedpointer_impl.h:599 #2 QWeakPointer<QObject>::assign<QObject> (ptr=0x0, this=0x10c2fc0) at /usr/include/qt5/QtCore/qsharedpointer_impl.h:684 #3 QPointer<AppletsLayout>::operator= (p=0x0, this=0x10c2fc0) at /usr/include/qt5/QtCore/qpointer.h:83 #4 ItemContainer::setLayout (this=0x10c2f60, layout=0x0) at /usr/src/debug/plasma-workspace-5.18.1/components/containmentlayoutmanager/itemcontainer.cpp:191 #5 0x00007ffff5ce1c98 in QtPrivate::QSlotObjectBase::call (a=0x7fffffffcee0, r=0x10c2f60, this=0x2dcf4b0) at ../../src/corelib/kernel/qobjectdefs_impl.h:394 #6 QMetaObject::activate (sender=0x10c2f60, signalOffset=<optimized out>, local_signal_index=<optimized out>, argv=<optimized out>) at kernel/qobject.cpp:3784 #7 0x00007ffff77aedb2 in QQuickItem::parentChanged (this=this@entry=0x10c2f60, _t1=<optimized out>) at .moc/moc_qquickitem.cpp:1100 #8 0x00007ffff77c3fdd in QQuickItem::setParentItem (this=this@entry=0x10c2f60, parentItem=parentItem@entry=0x0) at items/qquickitem.cpp:2808 #9 0x00007ffff77c4c38 in QQuickItem::~QQuickItem (this=0x10c2f60, __in_chrg=<optimized out>) at items/qquickitem.cpp:2390 #10 0x00007fffba5d30a5 in QQmlPrivate::QQmlElement<AppletContainer>::~QQmlElement (this=0x10c2f60, __in_chrg=<optimized out>) at /usr/include/qt5/QtQml/qqmlprivate.h:106 #11 QQmlPrivate::QQmlElement<AppletContainer>::~QQmlElement (this=0x10c2f60, __in_chrg=<optimized out>) at /usr/include/qt5/QtQml/qqmlprivate.h:108 #12 0x00007ffff5ce2380 in QObject::event (this=this@entry=0x10c2f60, e=e@entry=0x2b76cc0) at kernel/qobject.cpp:1252 #13 0x00007ffff77c2ae3 in QQuickItem::event (this=0x10c2f60, ev=0x2b76cc0) at items/qquickitem.cpp:8105 #14 0x00007ffff6711c42 in QApplicationPrivate::notify_helper (this=this@entry=0x4c14c0, receiver=receiver@entry=0x10c2f60, e=e@entry=0x2b76cc0) at kernel/qapplication.cpp:3700 #15 0x00007ffff671b1c0 in QApplication::notify (this=0x7fffffffd730, receiver=0x10c2f60, e=0x2b76cc0) at kernel/qapplication.cpp:3446 #16 0x00007ffff5cb7212 in QCoreApplication::notifyInternal2 (receiver=0x10c2f60, event=0x2b76cc0) at ../../src/corelib/kernel/qobject.h:142 #17 0x00007ffff5cb9e08 in QCoreApplicationPrivate::sendPostedEvents (receiver=0x0, event_type=0, data=0x4b6330) at kernel/qcoreapplication.cpp:1825 #18 0x00007ffff5d0d963 in postEventSourceDispatch (s=0x51a7c0) at kernel/qeventdispatcher_glib.cpp:276 #19 0x00007ffff3b86c6d in g_main_dispatch (context=0x7fffe8005010) at ../glib/gmain.c:3179 #20 g_main_context_dispatch (context=context@entry=0x7fffe8005010) at ../glib/gmain.c:3844 #21 0x00007ffff3b86ef0 in g_main_context_iterate (context=context@entry=0x7fffe8005010, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3917 #22 0x00007ffff3b86f7f in g_main_context_iteration (context=0x7fffe8005010, may_block=may_block@entry=1) at ../glib/gmain.c:3978 #23 0x00007ffff5d0cfa1 in QEventDispatcherGlib::processEvents (this=0x51b110, flags=...) at kernel/qeventdispatcher_glib.cpp:422 #24 0x00007ffff5cb5e9b in QEventLoop::exec (this=this@entry=0x7fffffffd5f0, flags=..., flags@entry=...) at ../../src/corelib/global/qflags.h:140 #25 0x00007ffff5cbd942 in QCoreApplication::exec () at ../../src/corelib/global/qflags.h:120 #26 0x00007ffff608ecbc in QGuiApplication::exec () at kernel/qguiapplication.cpp:1784 #27 0x00007ffff6711bb5 in QApplication::exec () at kernel/qapplication.cpp:2856 #28 0x000000000041f4f1 in main (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/plasma-workspace-5.18.1/shell/main.cpp:228 (gdb) c
As you may see, due to m_layout being a QPointer, which contains QWeakPointer, reference counter is decreased twice. If there is no QPointer, there is no double decrement and no issue. And m_layout doesn't become dangling pointer since object m_layout points to is created in qml and is not destroyed until desktop view is destroyed.
Thus, while this fix is not perfect, it definitely fixes current issue.
But looking into it closely after additional investigation, I think it might be fixed using another approach: signals have to be disconnected so that signal to already deconstructed object wouldn't be called from it's parent class. I'll upload updated version.
Fix consists of following line:
disconnect(this, &QQuickItem::parentChanged, this, nullptr);
Two other disconnects are just in case of similar issues arising and for code consistency.
components/containmentlayoutmanager/itemcontainer.cpp | ||
---|---|---|
66 | You can translate this calls to |
And this used QWeakPointer leads to premature destruction of object m_layout points to.
Do you have commit access?
components/containmentlayoutmanager/itemcontainer.cpp | ||
---|---|---|
68 | Right, this one line makes a lot of sense. I can imagine QQuickItem::~QuickItem changes parent, and calling ItemContainer::setLayout after we've run our ItemContainers destructor means all bets are completely off. I can believe an already destroyed QPointer behaves weirdly. Lets not change the other two. There's no chance of an event loop processing a timer event after ItemContainer:::~ItemContainer before we're auto disconnected. |
Yep, original description was not entirely correct.
No, AFAIK I don't have commit access.
components/containmentlayoutmanager/itemcontainer.cpp | ||
---|---|---|
68 | Should I remove these 2 disconnects or leave them as they are? |
No, AFAIK I don't have commit access.
Ok, well I hope this is the first of many patches. I like working with people who can use gdb!
Good stuff.
components/containmentlayoutmanager/itemcontainer.cpp | ||
---|---|---|
68 | Lets get rid of them. Then it helps self-document that this the one left is doing something explicitly for a good reason. |
No, AFAIK I don't have commit access.
Can I have your full name + email address please.