QProcess give FailedToStart after starting multiple times - c++

I'm trying to use QProcess inside a thread to do some operations (read I2C connections).The update method is calling every 100 msec:
void TempsReader::update()
{
if (_currProcess == nullptr) {
_currProcess = new QProcess();
connect(_currProcess, &QProcess::errorOccurred, this, &TempsReader::onProcessError);
connect(_currProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(onProcessFinished()));
}
_currProcess->start("sh");
if (_currProcess->waitForStarted()) {
_currProcess->write("i2cdetect -y 1");
_currProcess->closeWriteChannel();
_currProcess->waitForFinished();
}
}
After some time, the process gives "FailedToStart" error and never starts again.
void TempsReader::onProcessError(QProcess::ProcessError error)
{
qDebug() << error;
_currProcess->close();
}
void TempsReader::onProcessFinished()
{
QString devs = _currProcess->readAll();
_currProcess->waitForFinished();
// doing some stuff with devs
_currProcess->close();
}
How can I fix this issue? Am I using QProcess in a wrong way? and how can I start the process again when it drops in error slot. Thanks in advance.
Update: QProcess::errorString() gives this: "Resource error (fork failure): Too many open files"
UPDATE: Finally I've found the issue and it was not related to QProcess itself. It was relating to I2C connection.

My guess is that you get the failure because all your update() calls share the same QProcess object.
What happens here is that when you call update(), you start the process. And 100ms later, you call it again without ensuring that the previous update() has finished to wait the end of the process.
The consequence is that you try to start an already started process and thus it fails.
For me, the easiest solution is to create one QProcess object for each update() call.
Something like:
void TempsReader::update()
{
QProcess * current_process = new QProcess;
connect(current_process, &QProcess::errorOccured, this, &TempReader::onProcessError);
connect(current_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &TempReader::onProcessFinished());
current_process->start("sh"); // Your command
current_process->waitForStarted();
current_process->write("i2cdetect -y 1");
current_process->waitForFinished();
current_process->deleteLater();
}
Or without pointers:
void TempsReader::update()
{
QProcess current_process;
connect(&current_process, &QProcess::errorOccured, this, &TempReader::onProcessError);
connect(&current_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &TempReader::onProcessFinished());
current_process.start("sh"); // Your command
current_process.waitForStarted();
current_process.write("i2cdetect -y 1");
current_process.waitForFinished();
}
As you did not show the calling part of the code (the thread creation, the 100ms loop, ...), this may not be the solution you need.
In this case, please let me know if it does not solve your issue so that I'll remove this answer.

Finally I've found the issue and it was not related to QProcess itself. It was relating to I2C connection. I was using this command in update: wiringPiI2CSetup(addr); and it opens a new device each time.

Related

Call a script in parallel in Qt C++

I am trying to run a script in parallel to my Qt program and am having trouble starting it as a separate process. Check out my attempts below and let me know what you see wrong.
The first attempt was just a system call:
system("python3 startprocess.py");
This works but it also stops the program while running it.
I then followed this guy https://forum.qt.io/topic/92205/writing-commands-to-linux-bash with no success. No errors, just no start of my script.
I am trying this after I saw the documentation and have the below code.
QProcess process;
process.start("python3 startprocess.py");
process.waitForStarted();
I am just wanting to start this script and have it run at the same time as my C++ code. Perhaps I am using the QProcess wrong?
UPDATE:
It was a lot easier to use QThreading and the original system call.
I think the issue is that the QProcess doesn't have the file path and fails to find and start it! I suggest first to use the full file path! Also check the QProcess::setWorkingDirectory and QProcess::setProcessEnvironment that are useful to handle this case!
Update
In order to prevent the QProcess to be killed while running and without freezing the GUI, you need to define it as a pointer, then connect the QProcess::finished event; in the slot, you can check the exit code and delete the sender using QObject::deleteLater method. Check both the Qt example and the QProcess::finished.
Update 2
Try this code:
auto process = new QProcess(this);
connect(process, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished),
[this](int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitStatus == QProcess::ExitStatus::CrashExit
|| exitCode != 0) {
// Process error!
} else {
// Process OK!
}
});
process->setWorkingDirectory("startprocess.py folder location");
process->start("python3 startprocess.py");
if (!process->waitForStarted(-1)) {
// Failed to start process
delete process;
}

Qt avoid warning QProcess: destroyed while process still running (Assistant)

I am running a Qt app that launches a process. (The Assistant, launched from the main app).
When I close the app, I get the warning
QProcess: Destroyed while process is still running.
How can I get rid of it ?
I saw this similar question and tried to kill... Nothing happened.
This question seems to say maybe I should add waitForFinished()... Help won't close when app does.
Help::Help():m_helpProcess(0) {}
Help::~Help()
{
if (m_helpProcess) {
m_helpProcess.waitForFinished(); // help stays open after app closes
m_helpProcess->kill(); // added with no effect
delete m_helpProcess;
}
}
bool Help::start()
{
if (!m_helpProcess)
process = new QProcess();
QStringList args;
args << QLatin1String("-collectionFile")
<< QLatin1String("mycollection.qhc");
process->start(QLatin1String("Assistant.app"), args);
if (!process->waitForStarted())
return;
}
It should be sufficient to rewrite the destructor using close():
Closes all communication with the process and kills it. After calling this function, QProcess will no longer emit readyRead(), and data can no longer be read or written.
Help::~Help()
{
if (m_helpProcess) {
// m_helpProcess->waitForFinished(); // help stays open after app closes
m_helpProcess->close(); // close channels
delete m_helpProcess; // destructor actually kills the process
}
}

Display QProcess output in another window

I'm using the QT Creator on Ubuntu.
I have GUI with a mainwindow and another window called "progress".
Upon clicking a button the QProcess starts and executes an rsync command which copies a folder into a specific directory. I created a textbrowser which reads the output from the rsync command. Also clicking the button causes the "progress" window to pop up.
So far so so good, now my problem.
Instead of showing the rsync output in my mainwindow i want it to be in progress.
I've tried several methods to get the QProcess into the progress via connect but that doesn't seem to work.
mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{
if (ui->checkBox->isChecked()
)
m_time ="-t";
QObject parent;
m_myProcess = new QProcess();
connect(m_myProcess, SIGNAL(readyReadStandardOutput()),this, SLOT(printOutput()));
QString program = "/usr/bin/rsync";
arguments << "-r" << m_time << "-v" <<"--progress" <<"-s"
<< m_dir
<< m_dir2;
m_myProcess->start(program, arguments);
}
progress.cpp
void Progress::printOutput()
{
ui->textBrowser->setPlainText(m_myProcess->readAllStandardOutput());
}
I know it's pretty messy iv'e tried alot of things and haven't cleaned the code yet also I'm pretty new to c++.
My goal was to send the QProcess (m_myProcess) to progress via connect but that didn't seem to work.
Can you send commands like readyReadAllStandardOutput via connect to other windows (I don't know the right term )?
Am I doing a mistake or is there just another way to get the output to my progress window ?
m_myProcess is a member of the class MainWindow and you haven't made it visible to the class Progress. That's why you have the compilation error
m_myProcess was not declared in this scope
What you could do:
Redirect standard error of m_myProcess to standard output, such that you also print what is sent to standard error (unless you want to do something else with it). Using
m_myProcess.setProcessChannelMode(QProcess::MergedChannels);
Make the process object available outside MainWindow
QProcess* MainWindow::getProcess()
{
return m_myProcess;
}
Read the process output line by line in Progress. It needs to be saved in a buffer because readAllStandardOutput() only return the data which has been written since the last read.
... // somewhere
connect(window->getProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput())
...
void Progress::printOutput(){
//bigbuffer is member
bigbuffer.append(myProcess->readAllStandardOutput();)
ui->textBrowser->setPlainText(bigbuffer);
}

QProcess unknown error

I got strange problem. QProcess just not working!
And error is unknown.
I got global var in header
QProcess *importModule;
An I got this function ( I tried both start and startDetached methods btw )
void App::openImport(){
importModule = new QProcess();
importModule->setWorkingDirectory(":\\Resources");
importModule->startDetached("importdb_module.exe");
QMessageBox::information(0,"",importModule->errorString());
}
It jsut outputs that error is unknown. Also it wont start other exes like
void App::openImport(){
importModule = new QProcess();
importModule->setWorkingDirectory("C:\\Program Files\\TortoiseHg");
importModule->startDetached("hg.exe");
QMessageBox::information(0,"",importModule->errorString());
}
What I've done wrong?
And is there other ways to run some .exe from my programm?
Or maybe .bat files(which runs exes)? (Tried with QProcess too, not working)
startDetached() is a static method and doesn't operate on importModule at all.
It starts a process and then stops caring. Thus the error()/errorState() in importModule has nothing to do with the startDetached() call. What you want is start().
However, as QProcess is asynchronous, nothing will have happened yet immediately after start() returns. You must connect to the started(), error() and finished() signals to learn about the result.
connect(importModule, SIGNAL(started()), this, SLOT(importModuleStarted()));
connect(importModule, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(importModuleFinished(int, QProcess::ExitStatus)));
CONNECT(importModule, SIGNAL(error(QProcess::ProcessError)), this, SLOT(importModuleError(QProcess::ProcessError)));
importModule->start(QStringLiteral("importdb_module"), QStringList());
Alternatively you can use the blocking wait functions:
importModule->start(QStringLiteral("importdb_module"), QStringList());
importModule->waitForStarted(); // waits until starting is completed
importModule->waitForFinished(); // waits until the process is finished
However, I strongly advise against using them in the main thread, as they block the UI then.

QProcess, Cannot Create Pipe

I am running a QProcess in a timer slot at 1 Hz. The process is designed to evoke a Linux command and parse it's output.
The problem is this: after the program runs for about 20 minutes, I get this error:
QProcessPrivate::createPipe: Cannot create pipe 0x104c0a8: Too many open files
QSocketNotifier: Invalid socket specified
Ideally, this program would run for the entire uptime of the system, which may be days or weeks.
I think I've been careful with process control by reading the examples, but maybe I missed something. I've used examples from the Qt website, and they use the same code that I've written, but those were designed for a single use, not thousands. Here is a minimum example:
class UsageStatistics : public QObject {
Q_OBJECT
public:
UsageStatistics() : process(new QProcess) {
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(getMemoryUsage()));
timer->start(1000); // one second
}
virtual ~UsageStatistics() {}
public slots:
void getMemoryUsage() {
process->start("/usr/bin/free");
if (!process->waitForFinished()) {
// error processing
}
QByteArray result = process->realAll();
// parse result
// edit, I added these
process->closeReadChannel(QProcess::StandardOutput);
process->closeReadChannel(QProcess::StandardError);
process->closeWriteChannel();
process->close();
}
}
I've also tried manually deleting the process pointer at the end of the function, and then new at the beginning. It was worth a try, I suppose.
Free beer for whoever answers this :)
QProcess is derived from QIODevice, so I would say calling close() should close the file handle and solve you problem.
I cannot see the issue, however one thing that concerns me is a possible invocation overlap in getMemoryUsage() where it's invoked before the previous run has finished.
How about restructuring this so that a new QProcess object is used within getMemoryUsage() (on the stack, not new'd), rather than being an instance variable of the top-level class? This would ensure clean-up (with the QProcess object going out-of-scope) and would avoid any possible invocation overlap.
Alternatively, rather than invoking /usr/bin/free as a process and parsing its output, why not read /proc/meminfo directly yourself? This will be much more efficient.
First I had the same situation with you. I got the same results.
I think that QProcess can not handle opened pipes correctly.
Then, instead of QProcess, I have decided to use popen() + QFile().
class UsageStatistics : public QObject {
Q_OBJECT
public:
UsageStatistics(){
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(getMemoryUsage()));
timer->start(1000); // one second
}
virtual ~UsageStatistics() {}
private:
QFile freePipe;
FILE *in;
public slots:
void getMemoryUsage() {
if(!(in = popen("/usr/bin/free", "r"))){
qDebug() << "UsageStatistics::getMemoryUsage() <<" << "Can not execute free command.";
return;
}
freePipe.open(in, QIODevice::ReadOnly);
connect(&freePipe, SIGNAL(readyRead()), this, SLOT(parseResult()) );
// OR waitForReadyRead() and parse here.
}
void parseResult(){
// Parse your stuff
freePipe.close();
pclose(in); // You can also use exit code by diving by 256.
}
}
tl;dr:
This occurs because your application wants to use more resources than allowed by the system-wide resource limitations. You might be able to solve it by using the command specified in [2] if you have a huge application, but it is probably caused by a programming error.
Long:
I just solved a similar problem myself. I use a QThread for the logging of exit codes of QProcesses. The QThread uses curl to connect to a FTP server uploads the log. Since I am testing the software I didn't connect the FTP server and curl_easy_perform apparently waits for a connection. As such, my resource limit was reached and I got this error. After a while my program starts complaining, which was the main indicator to figure out what was going wrong.
[..]
QProcessPrivate::createPipe: Cannot create pipe 0x7fbda8002f28: Too many open files
QProcessPrivate::createPipe: Cannot create pipe 0x7fbdb0003128: Too many open files
QProcessPrivate::createPipe: Cannot create pipe 0x7fbdb4003128: Too many open files
QProcessPrivate::createPipe: Cannot create pipe 0x7fbdb4003128: Too many open files
[...]
curl_easy_perform() failed for curl_easy_perform() failed for disk.log
[...]
I've tested this by connecting the machine to the FTP server after this error transpired. That solved my problem.
Read:
[1] https://linux.die.net/man/3/ulimit
[2] https://ss64.com/bash/ulimit.html
[3] https://bbs.archlinux.org/viewtopic.php?id=234915