Reputation: 2742
I have the current situation:
The Worker is a field in the MainWindow class, the Watchdog is a field in the Worker class.
The execution goes like this:
Worker
is constructed
class Worker : public QThread
{
Q_OBJECT
public:
explicit Worker();
void run();
private:
Watchdog *watchdog;
bool running = false;
signals:
void tick();
public slots:
void ownerDied();
};
The constructor of Worker
constructs a Watchdog
on the heap
class Watchdog : public QThread
{
Q_OBJECT
public:
Watchdog();
void run();
public slots:
void tick();
signals:
void ownerIsDead();
};
The constructor does QObject::connect()
between the Watchdog
and Worker
signals and slots
connect(this, SIGNAL(tick()), watchdog, SLOT(tick()));
connect(watchdog, SIGNAL(ownerIsDead()), this, SLOT(ownerDied()));
The main loop of the Worker
starts in the Worker::run()
method.
The Worker
starts the Watchdog
. The Watchdog
loop is started.
If the Worker
does not tick()
within 5 seconds of the start()
call, the Watchdog
emits the ownerIsDead()
signal
ownerDied()
signal, killing the main Worker
loopWorker
does tick the Watchdog
, he sleeps another 5 secondsThe problem is, the tick()
never reaches the Watchdog
, nor does the ownerIsDead()
signal reach the worker because it did not tick. Why?
Here is the raw code, the class names are a bit different.
watchdog.h
#ifndef WATCHDOG_H
#define WATCHDOG_H
#define THRESHOLD 1000
#include <QThread>
#include <QObject>
class Watchdog : public QThread
{
Q_OBJECT
public:
Watchdog();
void run();
public slots:
void tick();
void kill();
private:
bool running = false;
bool ticked = false;
signals:
void error();
};
#endif // WATCHDOG_H
watchdog.cpp
#include "watchdog.h"
#include <QDebug>
Watchdog::Watchdog()
{
}
void Watchdog::run()
{
running = true;
qDebug() << "Starting watchdog";
while (running) {
QThread::msleep(THRESHOLD);
qDebug() << "Watchdog tick ... ";
if (!ticked) {
qDebug() << "read() or write() is read";
emit error();
}
}
}
void Watchdog::tick()
{
qDebug() << "Watchdog ticking";
ticked = true;
}
void Watchdog::kill()
{
qDebug() << "Killing watchdog...";
running = false;
}
diskerror.h ( AKA the 'Worker' )
#ifndef DISKERROR_H
#define DISKERROR_H
#include <QThread>
#include <watchdog.h>
extern "C" {
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <malloc.h>
}
class DiskError : public QThread
{
Q_OBJECT
public:
explicit DiskError();
void run();
private:
int mismatch(char *a, char *b);
Watchdog *watchdog;
bool running = false;
signals:
void tick();
void killWatchdog();
public slots:
void ownerIsDead();
};
#endif // DISKERROR_H
diskerror.cpp
include "diskerror.h"
#include "watchdog.h"
#include <QDebug>
#define BLKSZ 4096
DiskError::DiskError()
{
watchdog = new Watchdog();
connect(this, SIGNAL(killWatchdog()), watchdog, SLOT(kill()));
connect(this, SIGNAL(tick()), watchdog, SLOT(tick()));
connect(watchdog, SIGNAL(error()), this, SLOT(ownerIsDead()));
}
void DiskError::run()
{
int fd = open("/dev/sdc", O_RDWR | O_SYNC);
if (fd < 0) {
qDebug() << strerror(errno);
}
size_t size;
if (ioctl(fd, BLKGETSIZE64, &size) < 0) {
qDebug() << "IOCTL Error";
return;
}
size_t step = (size / 2500);
size_t done = 0;
int i = 0;
char testing[BLKSZ];
char pattern[BLKSZ];
for (int i = 0; i < BLKSZ; i++) {
pattern[i] = 0xCF;
}
int re, bb, wr;
off_t curr = 0;
watchdog->start();
running = true;
while (running) {
lseek(fd, curr, SEEK_SET);
wr = write(fd, pattern, BLKSZ); /* Write pattern to disk */
lseek(fd, curr, SEEK_SET);
re = read(fd, testing, BLKSZ); /* Read pattern back from disk */
bb = mismatch(pattern, testing);
curr += BLKSZ;
done += BLKSZ;
emit tick();
if ( (re == 0) || (wr < 0) ) {
qDebug() << "Flushing buffers...";
sync();
break;
}
if (done >= step) {
if (bb) {
qDebug() << "[" << i << "] Error occured";
} else {
qDebug() << "[" << i << "] OK";
}
done = 0;
i++;
}
}
emit killWatchdog();
sync();
if (close(fd) < 0) {
qDebug() << "Error closing device";
}
}
int DiskError::mismatch(char *a, char *b)
{
for (int i = 0; i < BLKSZ; i++) {
if ( (*(a+i)) != (*(b+i)) ) return 1;
}
return 0;
}
void DiskError::ownerIsDead()
{
qDebug() << "read() call blocked for more than 5 seconds, device inoperable";
}
I never see the debug text in the worker class, nor do I see the tick text in the worker.
Upvotes: 0
Views: 499
Reputation: 186
As stated in previous comments and answers by other guys, I think the central problem is that you have no event loop in your "threads", which is the responsible of managing (check, elaborate and do actions according) signals/slots. Also infinite loop is the worst thing to do, because it prevent your thread/application to "call" the event loop, so that signal/slot system becomes useless. You can find a more clear and extensive explenation here: https://wiki.qt.io/Threads_Events_QObjects
A trick to allow your while loop working with signal/slot is to call at each iteration the function QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags). From Qt docs:
Processes all pending events for the calling thread according to the specified flags until there are no more events to process.
You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).
In event you are running a local loop which calls this function continuously, without an event loop, the DeferredDelete events will not be processed. This can affect the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function properly. An alternative would be to call sendPostedEvents() from within that local loop.
Calling this function processes events only for the calling thread.
One last comment: the way you have implemented threading is not wrong, but is also not the way Qt suggests (but maybe the one you have choosen knowing what I'm saying). In fact, QThread objects are not the threads, but the objects managing threads. More information about this at this link of Qt docs: http://doc.qt.io/qt-4.8/qthread.html#details
Upvotes: 1
Reputation: 5207
What could be happening is that the receiver object "belongs" to a different thread than the one doing the emit.
This kind of cross-thread signal/slot connections, so called Qt::QueuedConnection
connections, require a running event loop in the thread of the receiver object.
If the receiver object has been created by one of the additional threads, then this thread needs to run its event loop, see QThread::exec()
Not sure you actually need the watchdog to be a separate thread, it seems in only checks a condition in regular intervals, something a QTimer
on the main thread could easily do as well.
Upvotes: 2