简体   繁体   中英

Why won't my nested QEventLoop deliver all the events for my QThread?

I'm having a problem with QEventLoop. I want to create a "TimeBoundExerciser" for my unit test so that my SUT, which blocks on a QEventLoop, won't block the rest of the test cases. Specifically, my test case is to make sure the SUT terminates after a timeout.

The TimeBoundExerciser basically spawns a thread, executes the SUT on that thread, waits for the thread to terminate, and if it doesn't terminate after a specific amount of time, invokes the quit() method on the thread through QMetaObject::invokeMethod() and a QueuedConnection. I would expect that executing quit() will cause my nested QEventLoop to exit, terminating my thread. However, what I've found is that the quit() method is never invoked, and the thread never terminates. The code for my TimeBoundExerciser is below:

class IExerciseTheSystem
{
    void operator()() = 0;
};

class TimeBoundExerciser : private QThread
{
Q_OBJECT
public:
    enum CompletionType
    {
        TERMINATED,
        FORCE_QUIT,
        QUIT
    };
    TimeBoundExerciser(const IExerciseTheSystem& exerciser);
    CompletionType exercise(unsigned long timeoutMillis);   
protected:
    void run();

protected slots:
    void exerciseTheSystem();
private:
    const IExerciseTheSystem& exerciser;
};

TimeBoundExerciser::TimeBoundExerciser(const IExerciseTheSystem& exerciser) : exerciser(exerciser)
{

}

TimeBoundExerciser::CompletionType TimeBoundExerciser::exercise(unsigned long timeoutMillis)
{
    start();
    while (!isRunning()) 
    {
        msleep(10);
    }

    moveToThread(this);

    wait(timeoutMillis);
    if (!isFinished()) 
    {
        bool quitResult;
        QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection, Q_RETURN_ARG(bool, quitResult));
        wait();
        return FORCE_QUIT;
    }

    return QUIT;
}

void TimeBoundExerciser::run()
{
    setTerminationEnabled(true);
    QMetaObject::invokeMethod(this, "exerciseTheSystem", Qt::QueuedConnection);
    exec();
}

void TimeBoundExerciser::exerciseTheSystem()
{
    cout << "Starting exerciser" << endl;
    exerciser();
    cout << "Exerciser ended" << endl;
}

The exercise() method is executed on the main thread to kick off the whole process.

If the test runs too long, it's probably in some sort of loop processing data.

Naturally, your quit request won't be delivered because the test thread is busy running the test. Messages don't interrupt threads, they are processed when the thread finishes processing the previous message and resumes the event loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM