QRegularExpression: refactor the handling of the study data
Consider the following situation: - threads A and B have shallow copies of the same QRegularExpression - threads A and B both call match() on a string - thread A calls optimizePattern(), which doesn't optimize - thread B calls optimizePattern(), which does optimize, and sets studyData - thread A uses studyData (set by B) A needs to properly acquire the memory pointed by studyData (which, in turn, needs to be released by B). This commit implements that. (Before, we used to return a copy of the current studyData from optimizePattern(), so A didn't see that B optimized the pattern and set studyData). Change-Id: I9e4741a3d3229905c247491a07099519815680bb Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
14b2d4cd38
commit
6b821a3dc8
@ -50,6 +50,7 @@
|
|||||||
#include <QtCore/qdebug.h>
|
#include <QtCore/qdebug.h>
|
||||||
#include <QtCore/qthreadstorage.h>
|
#include <QtCore/qthreadstorage.h>
|
||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
|
#include <QtCore/qatomic.h>
|
||||||
|
|
||||||
#include <pcre.h>
|
#include <pcre.h>
|
||||||
|
|
||||||
@ -801,7 +802,7 @@ struct QRegularExpressionPrivate : QSharedData
|
|||||||
void cleanCompiledPattern();
|
void cleanCompiledPattern();
|
||||||
void compilePattern();
|
void compilePattern();
|
||||||
void getPatternInfo();
|
void getPatternInfo();
|
||||||
pcre16_extra *optimizePattern();
|
void optimizePattern();
|
||||||
|
|
||||||
QRegularExpressionMatchPrivate *doMatch(const QString &subject,
|
QRegularExpressionMatchPrivate *doMatch(const QString &subject,
|
||||||
int offset,
|
int offset,
|
||||||
@ -827,7 +828,7 @@ struct QRegularExpressionPrivate : QSharedData
|
|||||||
// objects themselves; when the private is copied (i.e. a detach happened)
|
// objects themselves; when the private is copied (i.e. a detach happened)
|
||||||
// they are set to 0
|
// they are set to 0
|
||||||
pcre16 *compiledPattern;
|
pcre16 *compiledPattern;
|
||||||
pcre16_extra *studyData;
|
QAtomicPointer<pcre16_extra> studyData;
|
||||||
const char *errorString;
|
const char *errorString;
|
||||||
int errorOffset;
|
int errorOffset;
|
||||||
int capturingCount;
|
int capturingCount;
|
||||||
@ -934,10 +935,10 @@ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPri
|
|||||||
void QRegularExpressionPrivate::cleanCompiledPattern()
|
void QRegularExpressionPrivate::cleanCompiledPattern()
|
||||||
{
|
{
|
||||||
pcre16_free(compiledPattern);
|
pcre16_free(compiledPattern);
|
||||||
pcre16_free_study(studyData);
|
pcre16_free_study(studyData.load());
|
||||||
usedCount = 0;
|
usedCount = 0;
|
||||||
compiledPattern = 0;
|
compiledPattern = 0;
|
||||||
studyData = 0;
|
studyData.store(0);
|
||||||
usingCrLfNewlines = false;
|
usingCrLfNewlines = false;
|
||||||
errorOffset = -1;
|
errorOffset = -1;
|
||||||
capturingCount = 0;
|
capturingCount = 0;
|
||||||
@ -967,7 +968,7 @@ void QRegularExpressionPrivate::compilePattern()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Q_ASSERT(errorCode == 0);
|
Q_ASSERT(errorCode == 0);
|
||||||
Q_ASSERT(studyData == 0); // studying (=>optimizing) is always done later
|
Q_ASSERT(studyData.load() == 0); // studying (=>optimizing) is always done later
|
||||||
errorOffset = -1;
|
errorOffset = -1;
|
||||||
|
|
||||||
getPatternInfo();
|
getPatternInfo();
|
||||||
@ -979,7 +980,7 @@ void QRegularExpressionPrivate::compilePattern()
|
|||||||
void QRegularExpressionPrivate::getPatternInfo()
|
void QRegularExpressionPrivate::getPatternInfo()
|
||||||
{
|
{
|
||||||
Q_ASSERT(compiledPattern);
|
Q_ASSERT(compiledPattern);
|
||||||
Q_ASSERT(studyData == 0);
|
Q_ASSERT(studyData.load() == 0);
|
||||||
|
|
||||||
pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_CAPTURECOUNT, &capturingCount);
|
pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_CAPTURECOUNT, &capturingCount);
|
||||||
|
|
||||||
@ -1101,17 +1102,18 @@ static bool isJitEnabled()
|
|||||||
leaving studyData to NULL); but before calling pcre16_exec to perform the
|
leaving studyData to NULL); but before calling pcre16_exec to perform the
|
||||||
match, another thread performs the studying and sets studyData to something
|
match, another thread performs the studying and sets studyData to something
|
||||||
else. Although the assignment to studyData is itself atomic, the release of
|
else. Although the assignment to studyData is itself atomic, the release of
|
||||||
the memory pointed by studyData isn't. Therefore, the current studyData
|
the memory pointed by studyData isn't. Therefore, we work on a local copy
|
||||||
value is returned and used by doMatch.
|
(localStudyData) before using storeRelease on studyData. In doMatch there's
|
||||||
|
the corresponding loadAcquire.
|
||||||
*/
|
*/
|
||||||
pcre16_extra *QRegularExpressionPrivate::optimizePattern()
|
void QRegularExpressionPrivate::optimizePattern()
|
||||||
{
|
{
|
||||||
Q_ASSERT(compiledPattern);
|
Q_ASSERT(compiledPattern);
|
||||||
|
|
||||||
QMutexLocker lock(&mutex);
|
QMutexLocker lock(&mutex);
|
||||||
|
|
||||||
if (studyData || (++usedCount != qt_qregularexpression_optimize_after_use_count))
|
if (studyData.load() || (++usedCount != qt_qregularexpression_optimize_after_use_count))
|
||||||
return studyData;
|
return;
|
||||||
|
|
||||||
static const bool enableJit = isJitEnabled();
|
static const bool enableJit = isJitEnabled();
|
||||||
|
|
||||||
@ -1120,15 +1122,15 @@ pcre16_extra *QRegularExpressionPrivate::optimizePattern()
|
|||||||
studyOptions |= PCRE_STUDY_JIT_COMPILE;
|
studyOptions |= PCRE_STUDY_JIT_COMPILE;
|
||||||
|
|
||||||
const char *err;
|
const char *err;
|
||||||
studyData = pcre16_study(compiledPattern, studyOptions, &err);
|
pcre16_extra * const localStudyData = pcre16_study(compiledPattern, studyOptions, &err);
|
||||||
|
|
||||||
if (studyData && studyData->flags & PCRE_EXTRA_EXECUTABLE_JIT)
|
if (localStudyData && localStudyData->flags & PCRE_EXTRA_EXECUTABLE_JIT)
|
||||||
pcre16_assign_jit_stack(studyData, qtPcreCallback, 0);
|
pcre16_assign_jit_stack(localStudyData, qtPcreCallback, 0);
|
||||||
|
|
||||||
if (!studyData && err)
|
if (!localStudyData && err)
|
||||||
qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err);
|
qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err);
|
||||||
|
|
||||||
return studyData;
|
studyData.storeRelease(localStudyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -1231,7 +1233,12 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString
|
|||||||
capturingCount + 1);
|
capturingCount + 1);
|
||||||
|
|
||||||
// this is mutex protected
|
// this is mutex protected
|
||||||
const pcre16_extra *currentStudyData = const_cast<QRegularExpressionPrivate *>(this)->optimizePattern();
|
const_cast<QRegularExpressionPrivate *>(this)->optimizePattern();
|
||||||
|
|
||||||
|
// work with a local copy of the study data, as we are running pcre_exec
|
||||||
|
// potentially more than once, and we don't want to run call it
|
||||||
|
// with different study data
|
||||||
|
const pcre16_extra * const currentStudyData = studyData.loadAcquire();
|
||||||
|
|
||||||
int pcreOptions = convertToPcreOptions(matchOptions);
|
int pcreOptions = convertToPcreOptions(matchOptions);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user