Protect all accesses to the global engine cache by a mutex

Otherwise, we'll end up with corrupted memory when using
QRegExp from multiple threads.

Amends bbdc1b5ccbb19405f997cd67ec53b2c4860105f7.

Change-Id: I9d35897629d0bc26503aa0c537c5f99013921fdd
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Lars Knoll 2018-04-23 21:08:10 +02:00 committed by Simon Hausmann
parent 960424f86b
commit a9fc91466c

View File

@ -3813,60 +3813,57 @@ struct QRegExpPrivate
}; };
#if !defined(QT_NO_REGEXP_OPTIM) #if !defined(QT_NO_REGEXP_OPTIM)
typedef QHash<QRegExpEngineKey, QRegExpEngine *> EngineCache; struct QRECache
Q_GLOBAL_STATIC(EngineCache, globalEngineCache) {
typedef QCache<QRegExpEngineKey, QRegExpEngine> UnusedEngineCache; typedef QHash<QRegExpEngineKey, QRegExpEngine *> EngineCache;
Q_GLOBAL_STATIC(UnusedEngineCache, globalUnusedEngineCache) typedef QCache<QRegExpEngineKey, QRegExpEngine> UnusedEngineCache;
static QBasicMutex globalEngineCacheMutex; EngineCache usedEngines;
UnusedEngineCache unusedEngines;
};
Q_GLOBAL_STATIC(QRECache, engineCache)
static QBasicMutex engineCacheMutex;
#endif // QT_NO_REGEXP_OPTIM #endif // QT_NO_REGEXP_OPTIM
static void derefEngine(QRegExpEngine *eng, const QRegExpEngineKey &key) static void derefEngine(QRegExpEngine *eng, const QRegExpEngineKey &key)
{ {
if (!eng->ref.deref()) {
#if !defined(QT_NO_REGEXP_OPTIM) #if !defined(QT_NO_REGEXP_OPTIM)
if (globalUnusedEngineCache()) { QMutexLocker locker(&engineCacheMutex);
QMutexLocker locker(&globalEngineCacheMutex); if (!eng->ref.deref()) {
QT_TRY { if (QRECache *c = engineCache()) {
globalUnusedEngineCache()->insert(key, eng, 4 + key.pattern.length() / 4); c->unusedEngines.insert(key, eng, 4 + key.pattern.length() / 4);
} QT_CATCH(const std::bad_alloc &) { c->usedEngines.remove(key);
// in case of an exception (e.g. oom), just delete the engine
delete eng;
}
if (globalEngineCache())
globalEngineCache()->remove(key);
} else { } else {
delete eng; delete eng;
} }
}
#else #else
Q_UNUSED(key); Q_UNUSED(key);
if (!eng->ref.deref())
delete eng; delete eng;
#endif #endif
}
} }
static void prepareEngine_helper(QRegExpPrivate *priv) static void prepareEngine_helper(QRegExpPrivate *priv)
{ {
bool initMatchState = !priv->eng; Q_ASSERT(!priv->eng);
#if !defined(QT_NO_REGEXP_OPTIM) #if !defined(QT_NO_REGEXP_OPTIM)
if (!priv->eng && globalUnusedEngineCache()) { QMutexLocker locker(&engineCacheMutex);
QMutexLocker locker(&globalEngineCacheMutex); if (QRECache *c = engineCache()) {
priv->eng = globalUnusedEngineCache()->take(priv->engineKey); priv->eng = c->unusedEngines.take(priv->engineKey);
if (!priv->eng && globalEngineCache()) if (!priv->eng)
priv->eng = globalEngineCache()->value(priv->engineKey); priv->eng = c->usedEngines.value(priv->engineKey);
if (priv->eng != 0) if (!priv->eng)
priv->eng = new QRegExpEngine(priv->engineKey);
else
priv->eng->ref.ref(); priv->eng->ref.ref();
c->usedEngines.insert(priv->engineKey, priv->eng);
return;
} }
#endif // QT_NO_REGEXP_OPTIM #endif // QT_NO_REGEXP_OPTIM
if (!priv->eng) priv->eng = new QRegExpEngine(priv->engineKey);
priv->eng = new QRegExpEngine(priv->engineKey);
#if !defined(QT_NO_REGEXP_OPTIM)
if (globalEngineCache())
globalEngineCache()->insert(priv->engineKey, priv->eng);
#endif
if (initMatchState)
priv->matchState.prepareForMatch(priv->eng);
} }
inline static void prepareEngine(QRegExpPrivate *priv) inline static void prepareEngine(QRegExpPrivate *priv)
@ -3874,6 +3871,7 @@ inline static void prepareEngine(QRegExpPrivate *priv)
if (priv->eng) if (priv->eng)
return; return;
prepareEngine_helper(priv); prepareEngine_helper(priv);
priv->matchState.prepareForMatch(priv->eng);
} }
static void prepareEngineForMatch(QRegExpPrivate *priv, const QString &str) static void prepareEngineForMatch(QRegExpPrivate *priv, const QString &str)