PDA

View Full Version : QThread::wait() causing crash



Olliebrown
24th September 2010, 15:56
Greetings!

I'm having a problem with my QThread classes. I based them on the Qt Example Mandlebrot (I'm also doing an long rendering process). I extracted the core mutex and waitCondition syncronization that was in the renderThread into it's own class (which I call InfiniteThread) and then I sub-class this to specialize what task that thread will perform.

Problem is, whenever an instance of InfiniteThread is destroyed and the destructor gets called I get a SIGABRT and the following console output:



pure virtual method called
terminate called without an active exception
The program has unexpectedly finished.


Here's the top of the stack at the time:



0 __semwait_signal 0 0x00007fff87ecdeb6
1 _pthread_cond_wait 0 0x00007fff87ed1cd1
2 QWaitCondition::wait 0 0x000000010103bfa0
3 QThread::wait 0 0x00000001010a419e
4 InfiniteThread::~InfiniteThread infinitethread.cpp 17 0x000000010001022c
5 EMSampleThread::~EMSampleThread emsamplethread.cpp 18 0x000000010000fbfd
6 RFViewer::~RFViewer rfviewer.cpp 50 0x0000000100008a8f
7 main main.cpp 10 0x000000010000602a


So the SIGABRT seems to be coming from QThread::wait(), which is called in ~InfiniteThread(). Code for InfiniteThread is included below. Note that InfiniteThread::centralTask() is a pure-virtual member that is re-implemented to specialize the thread for a specific task.

Like I mentioned before, InfiniteThread is based entirely on renderthread.cpp in the Mandlebrot example and it does not have this problem. Any help is much appreciated. I'm new to threading but I am pretty comfortable with Qt.

Thanks!
Seth

InfiniteThread.h:


#ifndef INFINITETHREAD_H
#define INFINITETHREAD_H

#include <QThread>
#include <QMutex>
#include <QWaitCondition>

class InfiniteThread : public QThread
{
Q_OBJECT

public:
explicit InfiniteThread(QObject *parent = 0);
~InfiniteThread();

signals:
void reportProgress(int pProg, QString message);
void taskDone(bool pSuccess);

protected:
void startTask();
void run();

// Override this in the child class
virtual bool centralTask() = 0;

QMutex mutex;
QWaitCondition condition;
bool restart;
bool abort;
};

#endif // INFINITETHREAD_H


InfiniteThread.cpp:


#include "infinitethread.h"

InfiniteThread::InfiniteThread(QObject *parent) :
QThread(parent)
{
restart = false;
abort = false;
}

InfiniteThread::~InfiniteThread()
{
mutex.lock();
abort = true;
condition.wakeOne();
mutex.unlock();

wait();
}

void InfiniteThread::startTask()
{
QMutexLocker locker(&mutex);

if (!isRunning()) {
start(LowPriority);
} else {
restart = true;
condition.wakeOne();
}
}

void InfiniteThread::run()
{
forever {
// Perform central task
bool result = centralTask();
if(!restart) emit taskDone(result);

// Abort thread if requested
if(abort) return;

// Restart when new central task is given
mutex.lock();
if(!restart) condition.wait(&mutex);
restart = false;
mutex.unlock();
}
}

tbscope
24th September 2010, 16:04
The error says it all:


pure virtual method called

Your problem is this line:

virtual bool centralTask() = 0;

I guess you implemented this in a subclass?
Looking at the trace, I guess the subclass gets deleted first (obviously)?

Edit: possible solution, move the run() code from your base class to the subclass.

Olliebrown
24th September 2010, 16:13
Ahhh! Yup, that makes sense. I was thoroughly confused by the error as the only pure virtual function was CentralTask() and I was re-implementing it properly. It never occurred to me that if the sub-class get's deleted then this will no longer be the case. Good eye!

Thanks!
Seth

Olliebrown
24th September 2010, 16:24
That did the trick! I worked around the deletion order by moving the code that's in the destructor into a function called stopTask(). Then, I call this from the child's destructor so the run loop will get stopped BEFORE CentralTask() becomes a pure virtual function again. Worked like a charm.

Here's the updated code:

Seth



#ifndef INFINITETHREAD_H
#define INFINITETHREAD_H

#include <QThread>
#include <QMutex>
#include <QWaitCondition>

class InfiniteThread : public QThread
{
Q_OBJECT

public:
explicit InfiniteThread(QObject *parent = 0);
~InfiniteThread();

void startTask();
void stopTask();

signals:
void reportProgress(int pProg, QString message);
void taskDone(bool pSuccess);

protected:
void run();

// Override this in the child class
virtual bool centralTask() = 0;

QMutex mutex;
QWaitCondition condition;
bool restart;
bool abort;
};

#endif // INFINITETHREAD_H




#include "infinitethread.h"

InfiniteThread::InfiniteThread(QObject *parent) :
QThread(parent)
{
restart = false;
abort = false;
}

InfiniteThread::~InfiniteThread()
{
}

void InfiniteThread::stopTask()
{
mutex.lock();
abort = true;
condition.wakeOne();
mutex.unlock();

wait();
}

void InfiniteThread::startTask()
{
QMutexLocker locker(&mutex);

if (!isRunning()) {
start(LowPriority);
} else {
restart = true;
condition.wakeOne();
}
}

void InfiniteThread::run()
{
forever {
// Perform central task
bool result = centralTask();
if(!restart) emit taskDone(result);

// Abort thread if requested
if(abort) return;

// Restart when new central task is given
mutex.lock();
if(!restart) condition.wait(&mutex);
restart = false;
mutex.unlock();
}
}