diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 21e4c294c91..31abed73cf9 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -359,6 +359,11 @@ inline void QFactoryLoader::Private::updateSinglePath(const QString &path) loadedLibraries.resize(libraries.size()); } +void QFactoryLoader::setLoadHints(QLibrary::LoadHints loadHints) +{ + d->loadHints = loadHints; +} + void QFactoryLoader::update() { #ifdef QT_SHARED @@ -549,6 +554,7 @@ inline QObject *QFactoryLoader::instanceHelper_locked(int index) const if (size_t(index) < d->libraries.size()) { QLibraryPrivate *library = d->libraries[index].get(); d->loadedLibraries[index] = true; + library->setLoadHints(d->loadHints); return library->pluginInstance(); } // we know d->libraries.size() <= index <= numeric_limits::max() → no overflow diff --git a/src/corelib/plugin/qfactoryloader_p.h b/src/corelib/plugin/qfactoryloader_p.h index c14e5795a8f..d7482b36198 100644 --- a/src/corelib/plugin/qfactoryloader_p.h +++ b/src/corelib/plugin/qfactoryloader_p.h @@ -48,6 +48,7 @@ public: #if QT_CONFIG(library) ~QFactoryLoader(); + void setLoadHints(QLibrary::LoadHints hints); void update(); static void refreshAll(); @@ -79,6 +80,7 @@ private: QString suffix; QString extraSearchPath; Qt::CaseSensitivity cs; + QLibrary::LoadHints loadHints; void updateSinglePath(const QString &pluginDir); #endif diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index e629e2b1f12..f03eba3bffa 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -14,6 +14,11 @@ #ifdef Q_OS_DARWIN # include + +// Apple's dyld *does* support RTLD_NODELETE and the library remains loaded in +// memory after the dlclose() call, but their Objective C crashes when running +// code from unloaded-but-still-loaded plugins. +# undef RTLD_NODELETE #endif #ifdef Q_OS_ANDROID diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 3409e486ab0..0311b8e3845 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -144,12 +144,12 @@ int pthread_timedjoin_np(...) { return ENOSYS; } // pretend // Finally, for the thread that called ::exit() (which in most cases happens by // returning from the main() function), finish() and cleanup() happen at // function-local static destructor time, and the deref & delete happens later, -// at global static destruction time. This is important so QLibraryStore can -// unload plugins between those two steps: it is also destroyed by a global -// static destructor, but with a lower priority than ours. The order needs to -// be this way so we delete the event dispatcher before plugins unload, as -// often the dispatcher for the main thread is provided by a QPA plugin, but -// the QThreadData object must still be alive when the plugins do unload. +// at global static destruction time. That way, we delete the event dispatcher +// before QLibraryStore's clean up runs and unloads remaining plugins. This +// strategy was chosen because of crashes observed while running the event +// dispatcher's destructor, and though the cause of the crash was something +// else (QFactoryLoader always loads with PreventUnloadHint set), other plugins +// may still attempt to access QThreadData in their global destructors. Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;