summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2022-10-10 19:45:54 -0700
committerThiago Macieira <thiago.macieira@intel.com>2022-10-20 15:38:47 -0700
commit77eef3291782c967f17d703ca6a00477f7a76b11 (patch)
treee273b4efb17d8e56c044fc6152c8c1c18c26854a
parent3731e04668d0b1dbf6c165c0b14183d4c9dbc7ad (diff)
QLibrary: fix loading multiple versions of a library
The libraryMap only stored the file path, so we couldn't load two versions of the same library as we'd find the other version already loaded. Change the map to index by file name and version (using a NUL as separator, since that can't appear in file names). [ChangeLog][QtCore][QLibrary] Fixed a bug that caused QLibrary to be unable to load two different versions of a library of a given name at the same time. Note that this is often inadviseable and loading the second library may cause a crash. Pick-to: 6.4 Change-Id: I12a088d1ae424825abd3fffd171ce3bb0590978d Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/corelib/plugin/qlibrary.cpp14
-rw-r--r--tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp66
2 files changed, 75 insertions, 5 deletions
diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp
index 26b110b7de..abfb1e0df5 100644
--- a/src/corelib/plugin/qlibrary.cpp
+++ b/src/corelib/plugin/qlibrary.cpp
@@ -391,10 +391,12 @@ inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, con
QMutexLocker locker(&qt_library_mutex);
QLibraryStore *data = instance();
+ QString mapName = version.isEmpty() ? fileName : fileName + u'\0' + version;
+
// check if this library is already loaded
QLibraryPrivate *lib = nullptr;
if (Q_LIKELY(data)) {
- lib = data->libraryMap.value(fileName);
+ lib = data->libraryMap.value(mapName);
if (lib)
lib->mergeLoadHints(loadHints);
}
@@ -403,7 +405,7 @@ inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, con
// track this library
if (Q_LIKELY(data) && !fileName.isEmpty())
- data->libraryMap.insert(fileName, lib);
+ data->libraryMap.insert(mapName, lib);
lib->libraryRefCount.ref();
return lib;
@@ -423,9 +425,11 @@ inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib)
Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0);
if (Q_LIKELY(data) && !lib->fileName.isEmpty()) {
- QLibraryPrivate *that = data->libraryMap.take(lib->fileName);
- Q_ASSERT(lib == that);
- Q_UNUSED(that);
+ qsizetype n = erase_if(data->libraryMap, [lib](LibraryMap::iterator it) {
+ return it.value() == lib;
+ });
+ Q_ASSERT_X(n, "~QLibrary", "Did not find this library in the library map");
+ Q_UNUSED(n);
}
delete lib;
}
diff --git a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp
index 2a494cb12d..018756e81e 100644
--- a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp
+++ b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp
@@ -96,6 +96,8 @@ private slots:
void isLibrary();
void version_data();
void version();
+ void loadTwoVersions();
+ void setFileNameAndVersionTwice();
void setFileNameAndVersionAfterFailedLoad_data() { version_data(); }
void setFileNameAndVersionAfterFailedLoad();
void errorString_data();
@@ -196,6 +198,70 @@ void tst_QLibrary::version()
#endif
}
+void tst_QLibrary::loadTwoVersions()
+{
+#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN)
+ QSKIP("Versioned files are not generated for this OS, so this test is not applicable.");
+#endif
+
+ QLibrary lib1(directory + "/mylib", 1);
+ QLibrary lib2(directory + "/mylib", 2);
+ QVERIFY(!lib1.isLoaded());
+ QVERIFY(!lib2.isLoaded());
+
+ // load the first one
+ QVERIFY(lib1.load());
+ QVERIFY(lib1.isLoaded());
+
+ // let's see if we can load the second one too
+ QVERIFY(lib2.load());
+ QVERIFY(lib2.isLoaded());
+
+ auto p1 = (VersionFunction)lib1.resolve("mylibversion");
+ QVERIFY(p1);
+
+ auto p2 = (VersionFunction)lib2.resolve("mylibversion");
+ QVERIFY(p2);
+
+ QCOMPARE_NE(p1(), p2());
+
+ lib2.unload();
+ lib1.unload();
+}
+
+void tst_QLibrary::setFileNameAndVersionTwice()
+{
+#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN)
+ QSKIP("Versioned files are not generated for this OS, so this test is not applicable.");
+#endif
+
+ QLibrary library(directory + "/mylib", 1);
+ QVERIFY(library.load());
+ QVERIFY(library.isLoaded());
+
+ auto p1 = (VersionFunction)library.resolve("mylibversion");
+ QVERIFY(p1);
+ // don't .unload()
+
+ library.setFileNameAndVersion(directory + "/mylib", 2);
+ QVERIFY(!library.isLoaded());
+ QVERIFY(library.load());
+ QVERIFY(library.isLoaded());
+
+ auto p2 = (VersionFunction)library.resolve("mylibversion");
+ QVERIFY(p2);
+ QCOMPARE_NE(p1(), p2());
+
+ QVERIFY(library.unload());
+ QVERIFY(!library.isLoaded());
+
+ // set back
+ library.setFileNameAndVersion(directory + "/mylib", 1);
+ QVERIFY(library.isLoaded());
+ QVERIFY(library.unload());
+ QVERIFY(!library.isLoaded());
+}
+
void tst_QLibrary::load_data()
{
QTest::addColumn<QString>("lib");