I have written a small multithreaded message queue. Now I am trying to do a test. But I am having a synchronization problem that I am not able to fix.
If I remove everything about the queue, the minimum verifiable example looks like this (sorry, it's a bit big):
#include <QCoreApplication>
#include <QDebug>
#include <QTimer>
#include <QtTest/QtTest>
#include <random>
#include <thread>
static std::vector< int > dummy;
class Writer : public QObject {
Q_OBJECT
public:
Writer( QObject *parent = nullptr ) : QObject( parent ) { }
Writer( unsigned, QObject *parent = nullptr ) : QObject( parent ) { }
public:
const std::vector< int > &beginPendings( ) { return dummy; }
void endPendings( ) { }
Q_SIGNALS:
void haveEvents( );
public Q_SLOTS:
void start( ) {
m_stop.test_and_set( );
connect( &m_inputTimer, &QTimer::timeout, this, &Writer::onTimer );
m_inputTimer.start( 100U );
connect( &m_outputTimer, &QTimer::timeout, this, &Writer::notifyEvents );
m_outputTimer.start( 250U );
}
void stop( ) {
m_inputTimer.stop( );
m_outputTimer.stop( );
}
private Q_SLOTS:
void onTimer( ) {
int limit = dist( mt );
for( int idx = 0; idx < limit; ++idx ) {
++m_idx;
}
}
void notifyEvents( ) {
emit haveEvents( );
}
private:
QTimer m_inputTimer;
QTimer m_outputTimer;
int m_idx = 0;
std::atomic_flag m_stop = ATOMIC_FLAG_INIT;
std::random_device rd;
std::mt19937 mt{ rd( ) };
std::uniform_int_distribution< int > dist{ 1, 20 };
};
class InOutQueueTest: public QObject {
Q_OBJECT
public Q_SLOTS:
void onPendingEvents( void ) {
writer->endPendings( );
}
void onTimeout( ) {
writer->stop( );
backendThread->exit( 0 );
backendThread->deleteLater( );
stop = true;
}
private Q_SLOTS:
void limit15( ) {
finishTimer.setSingleShot( true );
finishTimer.start( 5000 );
backendThread = new QThread( );
writer = new Writer( 15U );
connect( &finishTimer, &QTimer::timeout, this, &InOutQueueTest::onTimeout );
connect( writer, &Writer::haveEvents, this, &InOutQueueTest::onPendingEvents );
writer->moveToThread( backendThread );
backendThread->start( );
writer->start( );
while( !stop ) {
QCoreApplication::processEvents( );
}
}
private:
Writer *writer;
QThread *backendThread;
int last = 0;
QTimer finishTimer;
bool stop = false;
};
QTEST_GUILESS_MAIN( InOutQueueTest )
#include "inoutqueue.moc"
I hope the test lasts 5 seconds, and ends correctly. However, I get:
WARNING: InOutQueueTest::limit15() Generate 19 numbers, starting at 517
Loc: [/home/juanjo/Trabajos/qml/test/inoutqueue.cpp(53)]
WARNING: InOutQueueTest::limit15() Generate 19 numbers, starting at 536
Loc: [/home/juanjo/Trabajos/qml/test/inoutqueue.cpp(53)]
QFATAL : InOutQueueTest::limit15() QThread: Destroyed while thread is still running
FAIL! : InOutQueueTest::limit15() Received a fatal error.
Loc: [Unknown file(0)]
Totals: 1 passed, 1 failed, 0 skipped, 0 blacklisted, 5068ms
********* Finished testing of InOutQueueTest *********
Aborted
The code that should end the test (after 5 seconds) is this:
void onTimeout( ) {
writer->stop( );
backendThread->exit( 0 );
backendThread->deleteLater( );
stop = true;
}
I call the stop( ) method in Writer, call the exit( ) method in the auxiliary thread, and delete it in the next iteration of the event loop (it's the theory).
Too, in the Writer class, the stop( ) method is:
void stop( ) {
m_inputTimer.stop( );
m_outputTimer.stop( );
}
I simply do stop both timers.
What am I doing wrong ?
How do I solve it ?
I have written a small multithreaded message queue
Why? Qt inherently has one already... Post QEvents to QObjects - it's done in a thread-safe manner, with support for event priorities, automatic draining when receivers are deleted, etc. There's even some support for custom allocators for QEvent, so you could use a dedicated memory pool if you wanted to. Of course a dedicated queue may be faster, but that requires benchmarks and some requirement for which Qt's own queue's performance is insufficient. Multithreaded message queues have pesky corner cases as the receivers traverse threads, etc. - there's a reason why Qt's event queue code is less than straightforward. It's not far fetched to imagine that eventually you'd be reimplementing all of it, so there better be a good reason :)
First, let's note that the includes can be much simpler - and there's never a need for the QtModule/QtInclude format - that only helps when the application build is misconfigured. Not doing that lets you know of the problem earlier in the build - otherwise it'd fail at link time. Thus:
#include <QtCore>
#include <QtTest>
The slots that only emit a signal are unnecessary: signals are invokable, and you can connect anything invokable to signals directly - in Qt 4 syntax. In modern Qt syntax, slots aren't really necessary unless you need them for metadata (e.g. as used by the QtTest to detect test implementations). Otherwise, connect signals to any method whatsoever, even in non-QObjects, or to a functor (e.g. a lambda).
Now the serious problems:
The timers can only be manipulated from the thread they are in.
Move the timers to the same thread their user object is in. This is best done
by letting the parent object own the timers - the moving then happens automatically. Note that QObject ownership in no way clashes with retaining the timers by value - there'll be no double deletion, since by the time the parent QObject begins to delete the children, the timers are long gone.
Assert that the methods that manipulate the timers are called from the proper thread.
Optionally provide convenience to allow propagating the calls across the thread barriers.
You were re-connecting the timers to their slots each time the writer was started. That's never correct. Such connections should be made according to the scope of the objects involved, ensuring that they only happen once. In this case, since the timers live as long as the enclosing object, the connections belong in the constructor.
#include <random>
#include <vector>
class Writer : public QObject {
Q_OBJECT
static constexpr bool allowCrossThreadConvenience = false;
bool invokedAcrossThreads(auto method) {
bool crossThreadCall = QThread::currentThread() == thread();
if (crossThreadCall && allowCrossThreadConvenience) {
QMetaObject::invokeMethod(this, method);
return true;
}
Q_ASSERT(!crossThreadCall);
return false;
}
public:
Writer(QObject *parent = nullptr) : QObject(parent) {
connect(&m_inputTimer, &QTimer::timeout, this, &Writer::onTimer);
connect(&m_outputTimer, &QTimer::timeout, this, &Writer::haveEvents);
}
const auto &beginPendings() {
static std::vector<int> dummy;
return dummy;
}
void endPendings() { }
Q_SIGNAL void haveEvents();
Q_SLOT void start() {
if (invokedAcrossThreads(&Writer::start)) return;
m_outputTimer.start(250);
m_inputTimer.start(100);
}
Q_SLOT void stop() {
if (invokedAcrossThreads(&Writer::stop)) return;
m_inputTimer.stop();
m_outputTimer.stop();
}
private:
void onTimer() {
m_idx += dist(mt);
}
QTimer m_inputTimer{this};
QTimer m_outputTimer{this};
int m_idx = 0;
std::mt19937 mt{std::random_device()};
std::uniform_int_distribution<int> dist{1, 20};
};
Ssince this is C++, a thread class that's not destructible is a wee bit lame. Qt has kept QThread unchanged in that regard since it'd potentially break things (not really, but they'd rather error on the safe side given their enormous deployed base).
class Thread final : public QThread {
public:
using QThread::QThread;
~Thread() override {
requestInterruption();
quit();
wait();
}
};
The Thread can be safely destroyed at any time, and thus you can hold it by value. Also, foo(void) is a C-ism, unnecessary in C++ (it's just noise and unidiomatic since foo() is not foo(...) like it was in C). Now things become rather simple:
class InOutQueueTest: public QObject {
Q_OBJECT
Q_SLOT void limit15() {
Writer writer;
Thread thread; // must be last, after all the objects that live in it
connect(&writer, &Writer::haveEvents, &writer, &Writer::endPendings);
writer.start(); // now because we don't allow calling it from the wrong thread
writer.moveToThread(&thread);
QEventLoop eventLoop;
QTimer::singleShot(5000, &writer, [&]{
// This runs in the context of the writer, i.e. in its thread
writer.stop();
writer.moveToThread(eventloop.thread()); // needed to avoid a silly runtime warning
eventLoop.exit();
});
eventLoop.exec();
}
};
QTEST_GUILESS_MAIN( InOutQueueTest )
#include "inoutqueue.moc"
You need to always wait() on a thread before deleting it (in non-Qt speak "join").
Calling exit() on a QThread returns immediately and does not wait. And deleteLater() is performed in the caller's thread (not target thread), so you cannot count on it to be called "late enough".
Do exit(), then wait(), then deleteLater().
Related
I am prity new on Qt an got some issues with QSharedPointer passing around within signals. I am working with two threads (UI and a worker). The worker sends signals to the UI using signals that contain QSharedPointer of a custom QObject:
class MyObject : QObject {...}
class Window : public QWidget {
Q_OBJECT
public slots:
void onFound(QSharedPointer<MyObject>);
}
class Worker : public QObject {
Q_OBJECT
public signals:
void found(QSharedPointer<MyObject>);
}
I connect the workers found with the windows onFound with Qt::QueuedConnection because they live in different Threads and the communication therefore has to be asynchronous.
Now I observe the folowing behavior, when I pass a the last QSharedPointer refering to my object:
The signal moc casts the reference to my pointer to void* and arcives it.
The function returns resulting in the shared pointer and the corresponding object to be destroyed.
That's not what I expected - though it's reasonable. Are QSharedPointer in general designed to be passed through signals that way? And if so, is there a mecanism to keep a reference while it is queued?
I considered the folowing solutions, but I'm not totally fine with neither of them:
Keep a reference somewhere that keeps reference while it is queued. But where is a reasonable place to do that and when should I let it go.
Make the connection Qt::DirectConnection but then I am still have to switch the thread somehow (same situation as before)
Introduce a new signal/slot with std::function parameter for passing a lambda function to be executed in the target thread and capturing a copy of my shared pointer. (This is my current solution but it's not perty elegant, isn't it?)
Do you have any other suggestions or ideas?
The signal return does not destroy the corresponding object. The QMetaObject::activate call copies the shared pointer. Here's the implementation of send signal:
// SIGNAL 0
void IO::send(const QSharedPointer<Unique> & _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
You're probably experiencing a race: by the time the thread where the signal was emitted resumes execution, the destination thread has already received the object. Thus it appears in the emitting thread that the object is gone - because, by that time, it is. Yet the target object receives the instance just fine. It works fine.
The example below illustrates that it works in both single- and multi-threaded cases, and then reproduces your problem by ensuring that the race is always won by the destination thread:
// https://github.com/KubaO/stackoverflown/tree/master/questions/shared-pointer-queued-49133331
#include <QtCore>
class Unique : public QObject {
Q_OBJECT
int const m_id = []{
static QAtomicInteger<int> ctr;
return ctr.fetchAndAddOrdered(1);
}();
public:
int id() const { return m_id; }
};
class IO : public QObject {
Q_OBJECT
int m_lastId = -1;
public:
Q_SIGNAL void send(const QSharedPointer<Unique> &);
Q_SLOT void receive(const QSharedPointer<Unique> & u) {
m_lastId = u->id();
}
int lastId() const { return m_lastId; }
};
int main(int argc, char ** argv) {
Q_ASSERT(QT_VERSION >= QT_VERSION_CHECK(5,9,0));
QCoreApplication app{argc, argv};
IO src, dst;
QObject::connect(&src, &IO::send, &dst, &IO::receive, Qt::QueuedConnection);
QSharedPointer<Unique> u;
QWeakPointer<Unique> alive;
int id = -1;
// Single-threaded case
alive = (u.reset(new Unique), u);
id = u->id();
Q_ASSERT(dst.lastId() != id); // the destination hasn't seen the object yet
emit src.send(u);
u.reset();
Q_ASSERT(!u); // we gave up ownership of the object
Q_ASSERT(dst.lastId() != id); // the destination mustn't seen the object yet
Q_ASSERT(alive); // the object must be still alive
app.processEvents();
Q_ASSERT(dst.lastId() == id); // the destination must have seen the object now
Q_ASSERT(!alive); // the object should have been destroyed by now
// Multi-threaded setup
struct Thread : QThread { ~Thread() { quit(); wait(); } } worker;
worker.start();
dst.moveToThread(&worker);
QSemaphore s_src, s_dst;
// This thread wins the race
alive = (u.reset(new Unique), u);
id = u->id();
Q_ASSERT(dst.lastId() != id);
QTimer::singleShot(0, &dst, [&]{ s_src.release(); s_dst.acquire(); });
// stop the thread
s_src.acquire(); // wait for thread to be stopped
emit src.send(u);
QTimer::singleShot(0, &dst, [&]{ s_src.release(); });
// resume the main thread when done
u.reset();
Q_ASSERT(!u);
Q_ASSERT(alive); // we won the race: the object must be still alive
s_dst.release(); // get the thread running
s_src.acquire(); // wait for the thread to be done
Q_ASSERT(dst.lastId() == id);
Q_ASSERT(!alive);
// The other thread wins the race
alive = (u.reset(new Unique), u);
id = u->id();
Q_ASSERT(dst.lastId() != id);
emit src.send(u);
QTimer::singleShot(0, &dst, [&]{ s_src.release(); });
// resume the main thread when done
u.reset();
s_src.acquire(); // wait for worker thread to be done
Q_ASSERT(!u);
Q_ASSERT(!alive); // we lost the race: the object must be gone
Q_ASSERT(dst.lastId() == id); // yet the destination has received it!
// Ensure the rendezvous logic didn't mess up
Q_ASSERT(id == 2);
Q_ASSERT(!s_src.available());
Q_ASSERT(!s_dst.available());
}
#include "main.moc"
I'm trying to understand the operation of a QTimer. I have things triggered off timeout() signals, but I am unable to find in the documentation if a timeout() signal is emitted if I stop the timer early.
Basically, how can I force a timeout() before the timer finishes counting? Just hack by restarting the timer with the minimum ms increment?
myTimer->start(1);
http://qt-project.org/doc/qt-5/qtimer.html#timeout
In neither Qt 4 nor Qt 5 can you directly emit QTimer::timeout from outside of the class. It's a private signal: in Qt 4, it's declared as private, in Qt 5, it's declared with an argument of a private type QObjectPrivate.
You can invoke it, though:
// fast, guaranteed to work in Qt 4 and 5
myTimer->QTimer::qt_metacall(QMetaObject::InvokeMetaMethod, 5, {});
// slower, has to look up the method by name
QMetaObject::invokeMethod(myTimer, "timeout");
In Qt 5, the moc-generated QTimer::qt_static_metacall constructs the private argument for us:
//...
case 0: _t->timeout(QPrivateSignal()); break;
You can also make the timer act as if it had timed out by sending it a timer event:
void emitTimeout(QTimer * timer) {
Q_ASSERT(timer);
QTimerEvent event{timer->timerId()};
QCoreApplication::sendEvent(timer, &event);
}
Both methods work on both Qt 4 and Qt 5.
Since you're looking to emit a timeout on stopping an active timer, the solutions would be, respectively:
void emitTimeoutAndStop(QTimer * timer) {
Q_ASSERT(timer);
Q_ASSERT(QAbstractEventDispatcher::instance()); // event loop must be on the stack
if (!timer->isActive()) return;
timer->QTimer::qt_metacall(QMetaObject::InvokeMetaMethod, 5, {});
timer->stop();
}
or
void emitTimeoutAndStop(QTimer * timer) {
Q_ASSERT(timer);
Q_ASSERT(QAbstractEventDispatcher::instance()); // event loop must be on the stack
if (!timer->isActive()) return;
QTimerEvent event{timer->timerId()};
QCoreApplication::sendEvent(timer, &event);
timer->stop();
}
The signals will be emitted immediately, and not by Qt code from the event loop. This shouldn't be a problem, since emitTimeoutAndStop will be invoked with an event loop on the stack. We assert that fact. If you wish to support invoking emitTimeoutAndStop from code tied to the same timer's timeout signal without reentering said code, then you have to use the ChattyTimer below, or the solution from another answer.
If all you need isn't quite a timer, but just an immediate, single-emission signal, QObject::destroyed is useful for that purpose. It will be emitted at the end of the block, where source goes out of scope and is destructed.
{ QObject source;
connect(&source, &QObject::destroyed, ...); }
Alternatively, you can have a small helper class:
// signalsource.h
#pragma once
#include <QObject>
class SignalSource : public QObject {
Q_OBJECT
public:
Q_SIGNAL void signal();
SignalSource(QObject * parent = {}) : QObject(parent) {}
};
Since you can connect multiple signals to a single receiver, perhaps it would make things clearer to connect both a timer and such a signal source to the receiver, instead of trying to hack around timer's behavior.
On the other hand, if such "signaling upon stopping" timer is something you find useful in several places, it'd be better to actually implement it as a dedicated class - it's not all that hard. Re-using the QTimer class is not possible, since the stop() slot is not virtual, and thus ChattyTimer is not Liskov-substitutable for a QTimer. This would be a bug waiting to happen - in the class of bugs that are hard to find.
There are several behavioral details that demand attention. This perhaps indicates that changing the behavior of something as fundamental as a timer is tricky - you never know what code might make assumptions that are obviously correct in QTimer, but not so when stop() might emit a timeout. It is a good idea to have all this in a class that is not-a QTimer - it really is not!
As in QTimer, the timeout event is always emitted from the event loop. To have it emitted immediately from stop(), set immediateStopTimeout.
There are two generalizations possible of isActive behavior upon stop (vs. that of QTimer):
becomes false immediately after stop returns, even if the final timeout will be emitted later, or
indicates whether timeout events may be emitted by the event loop, and will remain true after a stop() if the final timeout signal is deferred.
I chose the first behavior to be the default. Set activeUntilLastTimeout to select the second behavior.
There are three generalizations possible of the behavior of each of timerId and remainingTime upon stop (vs. that of QTimer):
returns -1 when isActive() is false, or a valid identifier/time otherwise (i.e. follows chosen isActive() behavior),
becomes -1 immediately after stop returns, even if the final timeout will be emitted later,
returns a valid id/time whenever timeout events may still be emitted by the event loop.
I chose the first behavior for both timerId and remainingTime, and it is not otherwise configurable.
// https://github.com/KubaO/stackoverflown/tree/master/questions/chattytimer-25695203
// chattytimer.h
#pragma once
#include <QAbstractEventDispatcher>
#include <QBasicTimer>
#include <QTimerEvent>
class ChattyTimer : public QObject {
Q_OBJECT
Q_PROPERTY(bool active READ isActive)
Q_PROPERTY(int remainingTime READ remainingTime)
Q_PROPERTY(int interval READ interval WRITE setInterval)
Q_PROPERTY(bool singleShot READ singleShot WRITE setSingleShot)
Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType)
Q_PROPERTY(bool immediateStopTimeout READ immediateStopTimeout WRITE setImmediateStopTimeout)
Q_PROPERTY(bool activeUntilLastTimeout READ activeUntilLastTimeout WRITE setActiveUntilLastTimeout)
Qt::TimerType m_type = Qt::CoarseTimer;
bool m_singleShot = false;
bool m_stopTimeout = false;
bool m_immediateStopTimeout = false;
bool m_activeUntilLastTimeout = false;
QBasicTimer m_timer;
int m_interval = 0;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() != m_timer.timerId()) return;
if (m_singleShot || m_stopTimeout) m_timer.stop();
m_stopTimeout = false;
emit timeout({});
}
public:
ChattyTimer(QObject * parent = {}) : QObject(parent) {}
Q_SLOT void start(int msec) {
m_interval = msec;
start();
}
Q_SLOT void start() {
m_stopTimeout = false;
m_timer.stop(); // don't emit the signal here
m_timer.start(m_interval, m_type, this);
}
Q_SLOT void stop() {
if (!isActive()) return;
m_timer.stop();
m_stopTimeout = !m_immediateStopTimeout;
if (m_immediateStopTimeout)
emit timeout({});
else // defer to the event loop
m_timer.start(0, this);
}
Q_SIGNAL void timeout(QPrivateSignal);
int timerId() const {
return isActive() ? m_timer.timerId() : -1;
}
bool isActive() const {
return m_timer.isActive() && (m_activeUntilLastTimeout || !m_stopTimeout);
}
int remainingTime() const {
return
isActive()
? QAbstractEventDispatcher::instance()->remainingTime(m_timer.timerId())
: -1;
}
int interval() const { return m_interval; }
void setInterval(int msec) {
m_interval = msec;
if (!isActive()) return;
m_timer.stop(); // don't emit the signal here
start();
}
bool singleShot() const { return m_singleShot; }
void setSingleShot(bool s) { m_singleShot = s; }
Qt::TimerType timerType() const { return m_type; }
void setTimerType(Qt::TimerType t) { m_type = t; }
bool immediateStopTimeout() const { return m_immediateStopTimeout; }
void setImmediateStopTimeout(bool s) { m_immediateStopTimeout = s; }
bool activeUntilLastTimeout() const { return m_activeUntilLastTimeout; }
void setActiveUntilLastTimeout(bool s) { m_activeUntilLastTimeout = s; }
};
If you stop a QTimer, will it emit a timeout() signal?
No.
Basically, how can I force a timeout() before the timer finishes
counting? Just hack by restarting the timer with the minimum ms
increment?
Call stop() on the timer, and then cause the signal to be emitted yourself. You could do this by subclassing QTimer and calling a method in your QTimer subclass that emits the signal:
void MyQTimer :: EmitTimeoutSignal() {emit timeout();}
… but if you don't want to go to the bother of making a subclass, an easier approach would be to add a signal to your own class and connect that signal to the QTimer object's timeout() signal (do this only once of course):
connect(this, SIGNAL(MyTimeoutSignal()), myTimer, SIGNAL(timeout()));
… and then your stop-and-fire method would could be done like this:
myTimer->stop();
emit MyTimeoutSignal();
It's actually quite easy to do this, at least in 4.8 and later (not sure about earlier versions): simply setInterval(0) (much like you suggest in your question, although there's no need to stop the timer and restart it).
This app will immediately print "Timer expired" and exit:
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
QTimer timer;
timer.setSingleShot(true);
timer.setInterval(1000 * 60 * 60); // one hour
QObject::connect(
&timer, &QTimer::timeout,
[&]()
{
std::cout << "Timer expired" << std::endl;
app.exit();
});
QTimer::singleShot(
0, //trigger immediately once QtEventLoop is running
[&]()
{
timer.start();
timer.setInterval(0); // Comment this out to run for an hour.
});
app.exec();
}
I want to stop a looping thread when a signal was emitted so here is my code
void MyThread::stopWatchingThread()
{
qDebug()<<"MyThread::stopWatchingThread()";
Keep_running=false;
qDebug()<<"MyThread::stopWatchingThread Keep_running"<<Keep_running;
...
}
void MyThread::run()
{
qDebug()<<"MyThread::run()";
qDebug()<<"MyThread::run Keep_running"<<Keep_running;
while(Keep_running)
{
...
}
qDebug()<<"MyThread::run Keep_running"<<Keep_running;
Keep_running=false;
qDebug()<<"MyThread::run Keep_running"<<Keep_running;
}
void Watcher::Init()
{
WatchingThread=new MyThread(this->L_RootToWatch);
connect(this,SIGNAL(stopmonotiring()),WatchingThread, SLOT(stopWatchingThread()));
...
}
void Watcher::StartWatching()
{
WatchingThread->start();
}
void Watcher::StopWatching()
{
emit stopmonotiring();
}
So every thing goes all right but my problem is that Keep_running never get false value in MyThread::run() after emitting stopWatchingThread and so while loop for ever.
What did I miss ?
any help will be appreciated.
Don't create threaded classes explicitly in Qt. Instead, create a worker object, move that object to a QThread, then call start() on the QThread. Here's a quick example:
class Worker : public QObject
{
Q_OBJECT
public:
Worker( QObject * parent = 0 )
: QObject( parent )
{}
public slots:
void doWork( ... )
{
// do work here
}
void stopMonitoring()
{
emit finished();
}
signals:
void finished();
};
int main()
{
Worker * w = new Worker();
QThread * thread = new QThread();
QObject::connect( w, SIGNAL(finished()), thread, SLOT(quit())
QObject::connect( w, SIGNAL(finished()), w, SLOT(deleteLater())
QObject::connect( thread, SIGNAL(finished()), thread, SLOT(deleteLater())
w->moveToThread( thread );
thread->start();
// some other object emits a signal connected to the 'doWork()' slot.
}
I omitted some of the standard QApplication boiler-plate, but you have that already if you're using Qt. This should get you started.
As your run() method is blocking and the event loop never entered, the slot stopWatchingThread will never be called. You must call exec() and not block the event loop by a spinning loop in run(). Either that, or have the watcher thread call stopWatchingThread directly instead of using a signal/slot connection. I'd go for the latter. keepRunning will be accessed from multiple threads then, so you have to protect it using a QMutex, QReadWriteLock or QAtomic. (Start with QMutex, it's easiest).
If you use an event loop in your thread just post the quit() signal to the thread object.
Maybe your C++ compiler optimizes away the read operation on Keep_running. Try declaring it as volatile, which tells the compiler that this variable might change "unexpectedly", e.g. from other threads or hardware interrupts.
My Qt application has a Qt gui (basically some buttons and an opengl context which draws data). I've also added scriptability exploiting PythonQt classes. The commands are evaluated from inside a PythonQtScriptingConsole.
I've explicitly created wrapper classes and factory methods to send C++ calls via the current python context through the console, but when running long tasks from inside the console, the gui freezes because (I think) the event loop is not processed. So a first solution would be to process the event loop with a timer, but this is both slow and kinda stupid I think, so I don't like it. A
Has someone some hint? Is the Python Global Interpreter Lock a problem here?
Yes, the GUI is freezing because the long call into Python is being executed via the UI thread. To get around this, I was able to subclass QThread and issue commands into the Python module via a Command pattern.
Before you start making calls into multiple Python modules using the following classes, be sure to initialize thread support in Python by calling PyEval_InitThreads() as you'll see in my main() function.
Good luck!
int main( int argc, char **argv ) {
QApplication qapp(argc, argv);
PyEval_InitThreads(); // IMPORTANT
PythonQt::init(PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut);
PythonQtObjectPtr module = PythonQt::self()->createUniqueModule();
ThreadedPythonContext context(module);
context.start();
# issue some commands into the module
context.issue("import sys");
context.issue("sys.path.append('C:\\Python27\\Lib\\site-packages')");
context.issue("import time");
context.issue("last = time.localtime().tm_sec");
// Release the global interpreter lock (if it has been created and thread support
// is enabled) and reset the thread state to NULL, returning the previous thread
// state (which is not NULL). If the lock has been created, the current thread must
// have acquired it. (This function is available even when thread support is
// disabled at compile time.)
// give up control of the GIL
PyThreadState *state = PyEval_SaveThread();
return qapp.exec()
}
ThreadedPythonContext.h
#ifndef THREADEDPYTHONCONTEXT_H
#define THREADEDPYTHONCONTEXT_H
#include "PythonQt.h"
#include <QtCore/QMutexLocker>
#include <QtCore/QQueue>
#include <QtCore/QThread>
#include <QtCore/QWaitCondition>
class ThreadedPythonContext : public QThread
{
Q_OBJECT
public:
ThreadedPythonContext(const PythonQtObjectPtr &context) :
QThread(),
_context(context),
_running(true)
{
}
~ThreadedPythonContext() {
_running = false;
wait();
}
void issue(const QString &code) {
_lock.lock();
_commands.enqueue(code);
_lock.unlock();
_CommandQueued.wakeOne();
}
bool isCommandQueueEmpty() {
QMutexLocker lock(&_lock);
return _commands.isEmpty();
}
protected:
QString dequeue() {
QMutexLocker lock(&_lock);
QString cmd( _commands.dequeue() );
return cmd.isEmpty() ? "\n" : cmd;
}
void run() {
QMutex signal;
PyGILState_STATE state;
while(_running) {
// wait to be signaled ...
signal.lock();
_CommandQueued.wait(&signal,1);
signal.unlock();
if ( isCommandQueueEmpty() ) {
continue;
}
while ( !isCommandQueueEmpty() ) {
PythonQtObjectPtr p;
PyObject* dict = NULL;
state = PyGILState_Ensure();
if (PyModule_Check(_context)) {
dict = PyModule_GetDict(_context);
} else if (PyDict_Check(_context)) {
dict = _context;
}
if (dict) {
// this command blocks until the code has completed execution
emit python_busy(true);
p.setNewRef(PyRun_String(dequeue().toLatin1().data(), Py_single_input, dict, dict));
emit python_busy(false);
}
// error in the kernel
if (!p) {
PythonQt::self()->handleError();
}
PyGILState_Release(state);
}
}
}
PythonQtObjectPtr _context;
QMutex _lock;
QQueue<QString> _commands;
QWaitCondition _CommandQueued;
bool _running;
signals:
void python_busy(bool);
};
#endif //THREADEDPYTHONCONTEXT_H
Since I need a more precise timer than the Qt one with its ~15ms resolution i tried to implement my own timer using QueryPerformanceCounter() from the windows api.
My first shot was to inherit from QObject and build a timer with an infinite loop that fires a signal every second. I tried to move this timer to its own thread with moveToThread() so that it would just update my main window every second and not block the whole thing. However the main window would never show up, removing the infinite loop -> main window shows up.
So I tried a singleshot approach, the basic idea is:
connect( highPerformanceTimer, SIGNAL(timer_tick()), this, SLOT(timeOutSlot()) );
connect( this, SIGNAL(graphics_updated()), highPerformanceTimer, SLOT(timer_start()));
So, timer ticks, ui (opengl) gets updated, at the end of the update a signal is generated to restart the timer.
Still my main window will not show up as long as i don't remove the restart of the timer from the end of the ui update code.
In the debugger I can see that the code seems to work like it should, it jumps from timer to ui and back. Without breakpoints it will result in a sudden segmentation fault.
I would be thankful for any hint where the problem could be or if there is a better way to implement this in Qt.
Edit: Some more code
highperformancetimer.h
#ifndef HIGHPERFORMANCETIMER_H
#define HIGHPERFORMANCETIMER_H
#include <QObject>
#include <windows.h>
class HighPerformanceTimer : public QObject
{
Q_OBJECT
public:
HighPerformanceTimer();
signals:
void timer_tick();
public slots:
void timer_start();
private:
// some variables
LARGE_INTEGER highPerformanceCounterValue, highPerformanceCounterFrequency, highPerformanceCounterValueOld;
int interval;
float value;
float last_tick_value;
};
#endif // HIGHPERFORMANCETIMER_H
highperformancetimer.cpp
#include "highperformancetimer.h"
#include "windows.h"
#include "QThread"
HighPerformanceTimer::HighPerformanceTimer()
{
QueryPerformanceFrequency(&highPerformanceCounterFrequency);
}
void HighPerformanceTimer::timer_start(){
float i = 0;
// just burn some time
while( i<1000000 ) i++;
emit HighPerformanceTimer::timer_tick();
}
Some Code from the main OpenGl Widget:
HighPerformanceTimer *highPerformanceTimer;
protected slots:
virtual void timeOutSlot();
NeHeChapter5( QWidget *parent=0, char *name=0 ) : NeHeWidget( 50, parent, name )
{
highPerformanceTimer = new HighPerformanceTimer();
connect( highPerformanceTimer, SIGNAL(timer_tick()), this, SLOT(timeOutSlot()) );
connect( this, SIGNAL(graphics_updated()), highPerformanceTimer, SLOT(timer_start()));
highPerformanceTimer->moveToThread(&timerThread);
highPerformanceTimer->timer_start();
}
void NeHeChapter5::timeOutSlot(){
timeOut();
}
void NeHeChapter5::timeOut()
{
updateGL();
}
void NeHeChapter5::paintGL()
{
//opengl code *snip*
emit graphics_updated();
}
Error is here:
NeHeChapter5( QWidget *parent=0, char *name=0 ) : NeHeWidget( 50, parent, name )
{
highPerformanceTimer = new HighPerformanceTimer();
connect( highPerformanceTimer, SIGNAL(timer_tick()), this, SLOT(timeOutSlot()) );
connect( this, SIGNAL(graphics_updated()), highPerformanceTimer, SLOT(timer_start()));
highPerformanceTimer->moveToThread(&timerThread);
highPerformanceTimer->timer_start();
}
You call timer_start(); and it's being called in caller thread, not in timerThread. Also you didn't even start your thread. Call timerThread.start() first. To invoke your timer_start() in thread you want you should call
QMetaObject::invokeMethod(highPerformanceTimer, "timer_start", Qt::QueuedConnection);
It will invoke timer_start in newly started thread, not in caller thread.
Also you don't need to call timer_tick with fully qualified name emit HighPerformanceTimer::timer_tick(); can be replaced with emit timer_tick();