Make QLocale "unload" the ICU libraries upon exit
authorThiago Macieira <thiago.macieira@intel.com>
Wed, 23 May 2012 14:03:40 +0000 (16:03 +0200)
committerQt by Nokia <qt-info@nokia.com>
Thu, 24 May 2012 18:20:52 +0000 (20:20 +0200)
Right now, valgrind reports that there is some reachable memory in
QLibrary because we don't call unload() in the libraries we loaded. So
do unload() them.

Unfortunately, ICU seems to have some global statics it doesn't free. If
we really unload the libraries, valgrind will report a leak. So use the
PreventUnloadHint, which causes libdl to not actually unload the
libraries.

Change-Id: I273f09627e27b9116366ddc427e1f3f53ea0f61a
Reviewed-by: Denis Dzyubenko <denis.dzyubenko@nokia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
src/corelib/tools/qlocale_icu.cpp

index eac80b2..42651aa 100644 (file)
@@ -70,27 +70,66 @@ static LibLoadStatus status = NotLoaded;
 
 static UCollator *icuCollator = 0;
 
+namespace {
+struct Libraries {
+    QLibrary libicui18n;
+    QLibrary libicuuc;
+    ~Libraries()
+    {
+        if (icuCollator) {
+            ptr_ucol_close(icuCollator);
+            icuCollator = 0;
+        }
+
+        libicui18n.unload();
+        libicuuc.unload();
+    }
+};
+}
+Q_GLOBAL_STATIC(Libraries, icuLibraries)
 
+static bool loadIcuLibrary(QLibrary &lib, const QString &name)
+{
+#ifdef Q_OS_WIN
+    // QLibrary on Windows does not use the version number, the libraries
+    // are named "icuin<version>.dll", though.
+    lib.setFileName(name);
+#else
+    // on Unix, we can use the version number
+    lib.setFileNameAndVersion(name, QStringLiteral(U_ICU_VERSION_SHORT));
+#endif
+
+    // the ICU libraries appear to allocate global statics and not free them
+    // set the PreventUnloadHint so that we can unload the QLibrary object and
+    // delete it, but the libraries themselves remain in memory
+    lib.setLoadHints(QLibrary::PreventUnloadHint);
+    return lib.load();
+}
+
+// this function is NOT THREAD-SAFE!
 bool qt_initIcu(const QString &localeString)
 {
-    if (status == ErrorLoading)
+    if (status == ErrorLoading || !icuLibraries())
         return false;
 
     if (status == NotLoaded) {
 
         // resolve libicui18n
-        const QString version = QString::fromLatin1(U_ICU_VERSION_SHORT);
 #ifdef Q_OS_WIN
         // QLibrary on Windows does not use the version number, the libraries
         // are named "icuin<version>.dll", though.
-        QString libName = QStringLiteral("icuin") + version;
+        // QStringLiteral should work here and will work when MSVC fully supports C++11
+        // unfortunately, current versions have do not support proper string concatenation
+        QString libicui18nName = QLatin1String("icuin" U_ICU_VERSION_SHORT);
+        QString libicuucName = QLatin1String("icuuc" U_ICU_VERSION_SHORT);
 #else
-        QString libName = QStringLiteral("icui18n");
+        QString libicui18nName = QStringLiteral("icui18n");
+        QString libicuucName = QStringLiteral("icuuc");
 #endif
-        QLibrary lib(libName, version);
-        if (!lib.load()) {
-            qWarning("Unable to load library '%s' version %s: %s",
-                     qPrintable(libName), qPrintable(version),
+        QLibrary &lib = icuLibraries()->libicui18n;
+        if (!loadIcuLibrary(lib, libicui18nName)) {
+            qWarning("Unable to load library '%s' version " U_ICU_VERSION_SHORT ": %s",
+                     qPrintable(libicui18nName),
                      qPrintable(lib.errorString()));
             status = ErrorLoading;
             return false;
@@ -112,21 +151,16 @@ bool qt_initIcu(const QString &localeString)
             ptr_ucol_close = 0;
             ptr_ucol_strcoll = 0;
 
-            qWarning("Unable to find symbols in '%s'.", qPrintable(libName));
+            qWarning("Unable to find symbols in '%s'.", qPrintable(libicui18nName));
             status = ErrorLoading;
             return false;
         }
 
         // resolve libicuuc
-#ifdef Q_OS_WIN
-        libName = QStringLiteral("icuuc") + version;
-#else
-        libName = QStringLiteral("icuuc");
-#endif
-        QLibrary ucLib(libName, version);
-        if (!ucLib.load()) {
-            qWarning("Unable to load library '%s' version %s: %s",
-                     qPrintable(libName), qPrintable(version),
+        QLibrary &ucLib = icuLibraries()->libicuuc;
+        if (!loadIcuLibrary(ucLib, libicuucName)) {
+            qWarning("Unable to load library '%s' version " U_ICU_VERSION_SHORT ": %s",
+                     qPrintable(libicuucName),
                      qPrintable(ucLib.errorString()));
             status = ErrorLoading;
             return false;
@@ -144,7 +178,7 @@ bool qt_initIcu(const QString &localeString)
             ptr_u_strToUpper = 0;
             ptr_u_strToLower = 0;
 
-            qWarning("Unable to find symbols in '%s'", qPrintable(libName));
+            qWarning("Unable to find symbols in '%s'", qPrintable(libicuucName));
             status = ErrorLoading;
             return false;
         }