QProcess becomes non-responsive with negative return value from Windows console app
-
I have the following situation.
QProcess (either stack or heap) with appropriate finish and error connections made, call start on a Windows console application. If the application returns a non-negative value, everything is okay.
If the console application returns a negative value, my spawning application crashes in QProcessPrivate::cleanup() at CloseHandle(pid->hThread)
Is this a bug in QProcess, or an misunderstanding of something regarding return values?
Thanks.
-
Yes, here we go:
Excerpted from class header:
@
public slots:
// methods
void RunNextWavelength(int exitCode, QProcess::ExitStatus exitStatus);
void ReconProcessError(QProcess::ProcessError error);private:
// interpret exitStatus for user display
QString UserMessageFromExitStatus(int status);// QProcess to run recon QProcess m_recon_proc;
@
From ctor/dtor:
@
CNexusReconJob::CNexusReconJob():
/* ... initialization list ... */
{
connect(&m_recon_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(RunNextWavelength(int, QProcess::ExitStatus)));
connect(&m_recon_proc, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(ReconProcessError(QProcess::ProcessError)));
connect(this, SIGNAL(KillRunningJob()),
&m_recon_proc, SLOT(kill()));
}CNexusReconJob::~CNexusReconJob()
{
disconnect(&m_recon_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(RunNextWavelength(int, QProcess::ExitStatus)));
disconnect(&m_recon_proc, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(ReconProcessError(QProcess::ProcessError)));
disconnect(this, SIGNAL(KillRunningJob()),
&m_recon_proc, SLOT(kill()));
}
@Excerpted from relevant functions:
@
// Check the return code of the previous job, if it came back okay, then we do the next wavelength
// because the index starts at 0, we increment after the job is started, and then check again
// the next time we enter this method - if we are outside the length of the list, then we are done
void CNexusReconJob::RunNextWavelength(int exitCode, QProcess::ExitStatus exitStatus)
{
if (((exitCode == 0) || (exitCode == NOISE_FALLBACK)) && (exitStatus == QProcess::NormalExit) && (current_wavelength < wavelengths.length()))
{
// state is always one behind the task we want to run/* ... remove some non-relevant states ... */
switch (ReconState)
{
case RunPrepocess:
syslog(LOG_DEBUG, "RunPrepro finished okay");
ReconState = CleanupPreprocess;
CleanupPrepro();
break;
case RunReconstruction:
syslog(LOG_DEBUG, "RunRecon finished okay");
if (exitCode == NOISE_FALLBACK)
{
QString user_msg = UserMessageFromExitStatus(exitCode);
emit ReconError(QString("Recon Warning"), QString("Recon job %1, %2, %3 %4.").arg(m_study, m_specimen, m_exam, user_msg));
}
ReconState = CleanupReconstruction;
CleanupRecon();
break;/* ... remove more non-relevant states ... */
}; } else if (((exitCode != 0) && (exitCode != NOISE_FALLBACK)) || (exitStatus == QProcess::CrashExit)) { if (!ignore_errors) { QString user_msg = UserMessageFromExitStatus(exitCode); emit ReconError(QString("Recon Error"), QString("Recon job %1, %2, %3 %4.").arg(m_study, m_specimen, m_exam, user_msg)); } emit RunningJobFinished(false); } else {
/* ... */
}
}// just to show how QProcess is configured and started
void CNexusReconJob::RunPrepro()
{
/* ... /
m_recon_proc.setNativeArguments(arg);
m_recon_proc.start(cmd);
/ ... */
}void CNexusReconJob::RunRecon()
{
/* ... /
m_recon_proc.setNativeArguments(arg);
m_recon_proc.start(cmd);
/ ... */
}void CNexusReconJob::ReconProcessError(QProcess::ProcessError error)
{
syslog(LOG_DEBUG, "Error reported: %d", error);
if (!ignore_errors)
{
QString user_msg = UserMessageFromExitStatus(m_recon_proc.exitCode());
emit ReconError(QString("Recon Error"), QString("Recon job %1, %2, %3 %4.").arg(m_study, m_specimen, m_exam, user_msg));
}
emit RunningJobFinished(false);
}QString CNexusReconJob::UserMessageFromExitStatus(int status)
{
QString ret;
switch (status)
{
case FILE_READING_ERROR:
ret = QString("failed due to an error reading a required input file");
break;/* .... several other options ... */
}; return ret;
}
@If m_recon_proc (a C console app) calls exit with a negative return value to be mapped to a user message, it goes through ReconProcessError, emits and displays the message on the GUI, and then crashes deleting the object of this class after logging QProcess: Destroyed while process still running ...
If it calls exit with a non-negative value, everything works just fine.
-
Sounds like the process is still running when the QProcess object is destroyed. Maybe your logic somehow starts a new process when the previous one exited with negative code?
Anyway, as "m_recon_proc" is an auto object, it will be destroyed when your class is destroyed. Therefore I would suggest you put something like this into your destructor, just to be sure:
@if(m_recon_pro.state() != QProcess::NotRunning)
{
if(!QProcess::waitForFinished(10000))
{
m_recon_pro.kill();
m_recon_pro.state.waitForFinished(-1);
}
}@ -
I added this code to the dtor:
@
syslog(LOG_DEBUG, "m_recon_proc.state(): %d", m_recon_proc.state());
if (m_recon_proc.state() != QProcess::NotRunning)
{
syslog(LOG_DEBUG, "wait for finish");
if (!m_recon_proc.waitForFinished(10000))
{
syslog(LOG_DEBUG, "killing proc");
m_recon_proc.kill();
m_recon_proc.waitForFinished(-1);
}
}
else
{
syslog(LOG_DEBUG, "apparently we're not running, but we are ??");
}syslog(LOG_DEBUG, "m_recon_proc.state(): %d", m_recon_proc.state());
@
The syslog output shows that the state is 2, or QProcess::Running.
It logs "wait for finish", but doesn't wait 10 seconds, it returns immediately and so doesn't call the kill(), logs that the state is still QProcess::Running, and proceeds with the rest of the dtor, and then crashes. -
That doesn't really make sense to me! If QProcess reports the state "Running" and then you call waitForFinished(), it won't return until either the process has terminated or the timeout has been triggered. You can check from the return value of waitForFinished() whether the timeout was triggered or it has terminated. And in case waitForFinished() returned with "true", the state has to be "NoRunning" afterwards.
Anything else would indicate a bug, I think. What version of Qt you are using?
--
You could still try:
@while(m_recon_proc.state() != QProcess::NotRunning)
{
m_recon_proc.kill();
m_recon_proc.waitForFinished(-1);
}@If that ends up in an infinite loop, something is definitely seriously wrong...