QJSEngine crashes when used in multithreading program
-
I have a program where an QJSEngine object is used with several different threads. There should be no concurrent access - a thread is created, calling or evaluating something, and then this thread is deleted.
However there are random crashes of program while using QJSEngine. All crashes are occured inside the private QJSEngine functions related to allocating or freeing memory. Example:
// QV4::PersistentValueStorage::allocate() (qv4persistent.cpp): Value *PersistentValueStorage::allocate() { Page *p = static_cast<Page *>(firstPage); while (p) { if (p->header.freeList != -1) break; p = p->header.next; } if (!p) p = allocatePage(this); Value *v = p->values + p->header.freeList; p->header.freeList = v->int_32(); // !!! Get SEGFAULT here // .... }
I've found a bugreport that is similar to my problem. The reporter provided the minimal code that reproduces the problem:
int main(int argc, char** argv) { QCoreApplication app(argc, argv); QJSEngine engine; engine.installExtensions(QJSEngine::ConsoleExtension); engine.evaluate("var iteration = 0;"); auto function = engine.evaluate(R"(( function() { if (++iteration % 100 == 0) console.log(iteration); } ))"); QThread thread; thread.start(); QTimer timer; QObject::connect(&timer, &QTimer::timeout, &engine, [&]{function.call();}, Qt::DirectConnection); timer.moveToThread(&thread); // Comment it if you want to test QJSEngine in the main thread QMetaObject::invokeMethod(&timer, "start", Q_ARG(int, 0)); return app.exec(); }
Doing the same thing in the main thread (thread where QJSEngine was created) does not crash the program.
Could you please tell me how to make QJSEngine thread-safe for this situation? Thanks in advance.
UPD: I had an idea to wrap QJSValue::call() to a thread-safe function to force it call in the QJSEngine's object thread by using QMetaObject::invokeMethod():
QJSValue ThreadSafeQJSEngine::call(QJSValue value, const QJSValueList &args) { if (QThread::currentThread() != this->thread()) { QJSValue result; QMetaObject::invokeMethod(this, "call_imp", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QJSValue, result), Q_ARG(QJSValue, value), Q_ARG(const QJSValueList&, args) ); return result; } else { return call_imp(value, args); } }
But the problem is not gone and it crashes as usual. What am I doing wrong?
-
@mdma2 said in QJSEngine crashes when used in multithreading program:
I've found a bugreport that is similar to my problem
Did you read the comments there? Especially this one: "You have to create the engine in the same thread as the one where you are using it. And you cannot create your function in one thread and call it in a different one.".
You can't make it thread safe without changing its implementation. Is there a reason why you wantr to use it from different threads? -
@jsulm said in QJSEngine crashes when used in multithreading program:
@mdma2 said in QJSEngine crashes when used in multithreading program:
I've found a bugreport that is similar to my problem
Did you read the comments there? Especially this one: "You have to create the engine in the same thread as the one where you are using it. And you cannot create your function in one thread and call it in a different one.".
You can't make it thread safe without changing its implementation. Is there a reason why you wantr to use it from different threads?Yeah, there's need of evaluating JS code and accessing a QJSEngine object from different threads because of my program specificity. However there should be workaround to make it possible, like switching to the engine's thread before accessing to QJSEngine object. All I have found is using signal/slot system or QMetaObject:: invokeMethod (the last one is provided in the bugreport) to do that, but none of them work at the test code, I don't know why.