QProcess writing to stdin of child process failing - c++

I have a qt c++ gui application A (parent) that sometimes starts a second qt c++ gui application B (child). parent 'A' communicates with child 'B' by writing bytes to stdin of qprocess B and also receiving bytes from stdout of B.
This code (snippet below) works perfectly on some machine but not on others. Target environment is windows 10 and all machines tested were running windows 10.
On the machines where application is failing I see the following on logs:
Critical error: QWindowsPipeWriter::write failed. (The handle is invalid.)
Is there a windows 10 setting or some other operating system setting that would prevent Parent process from writing to stdin of child process ?
QProcess* child_pr = new QProcess();
QString program = "C:/Program Files/Child/child_pr.exe";
child_pr->setEnvironment(QProcess::systemEnvironment());
child_pr->setProcessChannelMode(QProcess::MergedChannels);//.. merges output of running process to standard output channel
child_pr->setProgram(program);
connect(child_pr, &QProcess::readyReadStandardOutput, this, &ParentAppClass::updateReceived);
connect(child_pr, SIGNAL(finished(int , QProcess::ExitStatus)), this, SLOT(finishReceived(int, QProcess::ExitStatus)));
QFileInfo fileInfo(program);
child_pr->setWorkingDirectory(fileInfo.absolutePath());
child_pr->start( QProcess::Unbuffered | QProcess::ReadWrite);
bool started = child_pr->waitForStarted();
if (!started) {
qDebug() << __FUNCTION__ << __LINE__ << " Child not started as a process." << "\n";
}
int counter = 0;
while (false == (receivedAckFlags.value(child_pr->processId()))) { // note : slot method "updateReceived" handles whether ack is received or not and updates "receivedAckFlags"
child_pr->write(QString("spawned"));
counter++;
bool writeSuccess = child_pr->waitForBytesWritten();
if (!writeSuccess) {
qDebug() << __FUNCTION__ << __LINE__ << " Writing spawned message timed out or other issue." << "\n";
}
qDebug() << __FUNCTION__ << __LINE__ << " Sent spawned message " << counter << " times." << "\n";
QThread::sleep(1); //.. wait one sec before write again.
QApplication::processEvents();//.. to make sure slots for handling std in read are called
}

Related

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:
The printf's of the subprocess could not be read by the parent process.
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
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.

QSerialPort::readLine doesn't work as expected on MS Windows

I'm trying to connect a micro-controller with my desktop PC via USB-serial cable.
The OS of my desktop PC is Windows 8.1, and USB-serial cable is TTL-232R-3V3. (FTDI)
(Qt version: 5.2.0 beta1, QtCreator Version: 3.0, Compiler: MSVC2012)
Now I'm trying read/write loop-back tests, and that's why RX/TX pin of USB-serial cable are connected with each other.
Here is my code.
#include <QtCore/QCoreApplication>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QtCore/QDebug>
#define PORT_NAME "COM3"
#define BAUDRATE 19600
#define TIMEOUT_MS 1000
QT_USE_NAMESPACE
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSerialPort pSerial(PORT_NAME);
const char strMsg[] = "#1:Send data line \n #2:Send data line\n #3:Send data line end\n";
char strBuf[256];
qint64 nByte;
if(pSerial.open(QIODevice::ReadWrite)){
pSerial.setBaudRate(BAUDRATE);
qDebug() << "OPEN PASS";
pSerial.write(strMsg);
pSerial.flush();
if(pSerial.waitForBytesWritten(TIMEOUT_MS)){
qDebug() << "WRITE PASS";
}
pSerial.waitForReadyRead(TIMEOUT_MS);
while(true){
if( pSerial.canReadLine()){
qDebug() << "CAN READ LINE";
nByte = pSerial.readLine(strBuf,sizeof(strBuf));
qDebug() << "Length: " << nByte;
qDebug() << "Read data: " << strBuf;
}
}
pSerial.close();
} else {
qDebug() << "OPEN FAIL\n";
}
return a.exec();
}
When the program starts to run, the result is different than I expected.
Only first line of sent data can be received. So, "Read data: #1 Send data line" is printed
on console. But the rest of sent data will never be received. Does anyone know why?
Any help would be appreciated.
Thanks in advance.
EDIT: I revised my code according to Papp's comment.Then it works as I expected.
All sent message has been received.
Does it mean I misunderstand the usage about readLine() or canReadLine()?
// while(true){
// if( pSerial.canReadLine()){
// qDebug() << "CAN READ LINE";
// nByte = pSerial.readLine(strBuf,sizeof(strBuf));
// qDebug() << "Length: " << nByte;
// qDebug() << "Read data: " << strBuf;
// }
// }
pSerial.waitForReadyRead(TIMEOUT_MS);
QByteArray readData = pSerial.readAll();
while (pSerial.waitForReadyRead(TIMEOUT_MS)) {
readData.append(pSerial.readAll());
}
qDebug() << "Read data: " << readData;
EDIT 2nd time : Following code also works for me.
while(true){
if( pSerial.waitForReadyRead(TIMEOUT_MS) && pSerial.canReadLine()){ // I revised this line
qDebug() << "CAN READ LINE";
nByte = pSerial.readLine(strBuf,sizeof(strBuf));
qDebug() << "Length: " << nByte;
qDebug() << "Read data: " << strBuf;
qDebug() << "Error Message: " << pSerial.errorString();
}
}
That is because you need to read in a loop like this:
QByteArray readData = serialPort.readAll();
while (serialPort.waitForReadyRead(5000))
readData.append(serialPort.readAll());
Please see the creadersync example for the details what I added to 5.2. You can also check the creaderasync example for non-blocking operation.
To be fair, we have not tested readLine that much, but it works for me on Unix, so does it on Windows for someone else.
The mistake that you've made is expecting to receive all the sent data when waitForReadyRead returns. When waitForReadyRead finishes, all you're guaranteed is some data being available to be read. It may be as little as one character, not necessarily a whole line.
The loop from your last modification is the almost correct way to do it. You should nest reading of the lines in a separate loop. The following code is how it should be done, and agrees with the semantics of QIODevice:
while (pSerial.waitForReadyRead(TIMEOUT_MS)) {
while (pSerial.canReadLine()) {
qDebug() << "NEW LINE";
QByteArray line = pSerial.readLine();
qDebug() << "Length: " << line.size();
qDebug() << "Read data: " << line;
qDebug() << "Error Message: " << pSerial.errorString();
}
}
qDebug << "TIMED OUT";
Note that none of this code should even run in the GUI thread. Ideally you should move it to a QObject, use the signals emitted by QIODevice (and thus QSerialPort), and move that object to a separate thread.
The GUI thread can sometimes block for long periods of time, it's not normally desirable to have it disturb the timeliness of your device communication. Similarly, you don't want device timeouts to block the GUI thread. Both are equally bad and are a very common source of bad user experience. Qt makes multithreading very easy - leverage it for your user's sake, and do it properly.
On Linux I have to do it this way to receive ASCII text ending with '\n'
QByteArray readData = pSerial.readAll();
while (readData[readData.length() - 1] != '\n') {
pSerial.waitForReadyRead(5000);
readData.append(pSerial.readAll());
}
QString result(readData);
QSerialPort::readLine() doesn't work for me either

Unusual signal numbers from WTERMSIG macro after waitpid()

I am seeing unusual signal numbers (for example 50, 80 or 117) from the following code when waiting for a child process to terminate. I am only seeing this from one particular child process, and I have no access to the process source code and it only happens some of the time.
I want to know what these unusual values mean, given NSIG == 32, and where I can find some documentation in the headers or man pages?
Note that this code runs in a loop sending progressively more menacing signals until the child terminates.
int status, signal;
if (waitpid(m_procId, &status, WNOHANG) < 0) {
LOGERR << "Failed to wait for process " << name() << ": " <<
strerror(errno) << " (" << errno << ")";
break;
} else if (WIFEXITED(status)) {
m_exitCode = WEXITSTATUS(status);
terminated = true;
LOGINF << "Process " << name() << " terminated with exit code " << m_exitCode;
} else if (WIFSIGNALED(status)) {
signal = WTERMSIG(status); // !!! signal is sometimes 50, 80 or 117 !!!
terminated = true;
LOGINF << "Process " << name() << " terminated by signal " << signal;
} else {
LOGWRN << "Process " << name() << " changed state but did not terminate. status=0x" <<
hex << status;
}
This is running under OSX 10.8.4, but I have also seen it in 10.9 GM seed.
EDIT Modifying the code as below makes the code more robust, however sometimes the child process gets orphaned as I guess the loop doesn't do enough to kill the child process.
else if (WIFSIGNALED(status)) {
signal = WTERMSIG(status);
if (signal < NSIG) {
terminated = true;
LOGINF << "Process " << name() << " terminated by signal " << signal;
} else {
LOGWRN << "Process " << name() << " produced unusual signal " << signal
<< "; assuming it's not terminated";
}
}
Note this code is part of the Process::unload() method of this class.
From the OS X manpage for waitpid, when specifing WNOHANG, you should check for a return of 0:
When the WNOHANG option is specified and no processes wish to report status, wait4() returns a process
id of 0.
The waitpid() call is identical to wait4() with an rusage value of zero. The older wait3() call is the
same as wait4() with a pid value of -1.
The code posted does not check for this, which suggests to me that the value of status is likely junk (the value of the int is never initialized). This could cause what you are seeing.
EDIT: status is indeed only set when waitpid returns > 0.

dup2( ) causing child process to terminate early

So I'm writing a program that involves the creation of 2 sets of pipes so that a parent process can write to a child process & the child process can right back...
I have the following code for my child process:
if(pid==0){ //child process
cout << "executing child" << endl;
close(fd1[WRITE_END]);
close(fd2[READ_END]);
if(dup2(fd1[READ_END],STDIN_FILENO) < 0 || dup2(fd2[WRITE_END],STDOUT_FILENO) < 0){
cerr << "dup2 failed" << endl;
exit(1);
}
cout << "test output" << endl;
close(fd2[WRITE_END]);
close(fd1[READ_END]);
read(fd1[READ_END],buf,BUFFER_SIZE);
cout << "Child process read " << buf << endl;
execl("/bin/sort", "sort", "-nr", NULL);
} else { //... parent process
When I run my program, all I get as output from the child process is executing child but no test output.
However, when I remove the if-statement handling the dup2 calls, my output does include test output.
Any ideas as to why dup2 causes my child process to not finish terminating?
(and by the way, originally, my two dup2's were done in separate if statements... when I put the test output below the dup2(fd1[READ_END],STDIN_FILENO) < 0 test, it outputs, but not when I put it below the other dup2 conditional test, so I'm convinced that that's where my issue is)
Thanks in advance
The call to dup2(fd2[WRITE_END],STDOUT_FILENO) connects STDOUT (which is used by C++ cout stream) to your fd2 pipe. So 'test output' gets written to the pipe.

Qt C++ and QSerialDevice: Windows 7 USB->Serial Port Reading/Writing

I am attempting to read from/write to an RS-232 capable device. This works without issue on Linux. The device is connected via a Digitus USB/Serial Adapter.
The device shows up in Device Manager as COM4.
void PayLife::run() {
this->sendingData = 0;
this->running = true;
qDebug() << "Starting PayLife Thread";
this->port = new AbstractSerial();
this->port->setDeviceName(this->addy);
QByteArray ba;
if (port->open(AbstractSerial::ReadWrite| AbstractSerial::Unbuffered)) {
if (!port->setBaudRate(AbstractSerial::BaudRate19200)) {
qDebug() << "Set baud rate " << AbstractSerial::BaudRate19200 << " error.";
goto end_thread;
};
if (!port->setDataBits(AbstractSerial::DataBits7)) {
qDebug() << "Set data bits " << AbstractSerial::DataBits7 << " error.";
goto end_thread;
}
if (!port->setParity(AbstractSerial::ParityEven)) {
qDebug() << "Set parity " << AbstractSerial::ParityEven << " error.";
goto end_thread;
}
if (!port->setStopBits(AbstractSerial::StopBits1)) {
qDebug() << "Set stop bits " << AbstractSerial::StopBits1 << " error.";
goto end_thread;
}
if (!port->setFlowControl(AbstractSerial::FlowControlOff)) {
qDebug() << "Set flow " << AbstractSerial::FlowControlOff << " error.";
goto end_thread;
}
while(this->running) {
if ((port->bytesAvailable() > 0) || port->waitForReadyRead(900)) {
ba.clear();
ba = port->read(1024);
qDebug() << "Readed is : " << ba.size() << " bytes";
}
else {
qDebug() << "Timeout read data in time : " << QTime::currentTime();
}
}
}
end_thread:
this->running = false;
}
On Linux, I don't use QSerialDevice, just regular serial reading/writing.
No matter what, I always get:
Starting PayLife Thread
Readed is : 0 bytes
Timeout read data in time : QTime("16:27:43")
Timeout read data in time : QTime("16:27:44")
Timeout read data in time : QTime("16:27:45")
Timeout read data in time : QTime("16:27:46")
I am not exactly sure why.
Note, I tried first to use regular Windows API reading and writing with the same results, i.e. it just doesn't ready any data from the device.
I am 100% sure that there is always something to read from the device, as it spams ENQ across the connection.
You should generate the doxygen documentation of QSerialDevice if you haven't already done so. The problem seems to be explained there.
On Windows in unbuffered mode:
Necessary to avoid the values of CharIntervalTimeout and
TotalReadConstantTimeout equal to 0. In theory, it was planned that at
zero values of timeouts method AbstractSerial::read() will read the
data which are in the buffer device driver (not to be confused with
the buffer AbstractSerial!) and return them immediately. But for
unknown reasons, this reading always returns 0, not depending on
whether or not a ready-made data in the buffer.
Because read waits for the data in unbuffered mode, I guess waitForReadyReady doesn't do anything useful in that mode.