Search code examples
c++multithreadingqtthread-safetyqthread

Is QThread::quit usabled from within the running thread


So I have the following situation:

I have a QThread that runs an eventloop (i.e. no custom run function). To stop the thread, I send a signal to a worker in that thread. This worker then does cleanups etc and at some point is done and quits the thread.

The problem I am facing right now is: If I invoke the workers stop method and then immediatly wait for the thread to finish it will never do so because the workers done signal does not get processed. Code:

class Worker {
signals:
    void done();

public slots:
    void stop() {
        //dummy code to illustrate what happens here:
        QTimer::singleShot(3000, this, &Worker::done);
    }
};

// in the main thread
auto thread = new QThread();
auto worker = new Worker();
worker->moveToThread(thread);
connect(worker, &Worker::done, thread, &QThread::quit); //implicitly a queued connection

// ...

QMetaObject::invokeMethod(worker, "stop", Qt::QueuedConnection);
thread->wait(); //blocks here forever, because the connect is queued

Now reason the problem is obvious - Because I block on the main thread the slot can never be invoked (because queued connection) and thus quit is never called. However, if I simply call QThread::quit (or QThread::exit) directly from the worker (or use a DirectConnection) then there is no problem anymore because the eventloop of the main thread is no longer required to process the event.

So the actual question here is: Is that allowed? Can I call QThread::quit from within the actual thread? Or can this create Race conditions, deadlocks and other problems like that. The documentation does not mark the method as threadsafe - but the thread that is managed by QThread might be an exception.


Solution

  • Can I call QThread::quit from within the actual thread?

    The question is actually backwards!

    Since this method controls the event loop, and the event loop most definitely runs on the thread, the default assumption is that it's not a thread-safe method and thus can only be called from within the thread, since it controls a QEventLoop instance instantiated via QThread::run. That event loop, and its event dispatcher, are QObjects and most definitely have their thread() equal to the QThread instance in question.

    But that wouldn't make QThread very useful, and thus QAbstractEventDispatcher::exit, and thus QEventLoop::quit and QThread::quit, are indeed thread-safe methods - you can call them from wherever, including from threads other than the one where the event loop lives. Both the event loop and thread's methods take extra precautions to protect their state from races, so the "and thus" part a few sentences ago is hand-wavey a bit.