summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Ritt <ritt.ks@gmail.com>2013-03-15 01:28:40 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-20 07:02:29 +0100
commit36cb3f3f655a9090c82de609010cbfb88651a0f3 (patch)
treeec892d912da12ce9cbdf96ca3c4952909fa50cd7
parentef9c3c753dd22e675b334e1b045f93401e1f9df3 (diff)
Fix the font engines leaking
1. when there were some engines with ref > 1 in the cache, prior to calling QFontDatabase::{add,remove}ApplicationFont()/removeAllApplicationFonts() (QFontCache::clear() has never decreased engine's cache_count); 2. when the QFontEngineData's engine is not in cache i.e. the Box or Test font engine (~QFontEngineData() didn't free engines it keeps). Instead of using the font engine's (external) "cache_count" counter, QFontCache now references a given font engine every time it is inserted to the cache and dereferences exactly that number of times in clear(). Change-Id: I87677ebd24c1f4a81a53526f2e726e596b043c61 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--src/gui/text/qfont.cpp172
-rw-r--r--src/gui/text/qfont_p.h9
-rw-r--r--src/gui/text/qfontengine.cpp40
-rw-r--r--src/gui/text/qfontengine_p.h1
-rw-r--r--src/gui/text/qrawfont.cpp6
-rw-r--r--src/gui/text/qtextengine.cpp7
-rw-r--r--src/plugins/platforms/windows/qwindowsfontdatabase.cpp4
-rw-r--r--tests/auto/gui/text/qfontcache/.gitignore1
-rw-r--r--tests/auto/gui/text/qfontcache/qfontcache.pro8
-rw-r--r--tests/auto/gui/text/qfontcache/tst_qfontcache.cpp148
-rw-r--r--tests/auto/gui/text/text.pro2
11 files changed, 285 insertions, 113 deletions
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index 279d165322..fc694dc497 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -324,9 +324,11 @@ QFontEngineData::QFontEngineData()
QFontEngineData::~QFontEngineData()
{
for (int i = 0; i < QChar::ScriptCount; ++i) {
- if (engines[i])
- engines[i]->ref.deref();
- engines[i] = 0;
+ if (engines[i]) {
+ if (!engines[i]->ref.deref())
+ delete engines[i];
+ engines[i] = 0;
+ }
}
}
@@ -2647,24 +2649,6 @@ QFontCache::~QFontCache()
++it;
}
}
- EngineCache::ConstIterator it = engineCache.constBegin(),
- end = engineCache.constEnd();
- while (it != end) {
- if (--it.value().data->cache_count == 0) {
- if (it.value().data->ref.load() == 0) {
- FC_DEBUG("QFontCache::~QFontCache: deleting engine %p key=(%d / %g %g %d %d %d)",
- it.value().data, it.key().script, it.key().def.pointSize,
- it.key().def.pixelSize, it.key().def.weight, it.key().def.style,
- it.key().def.fixedPitch);
-
- delete it.value().data;
- } else {
- FC_DEBUG("QFontCache::~QFontCache: engine = %p still has refcount %d",
- it.value().data, it.value().data->ref.load());
- }
- }
- ++it;
- }
}
void QFontCache::clear()
@@ -2676,7 +2660,10 @@ void QFontCache::clear()
QFontEngineData *data = it.value();
for (int i = 0; i < QChar::ScriptCount; ++i) {
if (data->engines[i]) {
- data->engines[i]->ref.deref();
+ if (!data->engines[i]->ref.deref()) {
+ Q_ASSERT(engineCacheCount.value(data->engines[i]) == 0);
+ delete data->engines[i];
+ }
data->engines[i] = 0;
}
}
@@ -2684,23 +2671,25 @@ void QFontCache::clear()
}
}
- for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
- it != end; ++it) {
- if (it->data->ref.load() == 0) {
- delete it->data;
- it->data = 0;
- }
- }
-
- for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
- it != end; ++it) {
- if (it->data && it->data->ref.load() == 0) {
- delete it->data;
- it->data = 0;
+ bool mightHaveEnginesLeftForCleanup = true;
+ while (mightHaveEnginesLeftForCleanup) {
+ mightHaveEnginesLeftForCleanup = false;
+ for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
+ it != end; ++it) {
+ if (it.value().data && engineCacheCount.value(it.value().data) > 0) {
+ --engineCacheCount[it.value().data];
+ if (!it.value().data->ref.deref()) {
+ Q_ASSERT(engineCacheCount.value(it.value().data) == 0);
+ delete it.value().data;
+ mightHaveEnginesLeftForCleanup = true;
+ }
+ it.value().data = 0;
+ }
}
}
engineCache.clear();
+ engineCacheCount.clear();
}
@@ -2716,7 +2705,14 @@ QFontEngineData *QFontCache::findEngineData(const QFontDef &def) const
void QFontCache::insertEngineData(const QFontDef &def, QFontEngineData *engineData)
{
+#ifdef QFONTCACHE_DEBUG
FC_DEBUG("QFontCache: inserting new engine data %p", engineData);
+ if (engineDataCache.contains(def)) {
+ FC_DEBUG(" QFontCache already contains engine data %p for key=(%g %g %d %d %d)",
+ engineDataCache.value(def), def.pointSize,
+ def.pixelSize, def.weight, def.style, def.fixedPitch);
+ }
+#endif
engineDataCache.insert(def, engineData);
increaseCost(sizeof(QFontEngineData));
@@ -2741,13 +2737,22 @@ void QFontCache::updateHitCountAndTimeStamp(Engine &value)
FC_DEBUG("QFontCache: found font engine\n"
" %p: timestamp %4u hits %3u ref %2d/%2d, type '%s'",
value.data, value.timestamp, value.hits,
- value.data->ref.load(), value.data->cache_count,
+ value.data->ref.load(), engineCacheCount.value(value.data),
value.data->name());
}
void QFontCache::insertEngine(const Key &key, QFontEngine *engine, bool insertMulti)
{
- FC_DEBUG("QFontCache: inserting new engine %p", engine);
+#ifdef QFONTCACHE_DEBUG
+ FC_DEBUG("QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.load());
+ if (!insertMulti && engineCache.contains(key)) {
+ FC_DEBUG(" QFontCache already contains engine %p for key=(%g %g %d %d %d)",
+ engineCache.value(key).data, key.def.pointSize,
+ key.def.pixelSize, key.def.weight, key.def.style, key.def.fixedPitch);
+ }
+#endif
+
+ engine->ref.ref();
Engine data(engine);
data.timestamp = ++current_timestamp;
@@ -2756,12 +2761,9 @@ void QFontCache::insertEngine(const Key &key, QFontEngine *engine, bool insertMu
engineCache.insertMulti(key, data);
else
engineCache.insert(key, data);
-
// only increase the cost if this is the first time we insert the engine
- if (engine->cache_count == 0)
+ if (++engineCacheCount[engine] == 1)
increaseCost(engine->cache_cost);
-
- ++engine->cache_count;
}
void QFontCache::increaseCost(uint cost)
@@ -2825,11 +2827,8 @@ void QFontCache::timerEvent(QTimerEvent *)
EngineDataCache::ConstIterator it = engineDataCache.constBegin(),
end = engineDataCache.constEnd();
for (; it != end; ++it) {
-#ifdef QFONTCACHE_DEBUG
FC_DEBUG(" %p: ref %2d", it.value(), int(it.value()->ref.load()));
-#endif // QFONTCACHE_DEBUG
-
if (it.value()->ref.load() != 0)
in_use_cost += engine_data_cost;
}
@@ -2843,11 +2842,11 @@ void QFontCache::timerEvent(QTimerEvent *)
for (; it != end; ++it) {
FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes",
it.value().data, it.value().timestamp, it.value().hits,
- it.value().data->ref.load(), it.value().data->cache_count,
+ it.value().data->ref.load(), engineCacheCount.value(it.value().data),
it.value().data->cache_cost);
if (it.value().data->ref.load() != 0)
- in_use_cost += it.value().data->cache_cost / it.value().data->cache_count;
+ in_use_cost += it.value().data->cache_cost / engineCacheCount.value(it.value().data);
}
// attempt to make up for rounding errors
@@ -2893,29 +2892,25 @@ void QFontCache::timerEvent(QTimerEvent *)
FC_DEBUG(" CLEAN engine data:");
// clean out all unused engine data
- EngineDataCache::Iterator it = engineDataCache.begin(),
- end = engineDataCache.end();
- while (it != end) {
- if (it.value()->ref.load() != 0) {
+ EngineDataCache::Iterator it = engineDataCache.begin();
+ while (it != engineDataCache.end()) {
+ if (it.value()->ref.load() == 0) {
+ FC_DEBUG(" %p", it.value());
+ decreaseCost(sizeof(QFontEngineData));
+ delete it.value();
+ it = engineDataCache.erase(it);
+ } else {
++it;
- continue;
}
-
- EngineDataCache::Iterator rem = it++;
-
- decreaseCost(sizeof(QFontEngineData));
-
- FC_DEBUG(" %p", rem.value());
-
- delete rem.value();
- engineDataCache.erase(rem);
}
}
+ FC_DEBUG(" CLEAN engine:");
+
// clean out the engine cache just enough to get below our new max cost
- uint current_cost;
+ bool cost_decreased;
do {
- current_cost = total_cost;
+ cost_decreased = false;
EngineCache::Iterator it = engineCache.begin(),
end = engineCache.end();
@@ -2923,49 +2918,46 @@ void QFontCache::timerEvent(QTimerEvent *)
uint oldest = ~0u;
uint least_popular = ~0u;
- for (; it != end; ++it) {
- if (it.value().data->ref.load() != 0)
+ EngineCache::Iterator jt = end;
+
+ for ( ; it != end; ++it) {
+ if (it.value().data->ref.load() != engineCacheCount.value(it.value().data))
continue;
- if (it.value().timestamp < oldest &&
- it.value().hits <= least_popular) {
+ if (it.value().timestamp < oldest && it.value().hits <= least_popular) {
oldest = it.value().timestamp;
least_popular = it.value().hits;
+ jt = it;
}
}
- FC_DEBUG(" oldest %u least popular %u", oldest, least_popular);
-
- for (it = engineCache.begin(); it != end; ++it) {
- if (it.value().data->ref.load() == 0 &&
- it.value().timestamp == oldest &&
- it.value().hits == least_popular)
- break;
- }
-
+ it = jt;
if (it != end) {
FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, type '%s'",
it.value().data, it.value().timestamp, it.value().hits,
- it.value().data->ref.load(), it.value().data->cache_count,
+ it.value().data->ref.load(), engineCacheCount.value(it.value().data),
it.value().data->name());
- if (--it.value().data->cache_count == 0) {
- FC_DEBUG(" DELETE: last occurrence in cache");
-
- decreaseCost(it.value().data->cache_cost);
- delete it.value().data;
- } else {
- /*
- this particular font engine is in the cache multiple
- times... set current_cost to zero, so that we can
- keep looping to get rid of all occurrences
- */
- current_cost = 0;
+ QFontEngine *fontEngine = it.value().data;
+ // get rid of all occurrences
+ it = engineCache.begin();
+ while (it != engineCache.end()) {
+ if (it.value().data == fontEngine) {
+ fontEngine->ref.deref();
+ it = engineCache.erase(it);
+ } else {
+ ++it;
+ }
}
+ // and delete the last occurrence
+ Q_ASSERT(fontEngine->ref.load() == 0);
+ decreaseCost(fontEngine->cache_cost);
+ delete fontEngine;
+ engineCacheCount.remove(fontEngine);
- engineCache.erase(it);
+ cost_decreased = true;
}
- } while (current_cost != total_cost && total_cost > max_cost);
+ } while (cost_decreased && total_cost > max_cost);
}
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
index a35896e763..4cbf51d59c 100644
--- a/src/gui/text/qfont_p.h
+++ b/src/gui/text/qfont_p.h
@@ -193,9 +193,8 @@ private:
};
-class QFontCache : public QObject
+class Q_AUTOTEST_EXPORT QFontCache : public QObject
{
- Q_OBJECT
public:
// note: these static functions work on a per-thread basis
static QFontCache *instance();
@@ -205,8 +204,7 @@ public:
~QFontCache();
void clear();
- // universal key structure. QFontEngineDatas and QFontEngines are cached using
- // the same keys
+
struct Key {
Key() : script(0), screen(0) { }
Key(const QFontDef &d, int c, int s = 0)
@@ -245,13 +243,14 @@ public:
typedef QMap<Key,Engine> EngineCache;
EngineCache engineCache;
+ QHash<QFontEngine *, int> engineCacheCount;
QFontEngine *findEngine(const Key &key);
void updateHitCountAndTimeStamp(Engine &value);
void insertEngine(const Key &key, QFontEngine *engine, bool insertMulti = false);
- private:
+private:
void increaseCost(uint cost);
void decreaseCost(uint cost);
void timerEvent(QTimerEvent *event);
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 2bf33863f7..dbe56889da 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -170,6 +170,28 @@ static void hb_freeFace(void *face)
qHBFreeFace((HB_Face)face);
}
+
+#ifdef QT_BUILD_INTERNAL
+// for testing purpose only, not thread-safe!
+static QList<QFontEngine *> *enginesCollector = 0;
+
+Q_AUTOTEST_EXPORT void QFontEngine_startCollectingEngines()
+{
+ delete enginesCollector;
+ enginesCollector = new QList<QFontEngine *>();
+}
+
+Q_AUTOTEST_EXPORT QList<QFontEngine *> QFontEngine_stopCollectingEngines()
+{
+ Q_ASSERT(enginesCollector);
+ QList<QFontEngine *> ret = *enginesCollector;
+ delete enginesCollector;
+ enginesCollector = 0;
+ return ret;
+}
+#endif // QT_BUILD_INTERNAL
+
+
// QFontEngine
QFontEngine::QFontEngine()
@@ -177,7 +199,6 @@ QFontEngine::QFontEngine()
font_(0), font_destroy_func(0),
face_(0), face_destroy_func(0)
{
- cache_count = 0;
fsType = 0;
symbol = false;
@@ -194,6 +215,11 @@ QFontEngine::QFontEngine()
glyphFormat = -1;
m_subPixelPositionCount = 0;
+
+#ifdef QT_BUILD_INTERNAL
+ if (enginesCollector)
+ enginesCollector->append(this);
+#endif
}
QFontEngine::~QFontEngine()
@@ -208,6 +234,11 @@ QFontEngine::~QFontEngine()
face_destroy_func(face_);
face_ = 0;
}
+
+#ifdef QT_BUILD_INTERNAL
+ if (enginesCollector)
+ enginesCollector->removeOne(this);
+#endif
}
QFixed QFontEngine::lineThickness() const
@@ -1384,11 +1415,8 @@ QFontEngineMulti::~QFontEngineMulti()
{
for (int i = 0; i < engines.size(); ++i) {
QFontEngine *fontEngine = engines.at(i);
- if (fontEngine) {
- fontEngine->ref.deref();
- if (fontEngine->cache_count == 0 && fontEngine->ref.load() == 0)
- delete fontEngine;
- }
+ if (fontEngine && !fontEngine->ref.deref())
+ delete fontEngine;
}
}
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index 3c7746b3c9..f0f8713f74 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_p.h
@@ -273,7 +273,6 @@ public:
mutable qt_destroy_func_t face_destroy_func;
uint cache_cost; // amount of mem used in kb by the font
- int cache_count;
uint fsType : 16;
bool symbol;
struct KernPair {
diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp
index 5781b49eab..f52b46eeae 100644
--- a/src/gui/text/qrawfont.cpp
+++ b/src/gui/text/qrawfont.cpp
@@ -738,8 +738,7 @@ void QRawFont::setPixelSize(qreal pixelSize)
if (d->fontEngine != 0)
d->fontEngine->ref.ref();
- oldFontEngine->ref.deref();
- if (oldFontEngine->cache_count == 0 && oldFontEngine->ref.load() == 0)
+ if (!oldFontEngine->ref.deref())
delete oldFontEngine;
}
@@ -750,8 +749,7 @@ void QRawFontPrivate::cleanUp()
{
platformCleanUp();
if (fontEngine != 0) {
- fontEngine->ref.deref();
- if (fontEngine->cache_count == 0 && fontEngine->ref.load() == 0)
+ if (!fontEngine->ref.deref())
delete fontEngine;
fontEngine = 0;
}
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index ad85fedcd0..2fa7f0232d 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -1253,11 +1253,8 @@ void QTextEngine::shape(int item) const
static inline void releaseCachedFontEngine(QFontEngine *fontEngine)
{
- if (fontEngine) {
- fontEngine->ref.deref();
- if (fontEngine->cache_count == 0 && fontEngine->ref.load() == 0)
- delete fontEngine;
- }
+ if (fontEngine && !fontEngine->ref.deref())
+ delete fontEngine;
}
void QTextEngine::resetFontEngineCache()
diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp
index 2fa691347d..c59b0edf78 100644
--- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp
+++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp
@@ -1097,11 +1097,11 @@ QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal
if (request.family != fontEngine->fontDef.family) {
qWarning("%s: Failed to load font. Got fallback instead: %s",
__FUNCTION__, qPrintable(fontEngine->fontDef.family));
- if (fontEngine->cache_count == 0 && fontEngine->ref.load() == 0)
+ if (fontEngine->ref.load() == 0)
delete fontEngine;
fontEngine = 0;
} else {
- Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref.load() == 0);
+ Q_ASSERT(fontEngine->ref.load() == 0);
// Override the generated font name
static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName);
diff --git a/tests/auto/gui/text/qfontcache/.gitignore b/tests/auto/gui/text/qfontcache/.gitignore
new file mode 100644
index 0000000000..e963ab010a
--- /dev/null
+++ b/tests/auto/gui/text/qfontcache/.gitignore
@@ -0,0 +1 @@
+tst_qfontcache
diff --git a/tests/auto/gui/text/qfontcache/qfontcache.pro b/tests/auto/gui/text/qfontcache/qfontcache.pro
new file mode 100644
index 0000000000..313cd78714
--- /dev/null
+++ b/tests/auto/gui/text/qfontcache/qfontcache.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+CONFIG += parallel_test
+TARGET = tst_qfontcache
+QT += testlib
+QT += core-private gui-private
+SOURCES += tst_qfontcache.cpp
+
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp
new file mode 100644
index 0000000000..a85fadfce7
--- /dev/null
+++ b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#include <qfont.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+
+class tst_QFontCache : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QFontCache();
+ virtual ~tst_QFontCache();
+
+private slots:
+ void clear();
+};
+
+QT_BEGIN_NAMESPACE
+extern void qt_setQtEnableTestFont(bool value); // qfontdatabase.cpp
+
+#ifdef QT_BUILD_INTERNAL
+// qfontengine.cpp
+extern void QFontEngine_startCollectingEngines();
+extern QList<QFontEngine *> QFontEngine_stopCollectingEngines();
+#endif
+QT_END_NAMESPACE
+
+tst_QFontCache::tst_QFontCache()
+{
+}
+
+tst_QFontCache::~tst_QFontCache()
+{
+}
+
+void tst_QFontCache::clear()
+{
+#ifdef QT_BUILD_INTERNAL
+ QFontEngine_startCollectingEngines();
+#else
+ // must not crash, at very least ;)
+#endif
+
+ QFontEngine *fontEngine = 0;
+
+ {
+ // we're never caching the box (and the "test") font engines
+ // let's ensure we're not leaking them as well as the cached ones
+ qt_setQtEnableTestFont(true);
+
+ QFont f;
+ f.setFamily("__Qt__Box__Engine__");
+ f.exactMatch(); // loads engine
+ }
+ {
+ QFontDatabase db;
+
+ QFont f;
+ f.setStyleHint(QFont::Serif);
+ const QString familyForHint(f.defaultFamily());
+
+ // it should at least return a family that is available
+ QVERIFY(db.hasFamily(familyForHint));
+ f.exactMatch(); // loads engine
+
+ fontEngine = QFontPrivate::get(f)->engineForScript(QChar::Script_Common);
+ QVERIFY(fontEngine);
+ QVERIFY(QFontCache::instance()->engineCacheCount.value(fontEngine) > 0); // ensure it is cached
+
+ // acquire the engine to use it somewhere else:
+ // (e.g. like the we do in QFontSubset() or like QRawFont does in fromFont())
+ fontEngine->ref.ref();
+
+ // cache the engine once again; there is a special case when the engine is cached more than once
+ QFontCache::instance()->insertEngine(QFontCache::Key(QFontDef(), 0, 0), fontEngine);
+ }
+
+ // use it:
+ // e.g. fontEngine->stringToCMap(..);
+
+ // and whilst it is alive, don't hesitate to add/remove the app-local fonts:
+ // (QFontDatabase::{add,remove}ApplicationFont() clears the cache)
+ QFontCache::instance()->clear();
+
+ // release the acquired engine:
+ if (fontEngine) {
+ if (!fontEngine->ref.deref())
+ delete fontEngine;
+ fontEngine = 0;
+ }
+
+ // we may even exit the application now:
+ QFontCache::instance()->cleanup();
+
+#ifdef QT_BUILD_INTERNAL
+ QList<QFontEngine *> leakedEngines = QFontEngine_stopCollectingEngines();
+for (int i = 0; i < leakedEngines.size(); ++i) qWarning() << i << leakedEngines.at(i) << leakedEngines.at(i)->ref.load();
+ // and we are not leaking!
+ QCOMPARE(leakedEngines.size(), 0);
+#endif
+}
+
+QTEST_MAIN(tst_QFontCache)
+#include "tst_qfontcache.moc"
diff --git a/tests/auto/gui/text/text.pro b/tests/auto/gui/text/text.pro
index 5055ab61a3..6c0def4d63 100644
--- a/tests/auto/gui/text/text.pro
+++ b/tests/auto/gui/text/text.pro
@@ -3,6 +3,7 @@ SUBDIRS=\
qabstracttextdocumentlayout \
qcssparser \
qfont \
+ qfontcache \
qfontdatabase \
qfontmetrics \
qglyphrun \
@@ -27,6 +28,7 @@ contains(QT_CONFIG, OdfWriter):SUBDIRS += qzip qtextodfwriter
win32:SUBDIRS -= qtextpiecetable
!contains(QT_CONFIG, private_tests): SUBDIRS -= \
+ qfontcache \
qcssparser \
qstatictext \
qtextlayout \