5

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?

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Even if this way turns out to actually work, I would not use it because there's no guarantee it will continue to work with future versions of Qt. The documentation saying the proper way to use `moveToThread` is to push object from its thread to another thread means it is Qt's public contract which its future versions should comply with. In other obscure cases there won't be any guarantee. I have once implemented a sort of a workaround for "pulling" the object from another thread - [here](https://github.com/d1vanov/quentier/blob/master/lib/utility/QObjectThreadMover.cpp). – Dmitry Jan 10 '20 at 10:45
  • Note that in the code example you give `obj.moveToThread(nullptr)` is called from thread T2 on `obj` which has thread affinity T1. So, technically, you're potentially accessing `obj` from multiple threads without any means of synchronization which would be UB. – G.M. Jan 10 '20 at 12:14
  • @G.M., this is just an example pseudo code with assumed synchronisation in place. When I practically run this code in multi-threaded environment, I do see some problems of thread movement for the objects. Hence this question. – iammilind Jan 10 '20 at 12:24

1 Answers1

3

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.

p-a-o-l-o
  • 9,807
  • 2
  • 22
  • 35
  • Regarding add-on question: Will the `moveToThread(nullptr)` allow/disallow any further signals to be catered on the `obj`? It's slightly unclear in the answer. – iammilind Jan 15 '20 at 16:49
  • Still unclear. :-) Let me rephrase: In case of `obj.disconnect()`, it doesn't accept any signals afterwards. However, the signals pending before `disconnect()` are processed. Is it true for `moveToThread(nullptr)` too? Or will it discard the pending signals too? – iammilind Jan 16 '20 at 03:28
  • @iammilind once the object thread is `0x0`, whatever *addressed* to the object will be discarded, since there's no indication about the thread in which to execute any call. – p-a-o-l-o Jan 16 '20 at 08:52