Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QJSEngine crashes when used in multithreading program
QtWS25 Last Chance

QJSEngine crashes when used in multithreading program

Scheduled Pinned Locked Moved Unsolved General and Desktop
multithreadingqjsengineqtscriptqmetaobject
3 Posts 2 Posters 809 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    mdma2
    wrote on last edited by mdma2
    #1

    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?

    jsulmJ 1 Reply Last reply
    0
    • M mdma2

      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?

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @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?

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      M 1 Reply Last reply
      1
      • jsulmJ jsulm

        @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?

        M Offline
        M Offline
        mdma2
        wrote on last edited by
        #3

        @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.

        1 Reply Last reply
        0

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved