How can I detect the QObject :: moveToThread () error in Qt5?

The QObject :: moveToThread () documentation for Qt5.3 explains what a method moveToThread()

can do if an object has a parent. How can I detect this glitch in my code?

I realize that just making sure my object has no parent is probably good enough, but as a defensive programming practice, I would like to check the return value from all calls that might fail.

EDIT: I want to emphasize here after some answers that I fully know that I can check if the parent is 0 prior to calling moveToThread. I'm looking for possible ways to empirically determine that a challenge moveToThread

actually succeeded.

+3


source to share


2 answers


To reliably get the result moveToThread()

, catch the event of the ThreadChange

object undergoing movement (by overriding QObject::event()

or setting an event filter) and store whether the event was seen in a local variable reference:

 static bool moveObjectToThread(QObject *o, QThread *t) {
     class EventFilter : public QObject {
         bool &result;
     public:
         explicit EventFilter(bool &result, QObject *parent = nullptr)
             : QObject(parent), result(result) {}
         bool eventFilter(QObject *, QEvent *e) override {
             if (e->type() == QEvent::ThreadChange)
                 result = true;
             return false;
         }
     };
     bool result = false;
     if (o) {
         o->installEventFilter(new EventFilter(result, o));
         o->moveToThread(t);
     }
     return result;
 }

      

Long story:

  • The documentation is wrong. You can move QObject

    with parent to another thread. To do this, you just need to call moveToThread()

    on the root hierarchy QObject

    that you want to move and all children will be moved as well (this is done to ensure that parents and their children are always in the same thread). I know this is an academic distinction. Just be thorough here.

  • The call moveToThread()

    can also fail if QObject

    thread()

    not == QThread::currentThread()

    (i.e. you can only push the object, but not pull it from another thread).

  • The last sentence is lie-to-children . You can pull the object if it was previously detached by any thread (by calling moveToThread(nullptr)

    .

  • When the affinity of a stream changes, the object is dispatched as an event QEvent::ThreadChange

    .

Now, your question was how to reliably detect that this has happened. Answer: It's not easy. The obvious first, comparing the return value QObject::thread()

after the call moveToThread()

with the argument is moveToThread()

not a good idea as QObject::thread()

it is not (documented) thread safe (cf. implementation ).

Why is this a problem?



As soon as it moveToThread()

returns, the moved thread may have already started executing the "object", ie. events for this object. As part of this processing, the object can be deleted. In this case, the next call QObject::thread()

on the original stream will dereference the deleted data. Or the new thread will pass the object to another thread, in which case reading the member variable in the call thread()

in the original thread will race against writing to the same member variable moveToThread()

in the new thread.

Bottomline: Accessing moveToThread()

ed object from original stream is undefined behavior. Do not do that.

The only way forward is to use an event ThreadChange

. This event is dispatched after all failures have been checked, but crucially, from the original thread (see implementation ; it's just wrong to dispatch such an event if nothing really happened).

You can check for an event either by subclassing the object you moved to and overriding QObject::event()

, or by setting an event filter on the moved object.

The event filter approach is better, of course, because you can use it for any QObject

, not just the ones you can or want to subclass. The problem is: as soon as the event has been dispatched, the event handling switches to a new thread, so the event filter object will be choked up from two threads, which is never a good idea. Simple solution: make the event filter a child of the object to move, then it will move with it. This, on the other hand, gives you the problem of managing the storage lifetime so that you can get the result even if the moved object is immediately disposed of when a new stream is reached. In short: the store should be a reference to a variable in the old thread, not a member variable of a float or an event filter. Then all storage accesses come from the original thread and there are no races.

But, but ... isn't it safe yet? Yes, but only if the object is moved to another thread again. In this case, the event filter will access the storage location from the first moved stream, and it will race with read access from the original stream. Simple solution: disable the event filter after running it once. This implementation is left as an exercise for the reader :)

+5


source


QObject::moveToThread

only fails if it has a parent. If its a parent NULL

then you can move it, otherwise you cannot.

EDIT:



What you can do is that you can check the thread of the affinity object after being called moveToThread

by calling QObject :: thread and check if that actually changed its affinity.

QThread *pThread = new QThread;

QObject *pObject = new QObject;

{
    QMutexLocker locker(&mutex);

    pObject->moveToThread(pThread);

    if(pObject->thread() != pThread)
    {
        qDebug() << "moveToThread failed.";
    }
}

      

+2


source







All Articles