Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use QProcess write correctly?

I need a program to communicate with a subprocess that is relying on in- and output. The problem is that I am apparently not able to use QProcess correctly.

The code further down should create a QProcess, start it and enter the main while loop. In there it prints all the output created by the subprocess to the console and subsequently asks the user for input which is then passed to the subprocess via write(...).

Originally I had two problems emerging from this scenario:

  1. The printf's of the subprocess could not be read by the parent process.
  2. scanf in the subprocess is not receiving the strings sent via write.

As for (1), I came to realize that this is a problem caused by the buffering of the subprocess' stdout. This problem can be solved easily with fflush(stdout) calls or manipulations regarding its flushing behavior.

The second problem is the one I can't wrap my head around. write gets called and even returns the correct number of sent bytes. The subprocess, however, is not continuing its excecution, because no new data is written to its output. The scanf seems not to be receiving the data sent. The output given by the program is:

Subprocess should have started.
124 bytes available!
Attempting to read:
Read: This is a simple demo application.
Read: It solely reads stdin and echoes its contents.
Read: Input exit to terminate.
Read: ---------
Awaiting user input: test
Written 5 bytes
No line to be read...
Awaiting user input: 

I am seriously stuck right here. Google + heavy thinking having failed on me, I want to pass this on to you as my last beacon of hope. In case I am just failing to see the forest for all the trees, my apologies.

In case this information is necessary: I am working on 64bit MacOS X using Qt5 and the clang compiler. The subprocess-code is compiled with gcc on the same machine.

Thank you very much in advance,

NR

Main-Code:

int main() {
    // Command to execute the subprocess
    QString program = "./demo";
    QProcess sub;
    sub.start(program, QProcess::Unbuffered | QProcess::ReadWrite);

    // Check, whether the subprocess is starting correctly.
    if (!sub.waitForStarted()) {
        std::cout << "Subprocess could not be started!" << std::endl;
        sub.close();
        return 99;
    }

    std::cout << "Subprocess should have started." << std::endl;

    // Check, if the subprocess has written its starting message to the output.
    if (!sub.waitForReadyRead()) {
        std::cout << "No data available for reading. An error must have occurred." << std::endl;
        sub.close();
        return 99;
    }

    while (1) {
        // Try to read the subprocess' output
        if (!sub.canReadLine()) {
            std::cout << "No line to be read..." << std::endl;
        } else {
            std::cout << sub.bytesAvailable() << " bytes available!" << std::endl;
            std::cout << "Attempting to read..." << std::endl;
            while (sub.canReadLine()) {
                QByteArray output = sub.readLine();
                std::cout << "Read: " << output.data();
            }
        }

        std::cout << "Awaiting user input: ";
        std::string input;
        getline(std::cin, input);

        if (input.compare("exit") == 0) break;

        qint64 a = sub.write(input.c_str());
        qint64 b = sub.write("\n");
        sub.waitForBytesWritten();
        std::cout << "Written " << a + b << " bytes" << std::endl;
    }

    std::cout << "Terminating..." << std::endl;
    sub.close();
}

Subprocess-Code:

int main() {
    printf("This is a simple demo application.\n");
    printf("It reads stdin and echoes its contents.\n");
    printf("Input \"exit\" to terminate.\n");

    while (1) {
        char str[256];
        printf("Input: ");
        fflush(stdout);
        scanf("%s", str);

        if (strcmp(str, "exit") == 0) return 0;

        printf("> %s\n", str);
    }
}

P.s: Since this is my first question on SO, please tell me if something is wrong concerning the asking style.


Solution

After many many more trials & errors, I managed to come up with a solution to the problem. Adding a call to waitForReadyRead() causes the main process to wait until new output is written by the subprocess. The working code is:

...
sub.waitForBytesWritten();
std::cout << "Written " << a + b << " bytes" << std::endl;
// Wait for new output
sub.waitForReadyRead();
...

I still don't have a clue why it works this way. I guess it somehow relates to the blocking of the main process by getline() vs blocking by waitForReadyRead(). To me it appears as if getline() blocks everything, including the subprocess, causing the scanf call never to be processed due to race conditions.

It would be great, if someone who understands could drop an explanation.

Thank you for your help :)

NR

like image 925
NRiesterer Avatar asked Oct 21 '22 12:10

NRiesterer


1 Answers

This will not work. You are waiting for the sent bytes to be written but you are not waiting for the echo. Instead you are entering the getline() function waiting for new user input. Keep in mind that two processes are involved here where each process can be delayed to any degree.

Apart from this you should consider building your Qt application asynchronously (having an event loop) instead of trying the synchronous approach. This way your Qt application can do things in parallel... e.g. reading input or waiting for input from the remote process while still not being blocked and able to accept user input.

like image 160
Silicomancer Avatar answered Oct 23 '22 03:10

Silicomancer