0

This is using a sub-classed QThread based on the ideas expressed in the whitepaper "QThreads: You were not doing so wrong". It does not have an event loop, nor does it have slots. It just emits signals and stops. In fact its primary signal is the QThread finished one.

Basically I have a Qt using a background thread to monitor stuff. Upon finding what it is looking for, it records its data, and terminates.

The termination sends a signal to the main event loop part of the application, which processes it, and when done, starts the background anew. I can usually get this working for tens of seconds, but then it just seems to quit.

It seems that when the main application tries to start the thread, it doesn't really run. I base this on telemetry code that increments counters as procedures get executed.

basically

//in main application.  Setup not shown. 
//background points to the QThread sub-class object
void MainWindow::StartBackground()
{
    background->startcount++;
    background->start();

    if ( background->isRunning() )
    {
        background->startedcount++;
    }
}


//in sub-classed QThread
void Background::run()
{
    runcount++;

    //Do stuff until done
}

So when I notice that it seems that my background thread isn't running, by watching Process Explorer, I cause the debugger to break in, and check the counts. What I see is that startcount and startedcount are equal. And have a value of one greater than runcount

So I can only conclude that the thread didn't really run, but I have been unable to find out any evidence of why.

I have not been able to find documentation on QThreads not starting do to some error condition, or what evidence there is of such an error.

I suppose I could set up a slot to catch started from the thread. The starting code could loop on a timed-out semaphore, trying again and again until the started slot actually resets the semaphore. But it feels ugly.

EDIT - further information

So using the semaphore method, I have a way to breakpoint on failure to start.

I sampled isFinished() right before I wanted to do start(), and it was false. After my 100ms semaphore timeout it became true.

So the question seems to be evolving into 'Why does QThread sometimes emit a finished() signal before isFinished() becomes true?'

Heck of a race condition. I'd hate to spin on isFinished() before starting the next background thread.

So this may be a duplicate of

QThread emits finished() signal but isRunning() returns true and isFinished() returns false

But not exactly, because I do override run() and I have no event loop.

In particular the events 8 and 9 in that answer are not in the same order. My slot is getting a finished() before isFinished() goes true.

I'm not sure an explicit quit() is any different than letting run() return;

Community
  • 1
  • 1
infixed
  • 1,155
  • 7
  • 15
  • What happens if you put a breakpoint on the statement `runcount++;` in `Background::run`? Is the breakpoint hit? – G.M. Jul 20 '16 at 21:33
  • @G.M. It would be hit for everytime that the runcount variable got incremented. Which is multiple times. `runcount` is non-zero. It is just one less than `startcount`. but with no way to predict when it won't get incremented. It's a breakpoint so it would be basically be F5 key (continue), breakpoint F5, breakpoint... many times, until F5 no breakpoint – infixed Jul 20 '16 at 21:41
  • Please provide more information, and make sure your code follows the guide specified in [the manual](http://doc.qt.io/qt-5/qthread.html)! http://doc.qt.io/qt-5/threads-technologies.html – Tadinu Jul 21 '16 at 04:28
  • From your link "Subclassing a QThread allows the application to initialize the new thread before starting its event loop, or to run parallel code without an event loop" I am following the guidelines it gives for that approach (Basically the first case in the Use Cases section) – infixed Jul 21 '16 at 12:36

2 Answers2

0

It sounds as if you have a race condition wherein you may end up trying to restart your thread before the previous iteration has actually finished. If that's the case then, from what I've seen, the next call to QThread::start will be silently ignored. You need to update your code so that it checks the status of the thread before restarting -- either by calling QThread::isFinished or handling the QThread::finished signal.

On the other hand... why have the thread repeatedly started/stopped. Would it not be easier to simply start the thread once? Whatever code is run within the context of QThread::run can monitor whatever it monitors and signal the main app when it finds anything of note.

Better still. Separate the monitor logic from the thread entirely...

class monitor: public QObject {
    .
    .
    .
};

QThread monitor_thread;
monitor monitor;

/*
 * Fix up any signals to/from monitor.
 */
monitor.moveToThread(&monitor_thread);
monitor_thread.start();

The monitor class can do whatever it wants and when it's time to quit the app can just call monitor_thread::quit.

G.M.
  • 12,232
  • 2
  • 15
  • 18
  • Well, the stopping thing is because the events are handled with a human interaction. In a previous factorization I was keeping the thread running. But signaling It to continue was an issue. If I were to make a slot to signal, that would have broken the "You were not doing so wrong" QThread sub-class idiom. Semaphores were considered, but conceptually just having the thread end and then restart it should have been simple. I did have a telemetry counter at the end of `run` to see if the thread exited. It did exit out the end of `run` and a `finished` signal was caught. – infixed Jul 21 '16 at 12:27
  • The separate monitor concept seem like the 'right' answer in a different white paper I've read titled "You are doing it wrong", to which the "not doing so wrong" paper was a response. It seems like this might be a controversy in the Qt community as deep as the rift between `vi` and `emacs` – infixed Jul 21 '16 at 12:36
0

There is a race condition in the version of Qt I am using. I don't know if it was reported or not before, but I do not have the latest, so it's probably moot unless I can demonstrate it in the current version.

Similar bugs were reported here long ago:

QThread.isFinished returns False in slot connected to finished() signal

(the version I use is much more recent than Qt 4.8.5)

What more important is I can workaround it with the following code

while ( isRunning() )
{
    msleep(1);
}
start();

I've run a few tests, and it never seems to take more than 1ms for the race condition to settle. Probably just needs a context switch to clean up.

Community
  • 1
  • 1
infixed
  • 1,155
  • 7
  • 15