Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is `moveToThread(nullptr)` a valid way to pull a QObject within the destination thread from its source thread?

Suppose if an object obj belongs to a QThread T1. Ideally being in Qhread T2's function, obj can't be 'pulled' from T1 to T2. This is mentioned in moveToThread() documentation:

Warning: This function is not thread-safe; the current thread must be same as the current thread affinity. In other words, this function can only "push" an object from the current thread to another thread, it cannot "pull" an object from any arbitrary thread to the current thread. There is one exception to this rule however: objects with no thread affinity can be "pulled" to the current thread.

This answer's point-3 suggests that actually it's a "lie-to-children". Because moveToThread(nullptr) will make an object to be movable from other threads.
Is it an idiomatic way without side-effects?

void FunctionRunningInT2 (QObject& obj) // `obj` belongs to thread `T1`
{
  obj.moveToThread(nullptr); // line-1 no event processing for obj!?
  obj.moveToThread(T2);      // line-2 is it OK ???
}

Add-on question: What will happen if any signal is emitted on obj between line-1 and line-2?
Rephrased: In case of obj.disconnect(), it doesn't accept any signals afterwards. However, the signals pending before disconnect() are still processed. Is it true for moveToThread(nullptr) as well? Or will it discard the pending signals too?

like image 891
iammilind Avatar asked Oct 16 '22 07:10

iammilind


1 Answers

As far as I understand, one just can't call moveToThread on an object living in a different thread. Even if the argument is nullptr, you'll end up with this messages from Qt:

QObject::moveToThread: Current thread ( ... ) is not the object's thread ( ... )
Cannot move to target thread (0x0)

and the object won't move from its thread.

A safe way to pull an object to a different thread is using a queued connection, i.e. giving the moveable object a slot like

class Moveable : public QObject
{
    Q_OBJECT
public:
    Moveable() : QObject(nullptr) {}

public slots:
    void moveMe(QThread * destination) { moveToThread(destination); }

This way, one can use a signal in the mover object, like

class Mover : public QObject
{
    Q_OBJECT
signals:
    void moveIt(QThread *);

connect them

connect(&mover, &Mover::moveIt, &moveable, &Moveable::moveMe, Qt::QueuedConnection);

and, wherever needed

mover.moveIt(QThread::currentThread());

or

mover.moveIt(to_whatever_thread);

If one just doesn't want to deal with implementing signals and connecting them, can safely use something like:

QMetaObject::invokeMethod(&moveable, "moveMe", Qt::QueuedConnection, Q_ARG(QThread*, destination_thread));

again exploiting a queued connection, but directly invoking the slot.

About the add-on question. Once the object is moved to thread 0x0, as stated here:

no event processing for this object or its children can happen, as they are no longer associated with any thread.

So, the object will stop receiving signals as well, not even from another object which has also been moved to 0x0.

like image 159
p-a-o-l-o Avatar answered Oct 18 '22 11:10

p-a-o-l-o