diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2020-03-16 18:41:27 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2020-03-16 18:41:27 +0100 |
commit | ff922e7b87de147797fbd759167878aec7625f0c (patch) | |
tree | a326819f23e2421723f144522f9362ba608d6bb1 /src/corelib/plugin | |
parent | e464e1eb8eb63c631fb0916c3ea4540a88d8aad3 (diff) | |
parent | 75f52bd0ddc4afbd181c14b310d256e3507052d6 (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts:
src/corelib/kernel/qmetatype.cpp
Change-Id: I88eb0d3e9c9a38abf7241a51e370c655ae74e38a
Diffstat (limited to 'src/corelib/plugin')
-rw-r--r-- | src/corelib/plugin/qfactoryloader.cpp | 13 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary.cpp | 83 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary_p.h | 26 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary_unix.cpp | 30 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary_win.cpp | 30 | ||||
-rw-r--r-- | src/corelib/plugin/qpluginloader.cpp | 8 |
6 files changed, 108 insertions, 82 deletions
diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 14de8db1c6..400c8bf05f 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -389,15 +389,10 @@ QObject *QFactoryLoader::instance(int index) const QMutexLocker lock(&d->mutex); if (index < d->libraryList.size()) { QLibraryPrivate *library = d->libraryList.at(index); - if (library->instance || library->loadPlugin()) { - if (!library->inst) - library->inst = library->instance(); - QObject *obj = library->inst.data(); - if (obj) { - if (!obj->parent()) - obj->moveToThread(QCoreApplicationPrivate::mainThread()); - return obj; - } + if (QObject *obj = library->pluginInstance()) { + if (!obj->parent()) + obj->moveToThread(QCoreApplicationPrivate::mainThread()); + return obj; } return nullptr; } diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 406a83c7dd..47f09e58a2 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -407,7 +407,7 @@ inline void QLibraryStore::cleanup() QLibraryPrivate *lib = it.value(); if (lib->libraryRefCount.loadRelaxed() == 1) { if (lib->libraryUnloadCount.loadRelaxed() > 0) { - Q_ASSERT(lib->pHnd); + Q_ASSERT(lib->pHnd.loadRelaxed()); lib->libraryUnloadCount.storeRelaxed(1); #ifdef __GLIBC__ // glibc has a bug in unloading from global destructors @@ -498,8 +498,7 @@ inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib) } QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints) - : pHnd(nullptr), fileName(canonicalFileName), fullVersion(version), instance(nullptr), - libraryRefCount(0), libraryUnloadCount(0), pluginState(MightBeAPlugin) + : fileName(canonicalFileName), fullVersion(version), pluginState(MightBeAPlugin) { loadHintsInt.storeRelaxed(loadHints); if (canonicalFileName.isEmpty()) @@ -519,7 +518,7 @@ QLibraryPrivate::~QLibraryPrivate() void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) { // if the library is already loaded, we can't change the load hints - if (pHnd) + if (pHnd.loadRelaxed()) return; loadHintsInt.storeRelaxed(lh); @@ -527,7 +526,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) QFunctionPointer QLibraryPrivate::resolve(const char *symbol) { - if (!pHnd) + if (!pHnd.loadRelaxed()) return nullptr; return resolve_sys(symbol); } @@ -539,9 +538,36 @@ void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh) mergeLoadHints(lh); } +QObject *QLibraryPrivate::pluginInstance() +{ + // first, check if the instance is cached and hasn't been deleted + QObject *obj = (QMutexLocker(&mutex), inst.data()); + if (obj) + return obj; + + // We need to call the plugin's factory function. Is that cached? + // skip increasing the reference count (why? -Thiago) + QtPluginInstanceFunction factory = instanceFactory.loadAcquire(); + if (!factory) + factory = loadPlugin(); + + if (!factory) + return nullptr; + + obj = factory(); + + // cache again + QMutexLocker locker(&mutex); + if (inst) + obj = inst; + else + inst = obj; + return obj; +} + bool QLibraryPrivate::load() { - if (pHnd) { + if (pHnd.loadRelaxed()) { libraryUnloadCount.ref(); return true; } @@ -550,7 +576,9 @@ bool QLibraryPrivate::load() Q_TRACE(QLibraryPrivate_load_entry, fileName); + mutex.lock(); bool ret = load_sys(); + mutex.unlock(); if (qt_debug_component()) { if (ret) { qDebug() << "loaded library" << fileName; @@ -573,9 +601,10 @@ bool QLibraryPrivate::load() bool QLibraryPrivate::unload(UnloadFlag flag) { - if (!pHnd) + if (!pHnd.loadRelaxed()) return false; if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to + QMutexLocker locker(&mutex); delete inst.data(); if (flag == NoUnloadSys || unload_sys()) { if (qt_debug_component()) @@ -584,12 +613,13 @@ bool QLibraryPrivate::unload(UnloadFlag flag) //when the library is unloaded, we release the reference on it so that 'this' //can get deleted libraryRefCount.deref(); - pHnd = nullptr; - instance = nullptr; + pHnd.storeRelaxed(nullptr); + instanceFactory.storeRelaxed(nullptr); + return true; } } - return (pHnd == nullptr); + return false; } void QLibraryPrivate::release() @@ -597,22 +627,23 @@ void QLibraryPrivate::release() QLibraryStore::releaseLibrary(this); } -bool QLibraryPrivate::loadPlugin() +QtPluginInstanceFunction QLibraryPrivate::loadPlugin() { - if (instance) { + if (auto ptr = instanceFactory.loadAcquire()) { libraryUnloadCount.ref(); - return true; + return ptr; } if (pluginState == IsNotAPlugin) - return false; + return nullptr; if (load()) { - instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance"); - return instance; + auto ptr = reinterpret_cast<QtPluginInstanceFunction>(resolve("qt_plugin_instance")); + instanceFactory.storeRelease(ptr); // two threads may store the same value + return ptr; } if (qt_debug_component()) qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString; pluginState = IsNotAPlugin; - return false; + return nullptr; } /*! @@ -719,6 +750,7 @@ bool QLibraryPrivate::isPlugin() void QLibraryPrivate::updatePluginState() { + QMutexLocker locker(&mutex); errorString.clear(); if (pluginState != MightBeAPlugin) return; @@ -739,7 +771,7 @@ void QLibraryPrivate::updatePluginState() } #endif - if (!pHnd) { + if (!pHnd.loadRelaxed()) { // scan for the plugin metadata without loading success = findPatternUnloaded(fileName, this); } else { @@ -803,7 +835,7 @@ bool QLibrary::load() if (!d) return false; if (did_load) - return d->pHnd; + return d->pHnd.loadRelaxed(); did_load = true; return d->load(); } @@ -839,7 +871,7 @@ bool QLibrary::unload() */ bool QLibrary::isLoaded() const { - return d && d->pHnd; + return d && d->pHnd.loadRelaxed(); } @@ -950,8 +982,10 @@ void QLibrary::setFileName(const QString &fileName) QString QLibrary::fileName() const { - if (d) + if (d) { + QMutexLocker locker(&d->mutex); return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName; + } return QString(); } @@ -1092,7 +1126,12 @@ QFunctionPointer QLibrary::resolve(const QString &fileName, const QString &versi */ QString QLibrary::errorString() const { - return (!d || d->errorString.isEmpty()) ? tr("Unknown error") : d->errorString; + QString str; + if (d) { + QMutexLocker locker(&d->mutex); + str = d->errorString; + } + return str.isEmpty() ? tr("Unknown error") : str; } /*! diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index a58547a2c3..3ca544b2de 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -54,10 +54,10 @@ #include <QtCore/private/qglobal_p.h> #include "QtCore/qlibrary.h" +#include "QtCore/qmutex.h" #include "QtCore/qpointer.h" #include "QtCore/qstringlist.h" #include "QtCore/qplugin.h" -#include "QtCore/qsharedpointer.h" #ifdef Q_OS_WIN # include "QtCore/qt_windows.h" #endif @@ -72,21 +72,18 @@ class QLibraryStore; class QLibraryPrivate { public: - #ifdef Q_OS_WIN - HINSTANCE + using Handle = HINSTANCE; #else - void * + using Handle = void *; #endif - pHnd; - enum UnloadFlag { UnloadSys, NoUnloadSys }; - QString fileName, qualifiedFileName; - QString fullVersion; + const QString fileName; + const QString fullVersion; bool load(); - bool loadPlugin(); // loads and resolves instance + QtPluginInstanceFunction loadPlugin(); // loads and resolves instance bool unload(UnloadFlag flag = UnloadSys); void release(); QFunctionPointer resolve(const char *); @@ -94,17 +91,22 @@ public: QLibrary::LoadHints loadHints() const { return QLibrary::LoadHints(loadHintsInt.loadRelaxed()); } void setLoadHints(QLibrary::LoadHints lh); + QObject *pluginInstance(); static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString(), QLibrary::LoadHints loadHints = { }); static QStringList suffixes_sys(const QString &fullVersion); static QStringList prefixes_sys(); - QPointer<QObject> inst; - QtPluginInstanceFunction instance; - QJsonObject metaData; + QAtomicPointer<std::remove_pointer<QtPluginInstanceFunction>::type> instanceFactory; + QAtomicPointer<std::remove_pointer<Handle>::type> pHnd; + // the mutex protects the fields below + QMutex mutex; + QPointer<QObject> inst; // used by QFactoryLoader + QJsonObject metaData; QString errorString; + QString qualifiedFileName; void updatePluginState(); bool isPlugin(); diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 4a092abb3e..29813e5863 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -214,8 +214,9 @@ bool QLibraryPrivate::load_sys() #endif bool retry = true; - for(int prefix = 0; retry && !pHnd && prefix < prefixes.size(); prefix++) { - for(int suffix = 0; retry && !pHnd && suffix < suffixes.size(); suffix++) { + Handle hnd = nullptr; + for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) { + for (int suffix = 0; retry && !hnd && suffix < suffixes.size(); suffix++) { if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix))) continue; if (path.isEmpty() && prefixes.at(prefix).contains(QLatin1Char('/'))) @@ -232,7 +233,7 @@ bool QLibraryPrivate::load_sys() attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix); } - pHnd = dlopen(QFile::encodeName(attempt), dlFlags); + hnd = dlopen(QFile::encodeName(attempt), dlFlags); #ifdef Q_OS_ANDROID if (!pHnd) { auto attemptFromBundle = attempt; @@ -248,7 +249,7 @@ bool QLibraryPrivate::load_sys() } #endif - if (!pHnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) { + if (!hnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) { // We only want to continue if dlopen failed due to that the shared library did not exist. // However, we are only able to apply this check for absolute filenames (since they are // not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...) @@ -259,7 +260,7 @@ bool QLibraryPrivate::load_sys() } #ifdef Q_OS_MAC - if (!pHnd) { + if (!hnd) { QByteArray utf8Bundle = fileName.toUtf8(); QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true); QCFType<CFBundleRef> bundle = CFBundleCreate(NULL, bundleUrl); @@ -268,23 +269,24 @@ bool QLibraryPrivate::load_sys() char executableFile[FILENAME_MAX]; CFURLGetFileSystemRepresentation(url, true, reinterpret_cast<UInt8*>(executableFile), FILENAME_MAX); attempt = QString::fromUtf8(executableFile); - pHnd = dlopen(QFile::encodeName(attempt), dlFlags); + hnd = dlopen(QFile::encodeName(attempt), dlFlags); } } #endif - if (!pHnd) { + if (!hnd) { errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror()); } - if (pHnd) { + if (hnd) { qualifiedFileName = attempt; errorString.clear(); } - return (pHnd != nullptr); + pHnd.storeRelaxed(hnd); + return (hnd != nullptr); } bool QLibraryPrivate::unload_sys() { - if (dlclose(pHnd)) { + if (dlclose(pHnd.loadAcquire())) { #if defined (Q_OS_QNX) // Workaround until fixed in QNX; fixes crash in char *error = dlerror(); // QtDeclarative auto test "qqmlenginecleanup" for instance if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative" @@ -316,13 +318,7 @@ Q_CORE_EXPORT QFunctionPointer qt_mac_resolve_sys(void *handle, const char *symb QFunctionPointer QLibraryPrivate::resolve_sys(const char* symbol) { - QFunctionPointer address = QFunctionPointer(dlsym(pHnd, symbol)); - if (!address) { - errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg( - QString::fromLatin1(symbol), fileName, qdlerror()); - } else { - errorString.clear(); - } + QFunctionPointer address = QFunctionPointer(dlsym(pHnd.loadAcquire(), symbol)); return address; } diff --git a/src/corelib/plugin/qlibrary_win.cpp b/src/corelib/plugin/qlibrary_win.cpp index 05a93d467e..000bf76276 100644 --- a/src/corelib/plugin/qlibrary_win.cpp +++ b/src/corelib/plugin/qlibrary_win.cpp @@ -95,26 +95,27 @@ bool QLibraryPrivate::load_sys() attempts.prepend(QDir::rootPath() + fileName); #endif + Handle hnd = nullptr; for (const QString &attempt : qAsConst(attempts)) { #ifndef Q_OS_WINRT - pHnd = LoadLibrary(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(attempt).utf16())); + hnd = LoadLibrary(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(attempt).utf16())); #else // Q_OS_WINRT QString path = QDir::toNativeSeparators(QDir::current().relativeFilePath(attempt)); - pHnd = LoadPackagedLibrary(reinterpret_cast<LPCWSTR>(path.utf16()), 0); - if (pHnd) + hnd = LoadPackagedLibrary(reinterpret_cast<LPCWSTR>(path.utf16()), 0); + if (hnd) qualifiedFileName = attempt; #endif // !Q_OS_WINRT // If we have a handle or the last error is something other than "unable // to find the module", then bail out - if (pHnd || ::GetLastError() != ERROR_MOD_NOT_FOUND) + if (hnd || ::GetLastError() != ERROR_MOD_NOT_FOUND) break; } #ifndef Q_OS_WINRT SetErrorMode(oldmode); #endif - if (!pHnd) { + if (!hnd) { errorString = QLibrary::tr("Cannot load library %1: %2").arg( QDir::toNativeSeparators(fileName), qt_error_string()); } else { @@ -123,7 +124,7 @@ bool QLibraryPrivate::load_sys() #ifndef Q_OS_WINRT wchar_t buffer[MAX_PATH]; - ::GetModuleFileName(pHnd, buffer, MAX_PATH); + ::GetModuleFileName(hnd, buffer, MAX_PATH); QString moduleFileName = QString::fromWCharArray(buffer); moduleFileName.remove(0, 1 + moduleFileName.lastIndexOf(QLatin1Char('\\'))); @@ -138,19 +139,20 @@ bool QLibraryPrivate::load_sys() HMODULE hmod; bool ok = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast<const wchar_t *>(pHnd), + reinterpret_cast<const wchar_t *>(hnd), &hmod); - Q_ASSERT(!ok || hmod == pHnd); + Q_ASSERT(!ok || hmod == hnd); Q_UNUSED(ok); } #endif // !Q_OS_WINRT } - return (pHnd != 0); + pHnd.storeRelaxed(hnd); + return (pHnd != nullptr); } bool QLibraryPrivate::unload_sys() { - if (!FreeLibrary(pHnd)) { + if (!FreeLibrary(pHnd.loadAcquire())) { errorString = QLibrary::tr("Cannot unload library %1: %2").arg( QDir::toNativeSeparators(fileName), qt_error_string()); return false; @@ -161,13 +163,7 @@ bool QLibraryPrivate::unload_sys() QFunctionPointer QLibraryPrivate::resolve_sys(const char* symbol) { - FARPROC address = GetProcAddress(pHnd, symbol); - if (!address) { - errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg( - QString::fromLatin1(symbol), QDir::toNativeSeparators(fileName), qt_error_string()); - } else { - errorString.clear(); - } + FARPROC address = GetProcAddress(pHnd.loadAcquire(), symbol); return QFunctionPointer(address); } QT_END_NAMESPACE diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp index f34578ea81..0a63b93762 100644 --- a/src/corelib/plugin/qpluginloader.cpp +++ b/src/corelib/plugin/qpluginloader.cpp @@ -196,9 +196,7 @@ QObject *QPluginLoader::instance() { if (!isLoaded() && !load()) return nullptr; - if (!d->inst && d->instance) - d->inst = d->instance(); - return d->inst.data(); + return d->pluginInstance(); } /*! @@ -233,7 +231,7 @@ bool QPluginLoader::load() if (!d || d->fileName.isEmpty()) return false; if (did_load) - return d->pHnd && d->instance; + return d->pHnd && d->instanceFactory.loadAcquire(); if (!d->isPlugin()) return false; did_load = true; @@ -275,7 +273,7 @@ bool QPluginLoader::unload() */ bool QPluginLoader::isLoaded() const { - return d && d->pHnd && d->instance; + return d && d->pHnd && d->instanceFactory.loadRelaxed(); } #if defined(QT_SHARED) |