3

In the following code omitting the waitForFinished() makes the QProcess stop emitting its signal. What the heck is wrong with it? Is this a Qt Bug? (5.7). Note this code is run parallel with QtConcurrent run. But this should not change anything, should it? Afaik sending signals in other threads is fine, though they will be queued.

QProcess *process = new QProcess;
process->setReadChannel(QProcess::StandardOutput);

connect(process, &QProcess::readyReadStandardOutput, [](){
    qDebug()<< "readyReadStandardOutput";
});

connect(process, &QProcess::stateChanged, [](QProcess::ProcessState state){
    qDebug()<< "stateChanged"<< state;
});

connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
    [=](){
    qDebug()<< "finsished";
});

connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
    [this, process](int exitCode, QProcess::ExitStatus exitStatus){
    qDebug()<< "finsished";
    if (exitStatus == QProcess::NormalExit && exitCode == 0){
        while (process->canReadLine()) {
           QString line = QString::fromLocal8Bit(process->readLine());
           QRegularExpression regex("\"(.*)\" {(.*)}");
           QRegularExpressionMatch match = regex.match(line);
           names_.push_back(match.captured(1));
           uuids_.push_back(match.captured(2));
        }
    }
    process->deleteLater();
});
process->start("VBoxManage",  {"list", "vms"});
process->waitForFinished(); // This line changes everything
qDebug()<< "leftWaitForFinished";
ManuelSchneid3r
  • 15,850
  • 12
  • 65
  • 103
  • do you have alll this code inside a lambda function that is passed to `QtConcurrent::run`? – Mike Aug 19 '16 at 12:38

1 Answers1

7

You're not running an event loop in the thread where the QProcess instance lives. Any QObject in a thread without an event loop is only partially functional - timers won't run, queued calls won't be delivered, etc. So you can't do that. Using QObjects with QtConcurrent::run requires care.

At the very least, you should have a temporary event loop for as long as the process lives - in that case you should hold QProcess by value, since deleteLater won't be executed after the event loop has quit.

QProcess process;
...
QEventLoop loop;
connect(process, &QProcess::finished, &loop, &QEventLoop::quit);
loop.exec();

Otherwise, you need to keep the process in a more durable thread, and keep that thread handle (QThread is but a handle!) in a thread that has an event loop that can dispose of it when it's done.

// This can be run from a lambda that runs in an arbitrary thread

auto thread = new QThread;
auto process = new QProcess;

...

connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
    [this, process](int exitCode, QProcess::ExitStatus exitStatus){
    ...
    process->deleteLater();
    process->thread()->quit();
});

process->start("VBoxManage",  {"list", "vms"});
process->moveToThread(thread);

// Move the thread **handle** to the main thread
thread->moveToThread(qApp->thread());
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();

Alas, this is very silly since you're creating temporary threads and that's expensive and wasteful. You should have one additional worker thread where you take care of all low-overhead work such as QProcess interaction. That thread should always be running, and you can move all QProcess and similar object instances to it, from concurrent lambdas etc.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • There are multiple jobs like this that have to be done. I use Concurrent because this way I can easily use all cores. However, why exactly does it not work? Is it the lambda? The object I call run on is in the main thread. Will making the lambda a member function work? – ManuelSchneid3r Aug 19 '16 at 13:36
  • sorry as this may sound stupid, but aren't threads from `QThreadPool` already running an event loop? (in order to execute functions that are passed to `QThreadPool::start` for instance) – Mike Aug 19 '16 at 13:37
  • 2
    I've said exactly why it doesn't work: it's because for a `QObject` like a `QProcess` to be functional, the thread it runs on must run an event loop! It has **nothing** to do with lambdas or anything like that. The object where you call `run` is irrelevant: the lambda runs in a worker thread where there's no event loop. – Kuba hasn't forgotten Monica Aug 19 '16 at 13:38
  • 1
    No, the threads from the event pool are *not* running an event loop. Event loops are comparatively high-latency because they interoperate with operating system event queues. The `QThreadPool` uses faster, direct mechanisms like mutexes/semaphores. The jobs you submit to `QThreadPool` can be very short, on the order of 0.01ms-0.1ms - it must have very low overhead when presented with such jobs, and the event loop wouldn't cut it generally speaking. – Kuba hasn't forgotten Monica Aug 19 '16 at 13:40
  • thanks @KubaOber , I expected that after reading your answer, just wanted to make sure. . . – Mike Aug 19 '16 at 13:40