aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-03-04 11:46:58 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-03-05 11:10:42 +0000
commita423244b25cb2650caf2f48be39b8960a4ac9dc4 (patch)
tree5cb96c46d8d1a5825436ce6b30d22e03a6e40a0a
parent2b2e9f697776fe4531dbd59ff7ab58a7f0092558 (diff)
ExecutionEngine: Move initialization of statics into separate method
... and do it only for the first engine (ie engineId == 1). The engineId is determined using atomic operations, so this is safe now. Task-number: QTBUG-73271 Change-Id: Ife38213fe04e26f35425a29230a2e3b586572dd2 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 6fb63032966cc27cc11ce3686e97b185fdb3cb6b) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/qml/jsruntime/qv4engine.cpp139
-rw-r--r--src/qml/jsruntime/qv4engine_p.h16
2 files changed, 87 insertions, 68 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index e6b17a1b3a..a90d165c59 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -151,14 +151,23 @@ DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
using namespace QV4;
+// While engineSerial is odd the statics haven't been initialized. The engine that receives ID 1
+// initializes the statics and sets engineSerial to 2 afterwards.
+// Each engine does engineSerial.fetchAndAddOrdered(2) on creation. Therefore engineSerial stays
+// odd while the statics are being initialized, and stays even afterwards.
+// Any further engines created while the statics are being initialized busy-wait until engineSerial
+// is even.
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
+int ExecutionEngine::s_maxCallDepth = -1;
+int ExecutionEngine::s_jitCallCountThreshold = 3;
+int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024;
+int ExecutionEngine::s_maxGCStackSize = 2 * 1024 * 1024;
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
{
return b->engine()->throwTypeError();
}
-qint32 ExecutionEngine::maxCallDepth = -1;
template <typename ReturnType>
ReturnType convertJSValueToVariantType(const QJSValue &value)
@@ -328,6 +337,59 @@ static QSequentialIterable jsvalueToSequence (const QJSValue& value) {
return QSequentialIterable(QMetaSequence(&sequence), &value);
}
+void ExecutionEngine::initializeStaticMembers()
+{
+ bool ok = false;
+
+ const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxJSStackSize > 0)
+ s_maxJSStackSize = envMaxJSStackSize;
+
+ const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxGCStackSize > 0)
+ s_maxGCStackSize = envMaxGCStackSize;
+
+ if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
+ s_maxCallDepth = std::numeric_limits<qint32>::max();
+ } else {
+ ok = false;
+ s_maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
+ if (!ok || s_maxCallDepth <= 0) {
+#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
+#ifdef Q_OS_QNX
+ s_maxCallDepth = 640; // QNX's stack is only 512k by default
+#else
+ s_maxCallDepth = 1234;
+#endif
+#else
+ // no (tail call) optimization is done, so there'll be a lot mare stack frames active
+ s_maxCallDepth = 200;
+#endif
+ }
+ }
+
+ Q_ASSERT(s_maxCallDepth > 0);
+
+ ok = false;
+ s_jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
+ if (!ok)
+ s_jitCallCountThreshold = 3;
+ if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
+ s_jitCallCountThreshold = std::numeric_limits<int>::max();
+
+ qMetaTypeId<QJSValue>();
+ qMetaTypeId<QList<int> >();
+
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
+ QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
+ QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
+ QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
+ QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
+}
+
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
@@ -336,7 +398,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
, publicEngine(jsEngine)
- , m_engineId(engineSerial.fetchAndAddOrdered(1))
+ , m_engineId(engineSerial.fetchAndAddOrdered(2))
, regExpCache(nullptr)
, m_multiplyWrappedQObjects(nullptr)
#if QT_CONFIG(qml_jit)
@@ -347,45 +409,23 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
#endif
, m_qmlEngine(nullptr)
{
- bool ok = false;
- const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
- if (ok && envMaxJSStackSize > 0)
- m_maxJSStackSize = envMaxJSStackSize;
-
- const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
- if (ok && envMaxGCStackSize > 0)
- m_maxGCStackSize = envMaxGCStackSize;
-
- memoryManager = new QV4::MemoryManager(this);
-
- if (maxCallDepth == -1) {
- if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
- maxCallDepth = std::numeric_limits<qint32>::max();
- } else {
- ok = false;
- maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
- if (!ok || maxCallDepth <= 0) {
-#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
-#ifdef Q_OS_QNX
- maxCallDepth = 640; // QNX's stack is only 512k by default
-#else
- maxCallDepth = 1234;
-#endif
-#else
- // no (tail call) optimization is done, so there'll be a lot mare stack frames active
- maxCallDepth = 200;
-#endif
- }
+ if (m_engineId == 1) {
+ initializeStaticMembers();
+ engineSerial.storeRelease(2); // make it even
+ } else if (Q_UNLIKELY(m_engineId & 1)) {
+ // This should be rare. You usually don't create lots of engines at the same time.
+ while (engineSerial.loadAcquire() & 1) {
+ QThread::yieldCurrentThread();
}
}
- Q_ASSERT(maxCallDepth > 0);
+ memoryManager = new QV4::MemoryManager(this);
// reserve space for the JS stack
// we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
// allocated outside of JIT'ed methods.
- *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *jsStack = WTF::PageAllocation::allocate(
+ s_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
jsStackBase = (Value *)jsStack->base();
#ifdef V4_USE_VALGRIND
VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024);
@@ -393,19 +433,10 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsStackTop = jsStackBase;
- *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
+ *gcStack = WTF::PageAllocation::allocate(s_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
/* writable */ true, /* executable */ false,
/* includesGuardPages */ true);
- {
- ok = false;
- jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
- if (!ok)
- jitCallCountThreshold = 3;
- if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
- jitCallCountThreshold = std::numeric_limits<int>::max();
- }
-
exceptionValue = jsAlloca(1);
*exceptionValue = Encode::undefined();
globalObject = static_cast<Object *>(jsAlloca(1));
@@ -416,7 +447,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
- jsStackLimit = jsStackBase + m_maxJSStackSize/sizeof(Value);
+ jsStackLimit = jsStackBase + s_maxJSStackSize/sizeof(Value);
identifierTable = new IdentifierTable(this);
@@ -863,18 +894,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
- qMetaTypeId<QJSValue>();
- qMetaTypeId<QList<int> >();
-
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
- QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
- QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
- QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
- QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
-
QV4::QObjectWrapper::initializeBindings(this);
m_delayedCallQueue.init(this);
@@ -1994,12 +2013,12 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType type, const void *dat
int ExecutionEngine::maxJSStackSize() const
{
- return m_maxJSStackSize;
+ return s_maxJSStackSize;
}
int ExecutionEngine::maxGCStackSize() const
{
- return m_maxGCStackSize;
+ return s_maxGCStackSize;
}
ReturnedValue ExecutionEngine::global()
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index be115cb064..3a149ba534 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -169,8 +169,6 @@ class ReactionHandler;
struct Q_QML_EXPORT ExecutionEngine : public EngineBase
{
private:
- static qint32 maxCallDepth;
-
friend struct ExecutionContextSaver;
friend struct ExecutionContext;
friend struct Heap::ExecutionContext;
@@ -699,7 +697,7 @@ public:
return false;
if (f) {
return !f->aotFunction && !f->isGenerator()
- && f->interpreterCallCount >= jitCallCountThreshold;
+ && f->interpreterCallCount >= s_jitCallCountThreshold;
}
return true;
#else
@@ -776,13 +774,18 @@ public:
private:
QV4::ReturnedValue fromData(QMetaType type, const void *ptr, const QVariant *variant = nullptr);
+ static void initializeStaticMembers();
+
+ static int s_maxCallDepth;
+ static int s_jitCallCountThreshold;
+ static int s_maxJSStackSize;
+ static int s_maxGCStackSize;
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
QScopedPointer<QV4::Profiling::Profiler> m_profiler;
#endif
QSet<QString> m_illegalNames;
- int jitCallCountThreshold;
// used by generated Promise objects to handle 'then' events
QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler;
@@ -801,9 +804,6 @@ private:
QHash<QString, quint32> m_consoleCount;
QVector<Deletable *> m_extensionData;
-
- int m_maxJSStackSize = 4 * 1024 * 1024;
- int m_maxGCStackSize = 2 * 1024 * 1024;
};
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
@@ -819,7 +819,7 @@ struct ExecutionEngineCallDepthRecorder
inline bool ExecutionEngine::checkStackLimits()
{
- if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) {
+ if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= s_maxCallDepth))) {
throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
return true;
}