summaryrefslogtreecommitdiffstats
path: root/src/corelib/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/plugin')
-rw-r--r--src/corelib/plugin/qfactoryloader.cpp13
-rw-r--r--src/corelib/plugin/qlibrary.cpp83
-rw-r--r--src/corelib/plugin/qlibrary_p.h26
-rw-r--r--src/corelib/plugin/qlibrary_unix.cpp30
-rw-r--r--src/corelib/plugin/qlibrary_win.cpp30
-rw-r--r--src/corelib/plugin/qpluginloader.cpp8
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)