In the following code, I get an ASSERT in unlock (), because lockingThreads (a QList<QThread*>) does not contain the unlocking (current) thread.
Examining lockingThreads, I find an "ntdll" thread that I have no idea what it does and only disassembly can be accessed which doesn't provide useful information.
The problem is very hard to reproduce, so thanks for any hint (probably a stupid twist in this logic).
Edit: the mutex is recursive.
MyMutex::MyMutex (QMutex::RecursionMode mode)
: QMutex (mode)
, lockCount (0)
{
}
MyMutex::~MyMutex ()
{
ASSERT (lockingThreads.isEmpty ());
}
void MyMutex::lock ()
{
lockCount++;
#if MYDEBUG
// grab thread before lock so we can identify it if it deadlocks here
QThread* thread = QThread::currentThread ();
if (!isRecursive ())
{
ASSERT (!lockingThreads.contains (thread));
if (!lockingThreads.contains (thread))
lockingThreads += thread;
}
else
lockingThreads += thread;
#endif
QMutex::lock ();
}
void MyMutex::unlock ()
{
#if MYDEBUG
QThread* thread = QThread::currentThread ();
ASSERT (lockingThreads.contains (thread));
lockingThreads.removeOne (thread);
if (lockingThreads.contains (thread) && !isRecursive ())
ASSERTFALSE;
#endif
QMutex::unlock ();
lockCount--;
}
Related
I have made new QTimer object in my class's ctor. When the application starts the timer also starts with the specified time. This timer keeps a track of all the process IDs and if finds that a particular process-id is not active(killed from task manager) then it restarts that particular process. Now the issue is my application lags a-bit due to this QTimer object and only after the timeout, the application becomes smooth. But after 5 seconds the timer starts again and the application starts lagging again. What is the best alternative to start a QTimer object apart from ctor of a class?
qTimer = new QTimer(0);
connect(qTimer, SIGNAL(timeout()), this, SLOT(RestartStoppedProcess()), Qt::DirectConnection);
connect(qTimer, SIGNAL(destroyed()), qTimer, SLOT(deleteLater()), Qt::DirectConnection);
qTimer->setTimerType(Qt::VeryCoarseTimer);
qTimer->start(5000);
void ProcessMonitor::RestartStoppedProcess()
{
try
{
QProcess *objMonitorProcess = new QProcess(this);
connect(objMonitorProcess, SIGNAL(finished(int,QProcess::ExitStatus)), objMonitorProcess, SLOT(deleteLater()));
for(int i = 0; i < ui->twShowProcess->rowCount(); i++)
{
#ifdef Q_WS_LIN
// objMonitorProcess->start("pidof", QStringList() << (ui->twShowProcess->item(i, 0)->text()), QIODevice::ReadOnly);
objMonitorProcess->start("pidof "+ (ui->twShowProcess->item(i, 0)->text()), QIODevice::ReadOnly);
#elif Q_WS_WIN
QString cmd("wmic process where ");
QString qszFilters("\"processid=\'");
QString qszSubFilters = qszFilters.append(ui->twShowProcess->item(i, 1)->text().append("\'").append("\"").append(" get name"));
cmd.append(qszSubFilters);
objMonitorProcess->start(cmd.toStdString().c_str(), QIODevice::ReadOnly);
#else
QString qszProcessName = ui->twShowProcess->item(i, 0)->text().split(".").takeFirst();
objMonitorProcess->start("pgrep", QStringList() << qszProcessName, QIODevice::ReadOnly);
#endif
if((objMonitorProcess->waitForStarted(4000) == true) && (objMonitorProcess->state() == QProcess::Running))
{
if(objMonitorProcess->waitForFinished(3000) == false)
continue;
else
{
QString qszProcessStatus = ui->twShowProcess->item(i, 3)->text();
if(qszProcessStatus.compare("Running") == 0)
{
QString qszStandardOutput, qszStandardError;
qszStandardOutput.clear(); qszStandardError.clear();
qszStandardOutput = QString(objMonitorProcess->readAllStandardOutput());
qszStandardError = QString(objMonitorProcess->readAllStandardError());
if(qszStandardError.simplified().compare("No Instance(s) Available.", Qt::CaseInsensitive) == 0 && qszStandardOutput.simplified().isEmpty() == true)
{
ui->twShowProcess->selectRow(i);
if(ui->twShowProcess->selectedItems().at(0)->text().compare("LTtagdb.exe", Qt::CaseInsensitive) == 0 ||
ui->twShowProcess->selectedItems().at(0)->text().compare("LTmessagebroker.exe", Qt::CaseInsensitive) == 0)
this->StopAllApplicationsForcefully();
else
this->on_actionStop_triggered();
if(this->ptrobjContextMenu->isVisible())
this->ptrobjContextMenu->close();
ui->twShowProcess->clearSelection();
if(mobj_ProcessLogMap.mobj_ProcessAndLogMapping.contains(ui->twShowProcess->item(i, 8)->text()) == true)
{
mobj_ProcessLogMap.mobj_ProcessAndLogMapping.remove(ui->twShowProcess->item(i, 8)->text());
mobj_ProcessLogMap.mobj_ProcessAndLogMapping.insert(ui->twShowProcess->item(i, 8)->text(), QString(""));
}
else
this->mobj_ProcessLogMap.mobj_ProcessAndLogMapping.insert(ui->twShowProcess->item(i, 8)->text(), QString(""));
}
if(ui->twShowProcess->item(i, 0)->text() == "LThistdb.exe")
{
if(IsServiceRunning(QString("LThistdb.exe")) == false)
this->on_actionStopPostgreSQL_DB_Server_triggered();
}
qszStandardOutput.clear();
}
}
}
else
break;
#ifdef Q_WS_WIN
cmd.clear();
qszFilters.clear();
qszSubFilters.clear();
#endif
}
}
catch(QProcess::ProcessError pError)
{
QMessageBox objCrashExit;
objCrashExit.information(this, "Crashed", "Process error returned code: " + QString::number(pError));
}
catch(...)
{
QMessageBox objCrashExit;
objCrashExit.information(this, "Unknown Crash", "Unknown crash exit-child process may not be running correctly!");
}
}
QTimer runs in the UI thread so it blocks your UI if it takes a long time to execute. A simple solution is to just run your "processing" stuff in another background thread, the easiest way to do that in Qt might be using QtConcurrent::run like this from your timeout slot called from the QTimer:
QtConcurrent::run(yourProcessingFunction);
You can obviously create your own Thread and use the build in QTimer from QObject::startTimer(...), but that is more work usually.
The problem is likely due to the amount of work being done by RestartStoppedProcess.
As the documentation for QTimer states, when discussing the called slot:
It should be written in such a way that it always returns quickly (typically after processing one data item) so that Qt can deliver events to the user interface and stop the timer as soon as it has done all its work.
As you've witnessed with your application, a heavy workload with a QTimer on the main thread can cause lag in the Gui. The Qt documentation goes on to state:
This is the traditional way of implementing heavy work in GUI applications, but as multithreading is nowadays becoming available on more and more platforms, we expect that zero-millisecond QTimer objects will gradually be replaced by QThreads.
With this in mind, I suggest refactoring your code so that RestartStoppedProcess functions in a separate thread. There's a great tutorial on "How to Really Truly Use QThread" here.
Is it necessary to clear all the inner lists to avoid a leak?:
class Instruction
{
int opcode;
int data1;
int data2;
bool Load(QTextStream* in);
void Save(QTextStream* out) const;
};
class Interpreter
{
QList<QList<Instruction>> steps;
bool Load(QTextStream* file)
{
if(file_is_bad)
{
return false;
}
int end = steps.size();
for(int i=0; i<end; i++)
{
steps.at(i).clear();
}
steps.clear();
//now that it's clear, rebuild it from file
return true;
}
};
Or can I just call steps.clear(); and call it a day?
(And here's some more text to get past the "too much code" error.)
Igor was right. steps.clear() is enough because it destroys all inner QLists. Destroying an inner QList calls the destructor of all Instruction instants.
So as long as a single Instruction does not leak memory (e.g. by calling a new in the constructor but no delete in the destructor), QList<QList<Instruction>> will not leak as well.
I have several fields in a widget, that each can affect the behavior of an item, and changing some of them will change others.
I read somewhere that the editingFinished() signal of a line edit is triggered only by user actions - and not by code changes... Is that true ?
connect(m_lineEdit1, SIGNAL(editingFinished()), this, SLOT(m_lineEdit1Changed()));
connect(m_lineEdit2, SIGNAL(editingFinished()), this, SLOT(m_lineEdit2Changed()));
connect(this, SIGNAL(someSignal()), this, SLOT(updateData()));
void m_lineEdit1Changed()
{
changedata1();
emit someSignal();
}
void m_lineEdit2Changed()
{
changedata2();
emit someSignal();
}
void updateData()
{
m_lineEdit1.setText(fromdata);
m_lineEdit2.setText(fromdata);
}
If I change m_lineEdit1, and update the entire widget (which changes, through code, m_lineEdit2), I hit a breakpoint in m_lineEdit2Changed()
This leads to an infinite loop of updates...
What can I do to get around it ?
Blocking signals is a bit of a sledgehammer of an approach. You can use a sentinel class to explicitly prevent recursion:
#define SENTINEL_STRINGIFY(x) #x
#define SENTINEL_TOSTRING(x) SENTINEL_STRINGIFY(x)
#define SENTINEL_AT __FILE__ ":" SENTINEL_TOSTRING(__LINE__)
class Sentinel {
Q_DISABLE_COPY(Sentinel);
static QMutex m_mutex;
static QSet<QString> m_sentinels;
QString const m_sentinel;
bool const m_ok;
static bool checkAndSet(const QString & sentinel) {
QMutexLocker lock(&m_mutex);
if (m_sentinels.contains(sentinel)) return false;
m_sentinels.insert(sentinel);
return true;
}
public:
explicit Sentinel(const char * sentinel) :
m_sentinel(sentinel), m_ok(checkAndSet(m_sentinel)) {}
~Sentinel() {
if (!m_ok) return;
QMutexLocker lock(&m_mutex);
m_sentinels.remove(m_sentinel);
}
bool operator()() const { return m_ok; }
};
QMutex Sentinel::m_mutex;
QSet<QString> Sentinel::m_sentinels;
...
void Foo::m_lineEdit1Changed()
{
Sentinel s(SENTINEL_AT);
if (!s) return; // exit if this method is on the call stack
...
changedata1();
emit someSignal();
}
This is thread-safe and can be used from any thread.
A technique to avoid this problem is to use the QObject::blockSignals() function.
In your example you would do:
void updateData()
{
m_lineEdit1.blockSignals(true);
m_lineEdit1.setText(fromdata);
m_lineEdit1.setText(fromdata);
m_lineEdit1.blockSignals(false);
}
The blockSignals() call prevents the object sending any signals while you are changing the data in the line edit.
I'm trying to use a thread to bring data from a MySQL database, but after a call to:
QThread* thread = new QThread;
Beacon *beacon = new Beacon(m_db, begin, end);
beacon->moveToThread(thread);
connect(beacon, &Beacon::values, this, &Tm::insertRow);
connect(beacon, &Beacon::finished, thread, &QThread::quit);
connect(beacon, &Beacon::finished, beacon, &Beacon::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
connect(thread, &QThread::started, beacon, &Beacon::executeQuerySmooth);
connect(beacon, &Beacon::finished, this, &Tm::finished);
thread->start();
I get:
QMYSQLResult::cleanup: unable to free statement handle
And the next time a try to use the connection I get:
QSqlError("2006", "QMYSQL: Unable to execute query", "MySQL server has gone away")
What might be happening?
You can only use the connection from a thread that created it (reference), or in general from one thread only.
There are two approaches you can take:
Maintain the database connection in a dedicated thread, and use it from there. The use of shortlived threads is a premature pessimization anyway, so it was a bad approach to begin with.
Move the database connection between worker threads from a thread pool. Use QtConcurrent::run to run a functor on a thread from the thread pool.
Note: Beacon::Beacon(...) constructor can't be using the database, it can only store a reference/pointer to it!
See this answer for more about postCall.
template <typename T>
void postCall(QThread * thread, T && functor) {
QObject source;
QObject::connect(&source, &QObject::destroyed,
QEventDispatcher::instance(thread), std::forward(functor));
}
Workers From The Thread Pool
class Tm : public QObject {
Q_OBJECT
QMutex m_dbMutex;
QSqlDatabase m_db;
Q_SIGNAL void openFailed();
Q_SIGNAL void openSucceeded();
public:
void openConnection() {
QMutexLocker lock(&m_dbMutex);
m_db.addDatabase("QSQLITE");
... // set database's properties
m_db.moveToThread(0); // we don't know what thread it will be used in
lock.unlock();
QtConcurrent::run([this]{
QMutexLocker lock(&m_dbMutex);
m_db.moveToThread(QThread::currentThread());
bool rc = m_db.open();
if (rc) {
m_db.moveToThread(0);
emit openSucceeded();
} else {
m_db.moveToThread(this->thread());
emit openFailed();
}
});
}
void beaconate() {
...
QSharedPointer<Beacon> beacon(new Beacon(&m_db, begin, end));
connect(beacon, &Beacon::values, this, &Tm::insertRow);
connect(beacon, &Beacon::finished, this, &Tm::finished);
beacon->setMoveToThread(0);
QtConcurrent::run([beacon]{
beacon->moveToThread(QThread::currentThread());
QMutexLocker lock(&m_dbMutex);
m_db.moveToThread(QThread::currentThread());
QEventLoop loop; // only if Beacon needs one
connect(beacon, &Beacon::finished, &loop, &QEventLoop::quit);
beacon->executeQuerySmooth();
loop.exec();
m_db.moveToThread(0);
});
}
~Tm() {
QMutexLocker lock(&m_dbMutex);
m_db.moveToThread(thread());
}
...
};
Dedicated Thread
class Thread : public QThread
{ using QThread::run; public: ~Thread() { quit(); wait(); } };
class Tm : public QObject {
Q_OBJECT
QSqlDatabase m_db;
Thread m_dbThread; // must be declared after m_db, so that it's destructed prior to m_db
Q_SIGNAL void openFailed();
Q_SIGNAL void openSucceeded();
public:
Tm(QObject * parent = 0) : QObject(parent) {
m_dbThread.start();
}
void openConnection() {
m_db.addDatabase("QSQLITE");
... // set database's properties
m_db.moveToThread(&m_dbThread);
postCall(&m_dbThread, [this]{
if (! m_db.open()) {
m_db.moveToThread(this->thread());
emit openFailed();
} else
emit openSucceeded();
});
}
void beaconate() {
...
auto beacon = new Beacon(&m_db, begin, end);
beacon.moveToThread(&m_dbThread);
connect(beacon, &Beacon::values, this, &Tm::insertRow);
connect(beacon, &Beacon::finished, beacon, &Beacon::deleteLater);
connect(beacon, &Beacon::finished, this, &Tm::finished);
postCall(&m_dbThread, [beacon]{ beacon->executeQuerySmooth(); });
}
~Tm() {
// Destructing objects living in other threads is undefined behavior
postCall(&m_dbThread, [this]{
if (m_db.thread() == QThread::currentThread())
m_db.moveToThread(thread());
});
}
...
};
I have an object that is derived from QThread and the class definition includes the Q_OBJECT macro. I created a timer within the thread so I can do some occasional checks while the thread is running; however, the timeout event is never occurring.
I've tried making the timer a singleshot as well, but no events are emitted.
Are events processed in a thread by default or do I need to do something else to have them processed?
Here's the code for how I set up the thread and timers:
void MyClass::run( void )
{
checkTimer_chA = new QTimer( this );
qDebug() << connect( checkTimer_chA, SIGNAL( timeout() ), this, SLOT( timerExpiry_chA() ) );
checkTimer_chA->start( 1000 );
// prevent multiple, simultaneous starts
if( !isRunning )
{
qDebug() << "Thread: MyClass::run";
isRunning = true;
while( isRunning )
{
getData();
processData();
yieldCurrentThread();
}
}
checkTimer_chA->stop();
delete checkTimer_chA;
}
void DAQ::timerExpiry_chA( void )
{
qDebug() << "timerExpiry_chA";
checkTimer_chA->stop();
}
If I add QApplication::processEvents(); right before the call to yieldCurrentThread(); the timer works as expected. However, this seems wrong to me.
Working with threads in Qt can sometimes be a bit of a hassle. This blog post was a real eye opener for me. In case we adopt the style proposed in the blog post to your problem we end up with a solution like below.
Consumer::Consumer():
checkTimer_(new QTimer(this))
{
QObject::connect(checkTimer_, SIGNAL(timeout()), this, SLOT(onTimerExpiration());
QObject::connect(this, SIGNAL(ready()), this, SLOT(consume());
}
bool Consumer::event(QEvent *e)
{
if (e->type() == QEvent::ThreadChange)
{
QTimer::singleShot(0, this, SLOT(start());
}
return QObject::event(e);
}
void Consumer::consume()
{
getData();
processData();
emit ready();
}
void Consumer::start()
{
checkTimer_->start(1000);
emit ready();
}
void Consumer::onTimerExpiration()
{
qDebug() << "timeout";
}
Then run it in a separate thread as follows:
...
Consumer *consumer = new Consumer();
...
QThread *thread = new QThread(this);
thread->start();
consumer->moveToThread(thread);
All child objects of Consumer will run in the context of the thread Consumer was moved to. It is possible to create a timeout signal for the Consumer class and connect it with an object that is not running in thread. Qt will ensure that the proper signal/slot types are applied to the connection once the object is moved to the thread.
I left out the whole isRunning part as I doubt you will still need it as long as you only create one Consumer.
Threads do not have their own event loops unless you explicitly create event loops in them. See http://doc.qt.io/qt-5/threads-qobject.html#per-thread-event-loop for details.
Perhaps you need an event loop running on the thread. What about changing the code to the following?
void MyClass::run( void )
{
checkTimer_chA = new QTimer( this );
qDebug() << connect( checkTimer_chA, SIGNAL( timeout() ), this, SLOT( timerExpiry_chA() ) );
checkTimer_chA->start( 1000 );
// prevent multiple, simultaneous starts
if( !isRunning )
{
qDebug() << "Thread: MyClass::run";
isRunning = true;
QTimer::singleShot(0, this, SLOT(process()));
exec();
}
checkTimer_chA->stop();
delete checkTimer_chA;
}
void MyClass::process()
{
if( isRunning )
{
getData();
processData();
yieldCurrentThread();
QTimer::singleShot(0, this, SLOT(process()));
}
else
QThread::exit();
}
void MyClass::timerExpiry_chA( void )
{
qDebug() << "timerExpiry_chA";
checkTimer_chA->stop();
}