3

I have a thread which blocks until data is received from a system resources such as a USB device. I chose this model because the amount of data may vary, and data may be received at any time. Upon exiting the application, I get the message “QThread: Destroyed while thread is still running”. How should I go about closing these threads?

I’ve looked at other problems/solutions such as:

The first solution involves using a flag (included in my code) however my thread will never reach the flag check. The second solution uses QWaitCondition but seem to be along the same lines as the first.

I’ve included a stripped down version of the code below. The system calls WaitForSingleObject() is a substitute for what I actually use (GetOverlappedResult()).

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>
#include <QThread>
#include <QReadWriteLock>
#include <QDebug>

#ifdef Q_OS_WIN
    #include <windows.h>
#endif // Q_OS_WIN

#ifdef Q_OS_LINUX
    #include <unistd.h>
#endif // Q_OS_LINUX

////////////////////////////////////////////////
//
//  Worker Object
//
////////////////////////////////////////////////
class Worker : public QObject {
    Q_OBJECT

public:
    QReadWriteLock lock;
    bool running;

public slots:
    void loop() {
        qDebug() << "entering the loop";
        bool _running;
        forever {

            lock.lockForRead();
            _running = running;
            lock.unlock();

            if (!_running) return;

            qDebug() << "loop iteration";

            #ifdef Q_OS_WIN
                HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
                WaitForSingleObject(event, INFINITE);
            #endif // Q_OS_WIN

            #ifdef Q_OS_LINUX
                read(0, 0, 1);
            #endif // Q_OS_LINUX
        }
    }
};

////////////////////////////////////////////////
//
//  Controller
//
////////////////////////////////////////////////
class Controller {
public:
    Controller() {
        myWorker.connect(&myThread, SIGNAL(started()), &myWorker, SLOT(loop()));
        myWorker.moveToThread(&myThread);
        myThread.start();
    }

    ~Controller() {
        // Safely close threads

        myWorker.lock.lockForWrite();
        myWorker.running = false;
        myWorker.lock.unlock();

        myThread.quit();

        //myThread.wait();
        //myThread.exit();
        //myThread.terminate();
    }

private:
    QThread myThread;
    Worker myWorker;
};

#endif // CONTROLLER_H
Daniel
  • 8,655
  • 5
  • 60
  • 87
  • 2
    Why aren't you using Qt's own IO functions, like QFile, etc.? They work with signals and slots, so you could just watch the `bytesReadyRead` signal. – sashoalm Nov 29 '12 at 07:51
  • @satuon I'm making my own QIODevice which reads and writes to HID devices. Although this never occurred to me before, perhaps using QFile can be used to access these devices. I'll try that out, thanks. – Daniel Nov 29 '12 at 12:51
  • 1
    Btw you could improve your question by making it shorter and more succinct - I read less than 10% of it, because it was so long. Also you should post only the relevant parts of your code, not the entire file. – sashoalm Nov 29 '12 at 12:55
  • @satuon Using QFile did not work. Although QFile::open() returns true, writing and reading does not work, probably because the device is sequential. I also tried the [QtSerialPort](http://qt-project.org/wiki/QtSerialPort) library but SerialPort::open() returns false. Although I've trimmed a few things from the original question, there's nothing wrong with the length of this question. The code is already stripped down and only contains 2 classes and 3 functions. – Daniel Nov 29 '12 at 14:49
  • OK, then use `WaitForMultipleObjects`, and add a second object like a HEVENT, which you would send when the thread needs to finish. – sashoalm Nov 29 '12 at 15:04
  • @satuon Ah yes that should do the trick. I'll start looking into a similar linux solution now, unless you'd know how to do that too off the top of your head? – Daniel Nov 29 '12 at 15:08
  • See http://stackoverflow.com/q/2719580/492336 – sashoalm Nov 29 '12 at 15:10
  • @satuon For linux I ended up using [sigaction(SIGUSR1, ...)](http://linux.die.net/man/3/sigaction) which interrupted read() with errno EINTR. Although I didn't try the windows version yet, I'm confident your suggestion will work. If you're willing to submit an answer to this question, I'll definitely select it as the answer. – Daniel Nov 29 '12 at 18:11

1 Answers1

-1

For Linux:

Sending a signal to the thread with pthread_kill() interrupted read() with failure code EINTR. sigaction() was used to register the signal, and the signal thrown was SIGUSR1.

// Global scope
void nothing(int signum) {}

...

// Within the start of the thread
pthread_t myThreadID = pthread_self(); // Get the thread ID
struct sigaction action;
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
action.sa_handler = nothing;
sigaction(SIGUSR1, &action, NULL);

...

// When it's time to close the thread
pthread_kill(myThreadID, SIGUSR1);

For Windows:

Signaling the OVERLAPPED's hEvent with SetEvent() was used to unblock GetOverlappedResult().

// Store a reference to the event
HANDLE myEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

...

// Within the start of the thread
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = myEvent;

...

// When it's time to close the thread
SetEvent(myEvent);
Daniel
  • 8,655
  • 5
  • 60
  • 87
  • 1
    Your answer is functionally identical to the quit(), terminate(), and exit() calls the QThread object already provides, except you picked a different signal to send. The reason your "solution" doesn't cause the error is because you added an explict signal handler. In theory you could also just put a generic signal handler in your QThread from your original code and it would solve the problem the same way. (I wasn't the downvote) – mtalexan Sep 20 '17 at 20:51
  • If someone could post a proper answer, I'll gladly switch the accepted answer to that one. – Daniel Jan 17 '19 at 00:15