Is setContextProperty thread safe?



  • Is it OK to call QQmlContext::setContextProperty() from any thread or do I have to be carefull?


  • Moderators

    In general you should not call any UI related stuff from other threads than the UI thread.



  • Ok
    What I want to do is the following:

    I have a lengthy operation, which should run in a separate thread. The result of the operation should be exposed via setContextProperty().

    How do I do this properly?


  • Qt Champions 2016

    @RudolfVonKrugstein
    Since setContextProperty is not a slot, and you should not rely on functions to be thread-safe (most of Qt's API is reentrant, not TS) you could connect a lambda to your signal (assuming you have C++11 support), that will do the setContextProperty invocation. Or my personal preference would be to have a QObject (a subclass with the appropriate slot) that will receive the signal and handle it correctly (signal-slot calls are generally safe between threads).



  • Hey, let me try to put this into code. I would have (abbreviated)

    class SomeClass public QObject {
    signals:
      void setContextProperty(QString name, QVariant value);
    public slots:
      void settingContextProperty(QString name, QVariant value) {
          rootContext->setContextProperty(name,value);
      }
    }
    ...
    SomeClass::SomeClass() {
      connect(this,&SomeClass::setContextProperty,this,&SomeClass::settingContextProperty);
    }
    
    void SomeClass::function() {
        QtConcurrent::run([this](){
          emit setContextProperty("name",3);
        });
    }
    

    And that would ensure, that the signal is run in the UI thread? Or is there something else I have to do to ensure that the slot is run in the UI thread?


  • Qt Champions 2016

    @RudolfVonKrugstein
    It should, as far as I know, but I'm not well versed in the high level threading API. Maybe someone more experienced could confirm/deny it.

    EDIT:
    You could try it, and use the debugger to break the execution in your settingContextProperty() slot to make sure that it's executed in the main thread.



  • Is there a function with which I can test if the current thread is the UI thread? Than I can confirm it myself?


  • Qt Champions 2016

    @RudolfVonKrugstein
    The UI thread is just a label for the thread your widgets/windows live in. It's not necessarily the first one (or the main thread), although this is usually the case. That being said, I'm not aware of any special function to check whether it's the main one. You could use QThread::currentThread() to get the current thread but I'm not quite sure if it would suit your needs.



  • Ok, from here QObject, Thread Affinty, I guess that it runs in the thread (returned by thread()) of SomeClass. So that should be the UI thread.
    Cool!
    Now about that option with the lamba function. That would be like this (right?):

    class SomeClass public QObject {
    signals:
      void setContextProperty(QString name, QVariant value);
    }
    ...
    SomeClass::SomeClass() {
      connect(this,&SomeClass::setContextProperty,[this](QString name, QVariant value){
          rootContext->setContextProperty(name,value);
        });
    }
    
    void SomeClass::function() {
        QtConcurrent::run([this](){
          emit setContextProperty("name",3);
        });
    }
    

    Now the "Slot" has no associated object. In which thread would it run? Would this still work?


  • Qt Champions 2016

    @RudolfVonKrugstein
    Although I'm not an expert, reading this part of the documentation leads me to believe that your signal will be invoked directly from the worker thread (by means of the lambda functor). That aside, your slot is executed in the thread the receiving object lives in. I think, your code should run fine. To be on the safe side, you may explicitly specify the connection you're creating as queued, which will remove any ambiguity.

    EDIT:
    Also, if you don't need this signal for anything else, you could consider QMetaObject::invokeMethod for calling your slot directly from the lambda.


  • Moderators

    @RudolfVonKrugstein You are doing it wrong: you still call rootContext->setContextProperty(name,value) from the same thread!
    What you should do instead: you connect the void setContextProperty(QString name, QVariant value) signal to a slot of an object running in the UI thread (use Qt::QueuedConnection connection type) and call rootContext->setContextProperty(name,value) there.

    Don't forget to add Q_OBJECT macro to your SomeClass!


  • Qt Champions 2016

    @jsulm
    This is expected behavior (see original poster's second comment), and it is the whole point, unless you know that QQmlContext::setContextProperty() is thread safe, which I believe it isn't. Also he mentioned that he's running a lengthy operation before emitting the signal, and that's why he is using a separate thread.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.