Global static QPixmapCache in Qt internals
-
[Forked from https://forum.qt.io/topic/126128/qapplication-in-std-thread -- Sorry, I messed up the order of posts when I did the fork; this top post should be further down]
@kshegunov said in QApplication in std::thread:
why would this generate a crash?
Since the widget creates a timer in the background, then it's probably receiving background events/signals.
delete
could cause the event/signal handler to act on a dangling pointer -- that's how it could generate a crash. And that's whydeleteLater()
can fix the crash. (Alternatively, we could change from a heap-allocated widget to a stack-allocated widget)I'm pretty sure deferred deletes are processed after the event loop has exited (see for example the
QThread::finished
->QObject::deleteLater
)The code below prints "Bye" but not "Widget destroyed", proving that the Widget's destructor is not run:
// widget.h #include <QWidget> class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr) : QWidget(parent) {} ~Widget(){ qDebug("Widget destroyed"); } }; // main.cpp #include "widget.h" #include <QApplication> #include <chrono> #include <thread> using namespace std::chrono_literals; int main(int argc, char *argv[]) { std::thread thr([&] { QApplication a(argc, argv); auto w = new Widget; w->show(); a.exec(); // delete w; // This WILL run the Widget's destructor w->deleteLater(); // This WON'T run the Widget's destructor }); std::this_thread::sleep_for(5000ms); QMetaObject::invokeMethod(qApp, &QApplication::quit); thr.join(); std::this_thread::sleep_for(5000ms); qDebug("Bye"); }
Anyway, I was trying to say that @Suthiro can't simply replace
delete m_qPlotsControl;
withm_qPlotsControl->deleteLater();
in-place at https://stackoverflow.com/questions/67257329/weird-qpushbutton-bug . Some re-shuffling is required.
-
@kshegunov said in QApplication in std::thread:
I meant the trace from
QT_FATAL_WARNINGS
, so we could see what was called leading to the mentioned warning.Nothing useful from GDB:
1 qt_message_fatal(QtMsgType, QMessageLogContext const&, QString const&) [clone .isra.4] 0x68e23630 2 ?? 0x3121a30
@JKSH said in QApplication in std::thread:
When main() is about to return, the program tries to unload the timer. But the unloading is done by the main() thread, not the Qt thread. Hence the complaint, "QObject::~QObject: Timers cannot be stopped from another thread".
I really doubt this as the runtime doesn't run your destructors automatically.
I don't have a better explanation for this. Do you?:
#include <QApplication> #include <QPushButton> #include <QDebug> #include <chrono> #include <thread> int main(int argc, char *argv[]) { std::thread thr([&] { QApplication a(argc, argv); QPushButton btn("Push"); btn.show(); a.exec(); qDebug("Event loop stopped"); }); // Play with the QPushButton within the next 5 seconds using namespace std::chrono_literals; std::this_thread::sleep_for(5000ms); QMetaObject::invokeMethod(qApp, &QApplication::quit); thr.join(); qDebug("Thread stopped"); std::this_thread::sleep_for(5000ms); qDebug("Waiting, waiting..."); std::this_thread::sleep_for(5000ms); qDebug("Program shutting down"); }
The output sequence is:
- (Pause 5s)
- "Event loop stopped"
- "Thread stopped"
- (Pause 5s)
- "Waiting, waiting..."
- (Pause 5s)
- "Program shutting down"
- "QObject::~QObject: Timers cannot be stopped from another thread"
If you don't interact with the button, then #8 doesn't appear. No difference if qDebug is switched to std::cout with flush.
In any case if that is the case, then it's a bug in the push button, which should be posted on the tracker.
QPushButton's timers aren't alone.
For example, the global QPixmapCache object hangs around after the destruction of QApplication too.
-
@JKSH said in QApplication in std::thread:
@kshegunov said in QApplication in std::thread:
I meant the trace from
QT_FATAL_WARNINGS
, so we could see what was called leading to the mentioned warning.Nothing useful from GDB:
1 qt_message_fatal(QtMsgType, QMessageLogContext const&, QString const&) [clone .isra.4] 0x68e23630 2 ?? 0x3121a30
Did you run this with a release Qt or with
-developer-build
? :)
(The address is rather odd, though)@JKSH said in QApplication in std::thread:
If you don't interact with the button, then #8 doesn't appear. No difference if qDebug is switched to std::cout with flush.
Can't reproduce:
09:01:47: Debugging starts Event loop stopped Thread stopped Waiting, waiting... Program shutting down
What's your kit?
I have 5.15.2 from the repo with gcc 10.2.1.
-
@JKSH this actually crashes for me on btn.show()
1 __pthread_kill (x86_64) /usr/lib/system/libsystem_kernel.dylib 0x7fff2030d462 2 pthread_kill (x86_64) /usr/lib/system/libsystem_pthread.dylib 0x7fff2033b610 3 abort (x86_64) /usr/lib/system/libsystem_c.dylib 0x7fff2028e720 4 abort_message (x86_64) /usr/lib/libc++abi.dylib 0x7fff20300418 5 demangling_terminate_handler() (x86_64) /usr/lib/libc++abi.dylib 0x7fff202f1a7d 6 _objc_terminate() (x86_64h) /usr/lib/libobjc.A.dylib 0x7fff201e79b1 7 std::__terminate(void ( *)()) (x86_64) /usr/lib/libc++abi.dylib 0x7fff202ff847 8 __cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception *) (x86_64) /usr/lib/libc++abi.dylib 0x7fff20302167 9 __cxa_throw (x86_64) /usr/lib/libc++abi.dylib 0x7fff2030212e 10 objc_exception_throw (x86_64h) /usr/lib/libobjc.A.dylib 0x7fff201e54f7 11 -[NSException raise] (x86_64h) /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation 0x7fff204d5a9a 12 -[NSWindow(NSWindow_Theme) _postWindowNeedsToResetDragMarginsUnlessPostingDisabled] (x86_64) /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit 0x7fff22c7597d 13 -[NSWindow _initContent:styleMask:backing:defer:contentView:] (x86_64) /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit 0x7fff22c606d5 14 -[NSWindow initWithContentRect:styleMask:backing:defer:] (x86_64) /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit 0x7fff22c6016f 15 -[NSWindow initWithContentRect:styleMask:backing:defer:screen:] (x86_64) /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit 0x7fff22f6a2c8 16 -[QNSWindow initWithContentRect:styleMask:backing:defer:screen:platformWindow:] (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/plugins/platforms/libqcocoa.dylib 0x10b335725 17 QCocoaWindow::createNSWindow(bool) (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/plugins/platforms/libqcocoa.dylib 0x10b322cac 18 QCocoaWindow::recreateWindowIfNeeded() (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/plugins/platforms/libqcocoa.dylib 0x10b31c672 19 QCocoaWindow::initialize() (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/plugins/platforms/libqcocoa.dylib 0x10b31c0fe 20 QWindowPrivate::create(bool, unsigned long long) (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/lib/QtGui.framework/Versions/5/QtGui 0x1007aceb7 21 QWidgetPrivate::create() (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets 0x10016c771 22 QWidget::create(unsigned long long, bool, bool) (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets 0x10016b414 23 QWidgetPrivate::setVisible(bool) (x86_64) /Users/jonashilk/Qt/5.15.2/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets 0x10017e81d 24 main::$_0::operator()() const main.cpp 13 0x100006538 25 decltype(std::forward<main::$_0>(fp)()) std::__invoke<main::$_0>(main::$_0&&) type_traits 3545 0x10000647d 26 void std::__thread_execute<std::unique_ptr<std::__thread_struct, std::default_delete<std::__thread_struct>>, main::$_0>(std::tuple<std::unique_ptr<std::__thread_struct, std::default_delete<std::__thread_struct>>, main::$_0>&, std::__tuple_indices<>) thread 273 0x1000063e5 27 void * std::__thread_proxy<std::tuple<std::unique_ptr<std::__thread_struct, std::default_delete<std::__thread_struct>>, main::$_0>>(void *) thread 284 0x100005c16 28 _pthread_start (x86_64) /usr/lib/system/libsystem_pthread.dylib 0x7fff2033b950 29 thread_start (x86_64) /usr/lib/system/libsystem_pthread.dylib 0x7fff2033747b
-
@J-Hilk said in QApplication in std::thread:
@JKSH this actually crashes for me on btn.show()
That's expected. MacOS does not support plugging into the event loop from a thread different from
main()
. Should work on linux and windows, though.
-
@kshegunov said in QApplication in std::thread:
Did you run this with a release Qt or with
-developer-build
? :)
(The address is rather odd, though)Ah, right... I thought the Debug build would suffice. Don't have a dev build right now.
Can't reproduce:
...
What's your kit?
I have 5.15.2 from the repo with gcc 10.2.1.Same result for me on Windows 10 20H2, with all official builds of:
- Qt 5.12.10 MSVC 2017 64-bit (using MSVC 2019 compiler)
- Qt 5.14.2 MinGW 7.3.0 32-bit
- Qt 5.15.2 MSVC 2019 64-bit
Might be Windows-specific.
-
@JKSH said in QApplication in std::thread:
Ah, right... I thought the Debug build would suffice. Don't have a dev build right now.
Not once in a universe lifetime.
@JKSH said in QApplication in std::thread:
Might be Windows-specific.
Possibly. Unfortunately I don't have windows on hand (or rather I'm too lazy now to boot into it). Maybe, I'll check it later.
-
@kshegunov Apple does a lot of funky stuff, in that regard, let me fire up my vm
-
@kshegunov said in QApplication in std::thread:
Not once in a universe lifetime.
Duly noted :-D
But before we get too sidetracked: @Suthiro's main issue isn't the message about timers. Rather, it's the crash which I believe is caused by
delete m_qPlotsControl;
(see https://stackoverflow.com/questions/67257329/weird-qpushbutton-bug ).I believe it is fixable with
m_qPlotsControl->deleteLater();
... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.
-
@JKSH Ok I can reproduce it as well, (on the VM) Qt 15.3 & MSVC2019
-
@JKSH said in QApplication in std::thread:
I believe it is fixable with m_qPlotsControl->deleteLater();... but that needs to be done before QApplication::exec() returns, because QDeferredDeleteEvent won't be processed without an event loop.
I still don't understand this. For what reason should that be the case, and why would this generate a crash? I'm pretty sure deferred deletes are processed after the event loop has exited (see for example the
QThread::finished
->QObject::deleteLater
), but even if that weren't the case theQPushButton
's destructor is going to be run before the thread actually finishes and before the application object's destructor's run (there's no difference betweenmain()
and any other function from c++'s point of view).
-
@JKSH said in QApplication in std::thread:
The code below prints "Bye" but not "Widget destroyed", proving that the Widget's destructor is not run:
I meant connecting to the
aboutToQuit
signal, but anyhow it's not that important.Since the widget creates a timer in the background, then it's probably receiving background events/signals. delete could cause the event/signal handler to act on a dangling pointer -- that's how it could generate a crash.
I think this shouldn't happen, but can't investigate it currently.
Anyway, I was trying to say that @Suthiro can't simply replace delete m_qPlotsControl; with m_qPlotsControl->deleteLater(); in-place at https://stackoverflow.com/questions/67257329/weird-qpushbutton-bug . Some re-shuffling is required.
I suppose, but that timer message should go away, otherwise the code can break at any moment (this applies to your code too - the one you mentioned where you get the warning).
-
I reproduced this on Windows and the message about timers comes from a destructor of a static QPixmapCache object that happens on the main() thread. That's not something the user's code does. It's in Qt's internals.
Right, so remember the rule that no QObject should outlive the application object and this means no static QObjects? Apparently Qt itself doesn't respect that rule and also creates static objects that run code in the main thread no matter where the application object is created :/
I would consider this a Qt bug and expect it to crash all over the place.
EDIT: I missed that JKSH already mentioned this, but yeah, this seems to be the culprit.
-
@Chris-Kawa said in QApplication in std::thread:
I reproduced this on Windows and the message about timers comes from a destructor of a static QPixmapCache object that happens on the main() thread. That's not something the user's code does. It's in Qt's internals.
Hm, interesting. Do you have a woboq location?
Right, so remember the rule that no QObject should outlive the application object and this means no static QObjects?
Apparently Qt itself doesn't respect that rule and also creates static objects that run code in the main thread no matter where the application object is created :/Yeah. Not living up to its own expectations it seems. :(
I would consider this a Qt bug and expect it to crash all over the place.
Me too, which is what I wrote somewhere upstairs - this should be fixed at the vendor side.
-
@kshegunov said in QApplication in std::thread:
Hm, interesting. Do you have a woboq location?
Instance declaration is here:
https://code.woboq.org/qt5/qtbase/src/gui/image/qpixmapcache.cpp.html#pm_cacheAnd here's Windows with MSVC crash callstack on exit:
1 QPMCache::~QPMCache qpixmapcache.cpp 269 2 ``anonymous namespace'::Q_QGS_pm_cache::innerFunction'::`2'::Holder::~Holder Qt5Guid 3 ``anonymous namespace'::Q_QGS_pm_cache::innerFunction'::`2'::`dynamic atexit destructor for 'holder'' Qt5Guid 4 initterm_e ucrtbased 5 initterm_e ucrtbased 6 initterm_e ucrtbased 7 execute_onexit_table ucrtbased 8 __scrt_dllmain_uninitialize_c utility.cpp 399 9 dllmain_crt_process_detach dll_dllmain.cpp 182 10 dllmain_crt_dispatch dll_dllmain.cpp 220 11 dllmain_dispatch dll_dllmain.cpp 293 12 _DllMainCRTStartup dll_dllmain.cpp 335 13 RtlActivateActivationContextUnsafeFast ntdll 14 LdrShutdownProcess ntdll 15 RtlExitUserProcess ntdll 16 FatalExit KERNEL32 17 wassert ucrtbased 18 wassert ucrtbased 19 exit ucrtbased 20 __scrt_common_main_seh exe_common.inl 297
I used this simplified code:
int main(int argc, char *argv[]) { std::thread thr([](int argc, char *argv[]) { QApplication a(argc, argv); QPushButton btn("Push"); btn.show(); a.exec(); }, argc, argv); thr.join(); }
-
Yes, this is constructed appropriately, however the deallocation should've been tied to the
QCoreApplication::aboutToQuit
. I doubt anyone is going to bother fixing it though, I'm not sure if it's not going to be discarded with "this is unsupported" even ...
-
Good detective work, @Chris-Kawa!
I encountered this a few years ago but had forgotten most of the details.
@kshegunov said in Global static QPixmapCache in Qt internals:
Yes, this is constructed appropriately, however the deallocation should've been tied to the
QCoreApplication::aboutToQuit
. I doubt anyone is going to bother fixing it though, I'm not sure if it's not going to be discarded with "this is unsupported" even ...Since a QPixmap requires a QGuiApplication, would a simple(?) and correct fix be to allocate the QPixmapCache in the QGuiApplication constructor, and deallocate it from the QGuiApplication destructor?
-
Possibly. Or it can be a pseudo-singleton (similarly to
QCoreApplication
) and be initialized on demand in the instance retrieving function (dropping the global static that it currently uses). You could poke Thiago, but as written, I wouldn't hold my breath.
-
There are some reports about such an issue:
https://bugreports.qt.io/browse/QTBUG-48709
https://bugreports.qt.io/browse/QTBUG-21807And it does not crash here for me on Linux and Windows (MSVC, debug-build) with 5.15.x
-
@Christian-Ehrlicher said in Global static QPixmapCache in Qt internals:
And it does not crash here for me on Linux and Windows (MSVC, debug-build) with 5.15.x
We are talking about the QTimers from another thread message. JKSH, split the threads. The other stuff (the supposed crash) contines to befuddle me.
-
I also don't have a warning about the timer with the testcase on windows/msvc :)
-
We are talking about the QTimers from another thread message. JKSH, split the threads. The other stuff (the supposed crash) contines to befuddle me.
I don't know how exactly the crash in the original thread is connected with this. However, the crash and the message about timers are appearing together. They appear after any interaction with simple
QPushButton
. UsingdeleteLater()
as suggested by @JKSH solves the crash, but the message persists.@Christian-Ehrlicher said in Global static QPixmapCache in Qt internals:
And it does not crash here for me on Linux and Windows (MSVC, debug-build) with 5.15.x
To clarify: crash appears only when one tries to unload a library with
QApplication
inside. The library in my case is loaded bylua.exe
, but I doubt it is lua-related problem.
-
@kshegunov said in Global static QPixmapCache in Qt internals:
Possibly. Or it can be a pseudo-singleton (similarly to
QCoreApplication
) and be initialized on demand in the instance retrieving function (dropping the global static that it currently uses). You could poke Thiago, but as written, I wouldn't hold my breath.That sounds like a source and binary compatibility break, unless instance() is used from within the existing static functions.
A thread_local version would be another solution, that could be implemented without changing any user source code. Keep the global static interface object for binary compatibility, and move the actual QObject-based implementation to thread_local storage for the gui thread.
The QPixmapCache documentation already says that the cache is only usable from the "application's main thread".
-
@jeremy_k said in Global static QPixmapCache in Qt internals:
That sounds like a source and binary compatibility break, unless instance() is used from within the existing static functions.
Adding non-virtual functions doesn't brake API or ABI compatibility. Also this is an internal member, any global getter function could do, it's not really necessary to expose it to the user.
A thread_local version would be another solution, that could be implemented without changing any user source code. Keep the global static interface object for binary compatibility, and move the actual QObject-based implementation to thread_local storage for the gui thread.
The QPixmapCache documentation already says that the cache is only usable from the "application's main thread".
It's an internal class, I didn't intend to modify the
QPixmapCache
to begin with. (see the woboq link in Chris' post)
-
@kshegunov said in Global static QPixmapCache in Qt internals:
@jeremy_k said in Global static QPixmapCache in Qt internals:
That sounds like a source and binary compatibility break, unless instance() is used from within the existing static functions.
Adding non-virtual functions doesn't brake API or ABI compatibility. Also this is an internal member, any global getter function could do, it's not really necessary to expose it to the user.
I had read it as a suggestion to introduce a static QPixmapCache::instance(), and make the currently static QPixmapCache member functions non-static. I autocompleted too much.
A thread_local version would be another solution, that could be implemented without changing any user source code. Keep the global static interface object for binary compatibility, and move the actual QObject-based implementation to thread_local storage for the gui thread.
The QPixmapCache documentation already says that the cache is only usable from the "application's main thread".
It's an internal class, I didn't intend to modify the
QPixmapCache
to begin with. (see the woboq link in Chris' post)I saw, and if I interpreted correctly, the issue is that the global static QPixmapCache and its internal QObject based implementation will be destroyed from the C++ main thread when main() exits. The destruction of the internal implementation object could be removed from the public QPixmapCache destructor, but the question is where to destroy it while maintaining the current intended behavior. Making the internal object (or a wrapper) thread local in the gui thread solves destruction at a point where the cache can't see further use anyway.
-
@jeremy_k said in Global static QPixmapCache in Qt internals:
I saw, and if I interpreted correctly, the issue is that the global static QPixmapCache and its internal QObject based implementation will be destroyed from the C++ main thread when main() exits.
Correct, but only with the allowance that we are talking about the
QPMCache
instance (which is some internal class).The destruction of the internal implementation object could be removed from the public QPixmapCache destructor, but the question is where to destroy it while maintaining the current intended behavior.
As I wrote:
QCoreApplication::aboutToQuit
is one possible such point. The problem with that solution is that it won't set the nullptr of the global pointer, which in turn is going to lead to a crash if the application object is recreated. It shouldn't get recreated, but I've seen code that relies on that (believe or not). With any API the first thing you get is hidden use cases, sadly.Making the internal object (or a wrapper) thread local in the gui thread solves destruction at a point where the cache can't see further use anyway.
I don't follow. The instance is created dynamically and on demand, and from some thread unknown at the point of declaration. How does specifying a thread local storage helps here, the pointer to the object is global (and I assume used from many a place)?
-
@kshegunov said in Global static QPixmapCache in Qt internals:
@jeremy_k said in Global static QPixmapCache in Qt internals:
I saw, and if I interpreted correctly, the issue is that the global static QPixmapCache and its internal QObject based implementation will be destroyed from the C++ main thread when main() exits.
Correct, but only with the allowance that we are talking about the
QPMCache
instance (which is some internal class).Yes. We seem to be talking around the same point.
The destruction of the internal implementation object could be removed from the public QPixmapCache destructor, but the question is where to destroy it while maintaining the current intended behavior.
As I wrote:
QCoreApplication::aboutToQuit
is one possible such point. The problem with that solution is that it won't set the nullptr of the global pointer, which in turn is going to lead to a crash if the application object is recreated. It shouldn't get recreated, but I've seen code that relies on that (believe or not). With any API the first thing you get is hidden use cases, sadly.Making the internal object (or a wrapper) thread local in the gui thread solves destruction at a point where the cache can't see further use anyway.
I don't follow. The instance is created dynamically and on demand, and from some thread unknown at the point of declaration. How does specifying a thread local storage helps here, the pointer to the object is global (and I assume used from many a place)?
Using QCoreApplication::aboutToQuit would change the behavior for applications that repeatedly instantiate and destroy the application instance, and it would add an opportunity to make a mistake for applications that don't use QCoreApplication::exec().
Using a thread local object keeps this into RAII territory. The only difference from the current global static is that destruction happens in the relevant thread rather than in the main() thread.
How does the cache managing object tell that it is in the gui thread? If the first application instance must be created before cache use, QThread::currentThread()->threadId() == QCoreApplication::instance()->thread()->threadId() could work. I don't know if creating the application first is currently required.
-
@jeremy_k said in Global static QPixmapCache in Qt internals:
Using a thread local object keeps this into RAII territory. The only difference from the current global static is that destruction happens in the relevant thread rather than in the main() thread.
Ehm, the object, currently, is created in the heap, is it not? Or perhaps I'm misunderstanding what you mean exactly.
@jeremy_k said in Global static QPixmapCache in Qt internals:
I don't know if creating the application first is currently required.
Yes, it is, but also no
QObject
shall outlive theQCoreApplication
instance is part of the rule, which is exactly what's happening in this case.PS.
How does the cache managing object tell that it is in the gui thread?
When a
QObject
is created it stores the reference to the creating thread internally. When the destructor's run that reference's thread id is checked against the current thread id, if they don't match you know you're destroying objects from another thread. This is where this warning is generated.
-
@kshegunov said in Global static QPixmapCache in Qt internals:
@jeremy_k said in Global static QPixmapCache in Qt internals:
Using a thread local object keeps this into RAII territory. The only difference from the current global static is that destruction happens in the relevant thread rather than in the main() thread.
Ehm, the object, currently, is created in the heap, is it not? Or perhaps I'm misunderstanding what you mean exactly.
IIRC, it is created and destroyed by a QGlobalStatic.
@jeremy_k said in Global static QPixmapCache in Qt internals:
I don't know if creating the application first is currently required.
Yes, it is, but also no
QObject
shall outlive theQCoreApplication
instance is part of the rule, which is exactly what's happening in this case.The cache object also outlives a stack allocated QCoreApplication when the gui thread is the main thread.
I'm looking for and not finding a reference to the rules about QCoreApplication versus QObject lifetimes. There used to be a concise list, if I didn't imagine it.
-
@jeremy_k said in Global static QPixmapCache in Qt internals:
IIRC, it is created and destroyed by a QGlobalStatic.
Yes, correct. So you want to make the
QGlobalStatic
instance thread local then?@jeremy_k said in Global static QPixmapCache in Qt internals:
The cache object also outlives a stack allocated QCoreApplication when the gui thread is the main thread.
Yes, but since they're created and destroyed in the same thread the message doesn't fire up. It's wrong, it's just hidden.
@jeremy_k said in Global static QPixmapCache in Qt internals:
I'm looking for and not finding a reference to the rules about QCoreApplication versus QObject lifetimes. There used to be a concise list, if I didn't imagine it.
I don't remember such a list, but this is a direct quote of Thiago[1]:
And QCoreApplication should be the first QObject created and last destroyed.
-
@kshegunov said in Global static QPixmapCache in Qt internals:
@jeremy_k said in Global static QPixmapCache in Qt internals:
IIRC, it is created and destroyed by a QGlobalStatic.
Yes, correct. So you want to make the
QGlobalStatic
instance thread local then?That would be nice.
QTheadLocalStatic
. I don't see a lot of thread_local in Qt 6 base.@jeremy_k said in Global static QPixmapCache in Qt internals:
I'm looking for and not finding a reference to the rules about QCoreApplication versus QObject lifetimes. There used to be a concise list, if I didn't imagine it.
I don't remember such a list, but this is a direct quote of Thiago[1]:
And QCoreApplication should be the first QObject created and last destroyed.
It's not the reference I was thinking of, but thanks.
-
@jeremy_k said in Global static QPixmapCache in Qt internals:
That would be nice. QTheadLocalStatic. I don't see a lot of thread_local in Qt 6 base.
Because it has very niche uses to begin with. Making the mentioned object thread local implies an instance being created for each thread that runs. It could work, but I'm not convinced it's necessary to create the wrapper object for each thread Qt or the user starts. Granted it's just some pointer wrapper, but still ... there should be a cleaner way, I think.
PS
We are overengineering the solution. The pixmap cache is only supposed to work from one thread and the GUI one at that. You just revert to a plain pointer and define an inline function that creates the object if not there. (It also connects a lambda for deletion and clearing the pointer itself). The uses seem to be limited only to theQPixmapCache
implementation itself.PS 2
Alternatively you convert it toQPointer
and create the object in theQPixmapCache
constructor where you also connect theQCoreApplication::aboutToQuit
to theQObject::deleteLater
for the internal object.
-
@kshegunov said in Global static QPixmapCache in Qt internals:
PS 2
Alternatively you convert it toQPointer
and create the object in theQPixmapCache
constructor where you also connect theQCoreApplication::aboutToQuit
to theQObject::deleteLater
for the internal object.This idea is sound, with a small adjustment to avoid initializing the object if it is never used (same as QGlobalStatic): https://lists.qt-project.org/pipermail/development/2021-May/041517.html
-
I saw the mail. I think there was some reason I decided against it, but can't remember currently. For sure one of it is that there's no
QPixmapCache
constructor ...Anyway, the
QPointer
is superfluous, as you're always the owner of the object. It never can get destroyed midway and you don't need to listen for it.