forked from qt-creator/qt-creator
Make process reusable when killing the running one
Try to mimic the behavior of QProcess inside process launcher as much as possible. After calling QtcProcess::kill() we send StopProcessPacket to the process launcher - when we receive it there we start reaping of the process itself and report back the finished packet with a failure. Remove canceled state from the LanucherHandle, as after canceling the process we still need to handle the finished (or others) signals - like in case of QProcess. Change-Id: Id66b06449de06675a0ab6778e93e9782afea09d4 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -120,8 +120,6 @@ void LauncherHandle::handlePacket(LauncherPacketType type, const QByteArray &pay
|
|||||||
void LauncherHandle::handleErrorPacket(const QByteArray &packetData)
|
void LauncherHandle::handleErrorPacket(const QByteArray &packetData)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
if (!m_canceled)
|
|
||||||
m_processState = QProcess::NotRunning;
|
|
||||||
if (m_waitingFor != SignalType::NoSignal) {
|
if (m_waitingFor != SignalType::NoSignal) {
|
||||||
m_waitCondition.wakeOne();
|
m_waitCondition.wakeOne();
|
||||||
m_waitingFor = SignalType::NoSignal;
|
m_waitingFor = SignalType::NoSignal;
|
||||||
@@ -166,8 +164,6 @@ void LauncherHandle::handleStartedPacket(const QByteArray &packetData)
|
|||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
wakeUpIfWaitingFor(SignalType::Started);
|
wakeUpIfWaitingFor(SignalType::Started);
|
||||||
if (m_canceled)
|
|
||||||
return;
|
|
||||||
m_processState = QProcess::Running;
|
m_processState = QProcess::Running;
|
||||||
const auto packet = LauncherPacket::extractPacket<ProcessStartedPacket>(m_token, packetData);
|
const auto packet = LauncherPacket::extractPacket<ProcessStartedPacket>(m_token, packetData);
|
||||||
m_processId = packet.processId;
|
m_processId = packet.processId;
|
||||||
@@ -182,8 +178,6 @@ void LauncherHandle::handleReadyReadStandardOutput(const QByteArray &packetData)
|
|||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
wakeUpIfWaitingFor(SignalType::ReadyRead);
|
wakeUpIfWaitingFor(SignalType::ReadyRead);
|
||||||
if (m_canceled)
|
|
||||||
return;
|
|
||||||
const auto packet = LauncherPacket::extractPacket<ReadyReadStandardOutputPacket>(m_token, packetData);
|
const auto packet = LauncherPacket::extractPacket<ReadyReadStandardOutputPacket>(m_token, packetData);
|
||||||
if (packet.standardChannel.isEmpty())
|
if (packet.standardChannel.isEmpty())
|
||||||
return;
|
return;
|
||||||
@@ -200,8 +194,6 @@ void LauncherHandle::handleReadyReadStandardError(const QByteArray &packetData)
|
|||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
wakeUpIfWaitingFor(SignalType::ReadyRead);
|
wakeUpIfWaitingFor(SignalType::ReadyRead);
|
||||||
if (m_canceled)
|
|
||||||
return;
|
|
||||||
const auto packet = LauncherPacket::extractPacket<ReadyReadStandardErrorPacket>(m_token, packetData);
|
const auto packet = LauncherPacket::extractPacket<ReadyReadStandardErrorPacket>(m_token, packetData);
|
||||||
if (packet.standardChannel.isEmpty())
|
if (packet.standardChannel.isEmpty())
|
||||||
return;
|
return;
|
||||||
@@ -218,8 +210,6 @@ void LauncherHandle::handleFinishedPacket(const QByteArray &packetData)
|
|||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
wakeUpIfWaitingFor(SignalType::Finished);
|
wakeUpIfWaitingFor(SignalType::Finished);
|
||||||
if (m_canceled)
|
|
||||||
return;
|
|
||||||
m_processState = QProcess::NotRunning;
|
m_processState = QProcess::NotRunning;
|
||||||
const auto packet = LauncherPacket::extractPacket<ProcessFinishedPacket>(m_token, packetData);
|
const auto packet = LauncherPacket::extractPacket<ProcessFinishedPacket>(m_token, packetData);
|
||||||
m_exitCode = packet.exitCode;
|
m_exitCode = packet.exitCode;
|
||||||
@@ -268,9 +258,6 @@ bool LauncherHandle::doWaitForSignal(int msecs, SignalType newSignal)
|
|||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
QTC_ASSERT(m_waitingFor == SignalType::NoSignal, return false);
|
QTC_ASSERT(m_waitingFor == SignalType::NoSignal, return false);
|
||||||
if (m_canceled) // we don't want to wait if we have canceled it before (ASSERT it?)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// It may happen, that after calling start() and before calling waitForStarted() we might have
|
// It may happen, that after calling start() and before calling waitForStarted() we might have
|
||||||
// reached the Running (or even Finished) state already. In this case we should have
|
// reached the Running (or even Finished) state already. In this case we should have
|
||||||
// collected Started (or even Finished) signal to be flushed - so we return true
|
// collected Started (or even Finished) signal to be flushed - so we return true
|
||||||
@@ -325,7 +312,6 @@ void LauncherHandle::cancel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_processState = QProcess::NotRunning;
|
m_processState = QProcess::NotRunning;
|
||||||
m_canceled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LauncherHandle::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode)
|
void LauncherHandle::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode)
|
||||||
|
@@ -72,7 +72,6 @@ public:
|
|||||||
QProcess::ProcessState state() const
|
QProcess::ProcessState state() const
|
||||||
{ QMutexLocker locker(&m_mutex); return m_processState; }
|
{ QMutexLocker locker(&m_mutex); return m_processState; }
|
||||||
void cancel();
|
void cancel();
|
||||||
bool isCanceled() const { return m_canceled; }
|
|
||||||
|
|
||||||
QByteArray readAllStandardOutput()
|
QByteArray readAllStandardOutput()
|
||||||
{ QMutexLocker locker(&m_mutex); return readAndClear(m_stdout); }
|
{ QMutexLocker locker(&m_mutex); return readAndClear(m_stdout); }
|
||||||
@@ -163,7 +162,6 @@ private:
|
|||||||
SignalType m_waitingFor = SignalType::NoSignal;
|
SignalType m_waitingFor = SignalType::NoSignal;
|
||||||
|
|
||||||
QProcess::ProcessState m_processState = QProcess::NotRunning;
|
QProcess::ProcessState m_processState = QProcess::NotRunning;
|
||||||
std::atomic_bool m_canceled = false;
|
|
||||||
std::atomic_bool m_failed = false;
|
std::atomic_bool m_failed = false;
|
||||||
int m_processId = 0;
|
int m_processId = 0;
|
||||||
int m_exitCode = 0;
|
int m_exitCode = 0;
|
||||||
|
@@ -48,6 +48,10 @@ public:
|
|||||||
|
|
||||||
void cancel()
|
void cancel()
|
||||||
{
|
{
|
||||||
|
if (state() == QProcess::NotRunning) {
|
||||||
|
deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (m_stopState) {
|
switch (m_stopState) {
|
||||||
case StopState::Inactive:
|
case StopState::Inactive:
|
||||||
m_stopState = StopState::Terminating;
|
m_stopState = StopState::Terminating;
|
||||||
@@ -61,7 +65,7 @@ public:
|
|||||||
break;
|
break;
|
||||||
case StopState::Killing:
|
case StopState::Killing:
|
||||||
m_stopState = StopState::Inactive;
|
m_stopState = StopState::Inactive;
|
||||||
emit failedToStop();
|
deleteLater(); // TODO: employ something like Core::Reaper here
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,9 +78,6 @@ public:
|
|||||||
|
|
||||||
quintptr token() const { return m_token; }
|
quintptr token() const { return m_token; }
|
||||||
|
|
||||||
signals:
|
|
||||||
void failedToStop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const quintptr m_token;
|
const quintptr m_token;
|
||||||
QTimer * const m_stopTimer;
|
QTimer * const m_stopTimer;
|
||||||
@@ -217,20 +218,6 @@ void LauncherSocketHandler::handleProcessFinished()
|
|||||||
removeProcess(proc->token());
|
removeProcess(proc->token());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LauncherSocketHandler::handleStopFailure()
|
|
||||||
{
|
|
||||||
// Process did not react to a kill signal. Rare, but not unheard of.
|
|
||||||
// Forget about the associated Process object and report process exit to the client.
|
|
||||||
Process * proc = senderProcess();
|
|
||||||
ProcessFinishedPacket packet(proc->token());
|
|
||||||
packet.error = QProcess::Crashed;
|
|
||||||
packet.exitCode = -1;
|
|
||||||
packet.exitStatus = QProcess::CrashExit;
|
|
||||||
packet.stdErr = proc->readAllStandardError();
|
|
||||||
packet.stdOut = proc->readAllStandardOutput();
|
|
||||||
sendPacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LauncherSocketHandler::handleStartPacket()
|
void LauncherSocketHandler::handleStartPacket()
|
||||||
{
|
{
|
||||||
Process *& process = m_processes[m_packetParser.token()];
|
Process *& process = m_processes[m_packetParser.token()];
|
||||||
@@ -262,6 +249,16 @@ void LauncherSocketHandler::handleStopPacket()
|
|||||||
// This can happen if the process finishes on its own at about the same time the client
|
// This can happen if the process finishes on its own at about the same time the client
|
||||||
// sends the request.
|
// sends the request.
|
||||||
logDebug("got stop request when process was not running");
|
logDebug("got stop request when process was not running");
|
||||||
|
} else {
|
||||||
|
// We got the client request to stop the starting / running process.
|
||||||
|
// We report process exit to the client.
|
||||||
|
ProcessFinishedPacket packet(process->token());
|
||||||
|
packet.error = QProcess::Crashed;
|
||||||
|
packet.exitCode = -1;
|
||||||
|
packet.exitStatus = QProcess::CrashExit;
|
||||||
|
packet.stdErr = process->readAllStandardError();
|
||||||
|
packet.stdOut = process->readAllStandardOutput();
|
||||||
|
sendPacket(packet);
|
||||||
}
|
}
|
||||||
removeProcess(process->token());
|
removeProcess(process->token());
|
||||||
}
|
}
|
||||||
@@ -296,7 +293,6 @@ Process *LauncherSocketHandler::setupProcess(quintptr token)
|
|||||||
this, &LauncherSocketHandler::handleReadyReadStandardError);
|
this, &LauncherSocketHandler::handleReadyReadStandardError);
|
||||||
connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||||
this, &LauncherSocketHandler::handleProcessFinished);
|
this, &LauncherSocketHandler::handleProcessFinished);
|
||||||
connect(p, &Process::failedToStop, this, &LauncherSocketHandler::handleStopFailure);
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -57,7 +57,6 @@ private:
|
|||||||
void handleReadyReadStandardOutput();
|
void handleReadyReadStandardOutput();
|
||||||
void handleReadyReadStandardError();
|
void handleReadyReadStandardError();
|
||||||
void handleProcessFinished();
|
void handleProcessFinished();
|
||||||
void handleStopFailure();
|
|
||||||
|
|
||||||
void handleStartPacket();
|
void handleStartPacket();
|
||||||
void handleStopPacket();
|
void handleStopPacket();
|
||||||
|
Reference in New Issue
Block a user