Improve the loading performance of QLibrary

If an absolute path is specified we try that first. Otherwise we first
try the most likely system-specific format (e.g. libfoo.so) on Unix.
This improves performance especially on systems with slow flash devices.

For example, prior to this commit loading the Xcursor library (in the
xcb plugin) results in attempts to dlopen:

"Xcursor"
"Xcursor.so.1"
"libXcursor"
"libXcursor.so.1"

With this commit this is reduced to a single attempt of:

"libXcursor.so.1"

Plugin loading uses absolute paths with QLibrary so there is no
performance penalty for plugins with this commit.

This is however a behavioural change with respect to Qt4 but one
that I believe is justified and wanted.

Change-Id: I7813afa335f9bf515e87934c2f8f97888818c69c
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
This commit is contained in:
Sean Harmer 2012-07-18 13:12:46 +01:00 committed by Qt by Nokia
parent b182940340
commit 0026b80cd2
3 changed files with 80 additions and 33 deletions

View File

@ -96,13 +96,22 @@ static QBasicMutex qt_library_mutex;
name in the constructor, or set it explicitly with setFileName().
When loading the library, QLibrary searches in all the
system-specific library locations (e.g. \c LD_LIBRARY_PATH on
Unix), unless the file name has an absolute path. If the file
cannot be found, QLibrary tries the name with different
platform-specific file suffixes, like ".so" on Unix, ".dylib" on
the Mac, or ".dll" on Windows. This makes it possible
to specify shared libraries that are only identified by their
basename (i.e. without their suffix), so the same code will work
on different operating systems.
Unix), unless the file name has an absolute path.
If the file name is an absolute path then an attempt is made to
load this path first. If the file cannot be found, QLibrary tries
the name with different platform-specific file prefixes, like
"lib" on Unix and Mac, and suffixes, like ".so" on Unix, ".dylib"
on the Mac, or ".dll" on Windows.
If the file path is not absolute then QLibrary modifies the search
order to try the system-specific prefixes and suffixes first,
followed by the file path specified.
This makes it possible to specify shared libraries that are only
identified by their basename (i.e. without their suffix), so the
same code will work on different operating systems yet still
minimise the number of attempts to find the library.
The most important functions are load() to dynamically load the
library file, isLoaded() to check whether loading was successful,

View File

@ -93,9 +93,8 @@ bool QLibraryPrivate::load_sys()
else
path += QLatin1Char('/');
// The first filename we want to attempt to load is the filename as the callee specified.
// Thus, the first attempt we do must be with an empty prefix and empty suffix.
QStringList suffixes(QLatin1String("")), prefixes(QLatin1String(""));
QStringList suffixes;
QStringList prefixes;
if (pluginState != IsAPlugin) {
prefixes << QLatin1String("lib");
#if defined(Q_OS_HPUX)
@ -185,6 +184,18 @@ bool QLibraryPrivate::load_sys()
}
#endif
#endif // QT_HPUX_LD
// If the filename is an absolute path then we want to try that first as it is most likely
// what the callee wants. If we have been given a non-absolute path then lets try the
// native library name first to avoid unnecessary calls to dlopen().
if (fsEntry.isAbsolute()) {
suffixes.prepend(QString());
prefixes.prepend(QString());
} else {
suffixes.append(QString());
prefixes.append(QString());
}
bool retry = true;
for(int prefix = 0; retry && !pHnd && prefix < prefixes.size(); prefix++) {
for(int suffix = 0; retry && !pHnd && suffix < suffixes.size(); suffix++) {

View File

@ -45,6 +45,7 @@
#include "qdir.h"
#include "qfileinfo.h"
#include "qdir.h"
#include <private/qfilesystementry_p.h>
#if defined(QT_NO_LIBRARY) && defined(Q_OS_WIN)
#undef QT_NO_LIBRARY
@ -59,46 +60,72 @@ extern QString qt_error_string(int code);
bool QLibraryPrivate::load_sys()
{
#ifdef Q_OS_WINCE
QString attempt = QFileInfo(fileName).absoluteFilePath();
#else
QString attempt = fileName;
#endif
//avoid 'Bad Image' message box
UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
// We make the following attempts at locating the library:
//
// WinCE
// if (absolute)
// fileName
// fileName + ".dll"
// else
// fileName + ".dll"
// fileName
// QFileInfo(fileName).absoluteFilePath()
//
// Windows
// if (absolute)
// fileName
// fileName + ".dll"
// else
// fileName + ".dll"
// fileName
//
// NB If it's a plugin we do not ever try the ".dll" extension
QStringList attempts;
if (pluginState != IsAPlugin)
attempts.append(fileName + QLatin1String(".dll"));
// If the fileName is an absolute path we try that first, otherwise we
// use the system-specific suffix first
QFileSystemEntry fsEntry(fileName);
if (fsEntry.isAbsolute()) {
attempts.prepend(fileName);
} else {
attempts.append(fileName);
#if defined(Q_OS_WINCE)
attempts.append(QFileInfo(fileName).absoluteFilePath());
#endif
}
Q_FOREACH (const QString &attempt, attempts) {
pHnd = LoadLibrary((wchar_t*)QDir::toNativeSeparators(attempt).utf16());
if (pluginState != IsAPlugin) {
#if defined(Q_OS_WINCE)
if (!pHnd && ::GetLastError() == ERROR_MOD_NOT_FOUND) {
QString secondAttempt = fileName;
pHnd = LoadLibrary((wchar_t*)QDir::toNativeSeparators(secondAttempt).utf16());
}
#endif
if (!pHnd && ::GetLastError() == ERROR_MOD_NOT_FOUND) {
attempt += QLatin1String(".dll");
pHnd = LoadLibrary((wchar_t*)QDir::toNativeSeparators(attempt).utf16());
}
// 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)
break;
}
SetErrorMode(oldmode);
if (!pHnd) {
errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName).arg(qt_error_string());
}
if (pHnd) {
} else {
// Query the actual name of the library that was loaded
errorString.clear();
wchar_t buffer[MAX_PATH];
::GetModuleFileName(pHnd, buffer, MAX_PATH);
attempt = QString::fromWCharArray(buffer);
const QDir dir = QFileInfo(fileName).dir();
const QString realfilename = attempt.mid(attempt.lastIndexOf(QLatin1Char('\\')) + 1);
QString moduleFileName = QString::fromWCharArray(buffer);
moduleFileName.remove(0, 1 + moduleFileName.lastIndexOf(QLatin1Char('\\')));
const QDir dir(fsEntry.path());
if (dir.path() == QLatin1String("."))
qualifiedFileName = realfilename;
qualifiedFileName = moduleFileName;
else
qualifiedFileName = dir.filePath(realfilename);
qualifiedFileName = dir.filePath(moduleFileName);
}
return (pHnd != 0);
}