Integrate QProcess into QIODevice's multistreaming infrastructure

As a result, this patch eliminates double-buffering in QProcess.

Change-Id: I436faa4a5ffc28ce77f959dd6089bef400ac39f6
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@theqtcompany.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
This commit is contained in:
Alex Trotsenko 2015-07-22 10:56:06 +03:00
parent 8f92baf5c9
commit 7ff655360f
7 changed files with 64 additions and 115 deletions

View File

@ -866,7 +866,8 @@ void QProcessPrivate::Channel::clear()
*/ */
QProcessPrivate::QProcessPrivate() QProcessPrivate::QProcessPrivate()
{ {
processChannel = QProcess::StandardOutput; readBufferChunkSize = QRINGBUFFER_CHUNKSIZE;
writeBufferChunkSize = QRINGBUFFER_CHUNKSIZE;
processChannelMode = QProcess::SeparateChannels; processChannelMode = QProcess::SeparateChannels;
inputChannelMode = QProcess::ManagedInputChannel; inputChannelMode = QProcess::ManagedInputChannel;
processError = QProcess::UnknownError; processError = QProcess::UnknownError;
@ -1017,10 +1018,15 @@ bool QProcessPrivate::tryReadFromChannel(Channel *channel)
if (available == 0) if (available == 0)
available = 1; // always try to read at least one byte available = 1; // always try to read at least one byte
char *ptr = channel->buffer.reserve(available); QProcess::ProcessChannel channelIdx = (channel == &stdoutChannel
? QProcess::StandardOutput
: QProcess::StandardError);
Q_ASSERT(readBuffers.size() > int(channelIdx));
QRingBuffer &readBuffer = readBuffers[int(channelIdx)];
char *ptr = readBuffer.reserve(available);
qint64 readBytes = readFromChannel(channel, ptr, available); qint64 readBytes = readFromChannel(channel, ptr, available);
if (readBytes <= 0) if (readBytes <= 0)
channel->buffer.chop(available); readBuffer.chop(available);
if (readBytes == -2) { if (readBytes == -2) {
// EWOULDBLOCK // EWOULDBLOCK
return false; return false;
@ -1048,18 +1054,17 @@ bool QProcessPrivate::tryReadFromChannel(Channel *channel)
#endif #endif
if (channel->closed) { if (channel->closed) {
channel->buffer.chop(readBytes); readBuffer.chop(readBytes);
return false; return false;
} }
channel->buffer.chop(available - readBytes); readBuffer.chop(available - readBytes);
bool didRead = false; bool didRead = false;
bool isStdout = channel == &stdoutChannel;
if (readBytes == 0) { if (readBytes == 0) {
if (channel->notifier) if (channel->notifier)
channel->notifier->setEnabled(false); channel->notifier->setEnabled(false);
} else if ((processChannel == QProcess::StandardOutput) == isStdout) { } else if (currentReadChannel == channelIdx) {
didRead = true; didRead = true;
if (!emittedReadyRead) { if (!emittedReadyRead) {
emittedReadyRead = true; emittedReadyRead = true;
@ -1067,7 +1072,8 @@ bool QProcessPrivate::tryReadFromChannel(Channel *channel)
emittedReadyRead = false; emittedReadyRead = false;
} }
} }
if (isStdout) emit q->channelReadyRead(int(channelIdx));
if (channelIdx == QProcess::StandardOutput)
emit q->readyReadStandardOutput(QProcess::QPrivateSignal()); emit q->readyReadStandardOutput(QProcess::QPrivateSignal());
else else
emit q->readyReadStandardError(QProcess::QPrivateSignal()); emit q->readyReadStandardError(QProcess::QPrivateSignal());
@ -1099,15 +1105,14 @@ bool QProcessPrivate::_q_canWrite()
if (stdinChannel.notifier) if (stdinChannel.notifier)
stdinChannel.notifier->setEnabled(false); stdinChannel.notifier->setEnabled(false);
if (stdinChannel.buffer.isEmpty()) { if (writeBuffer.isEmpty()) {
#if defined QPROCESS_DEBUG #if defined QPROCESS_DEBUG
qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer)."); qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer).");
#endif #endif
return false; return false;
} }
qint64 written = writeToStdin(stdinChannel.buffer.readPointer(), qint64 written = writeToStdin(writeBuffer.readPointer(), writeBuffer.nextDataBlockSize());
stdinChannel.buffer.nextDataBlockSize());
if (written < 0) { if (written < 0) {
closeChannel(&stdinChannel); closeChannel(&stdinChannel);
setErrorAndEmit(QProcess::WriteError); setErrorAndEmit(QProcess::WriteError);
@ -1119,16 +1124,17 @@ bool QProcessPrivate::_q_canWrite()
#endif #endif
if (written != 0) { if (written != 0) {
stdinChannel.buffer.free(written); writeBuffer.free(written);
if (!emittedBytesWritten) { if (!emittedBytesWritten) {
emittedBytesWritten = true; emittedBytesWritten = true;
emit q->bytesWritten(written); emit q->bytesWritten(written);
emittedBytesWritten = false; emittedBytesWritten = false;
} }
emit q->channelBytesWritten(0, written);
} }
if (stdinChannel.notifier && !stdinChannel.buffer.isEmpty()) if (stdinChannel.notifier && !writeBuffer.isEmpty())
stdinChannel.notifier->setEnabled(true); stdinChannel.notifier->setEnabled(true);
if (stdinChannel.buffer.isEmpty() && stdinChannel.closed) if (writeBuffer.isEmpty() && stdinChannel.closed)
closeWriteChannel(); closeWriteChannel();
return true; return true;
} }
@ -1387,7 +1393,7 @@ void QProcess::setInputChannelMode(InputChannelMode mode)
QProcess::ProcessChannel QProcess::readChannel() const QProcess::ProcessChannel QProcess::readChannel() const
{ {
Q_D(const QProcess); Q_D(const QProcess);
return d->processChannel; return ProcessChannel(d->currentReadChannel);
} }
/*! /*!
@ -1400,20 +1406,7 @@ QProcess::ProcessChannel QProcess::readChannel() const
*/ */
void QProcess::setReadChannel(ProcessChannel channel) void QProcess::setReadChannel(ProcessChannel channel)
{ {
Q_D(QProcess); QIODevice::setCurrentReadChannel(int(channel));
if (d->transactionStarted) {
qWarning("QProcess::setReadChannel: Failed due to the active read transaction");
return;
}
if (d->processChannel != channel) {
QRingBuffer *buffer = (d->processChannel == QProcess::StandardOutput)
? &d->stdoutChannel.buffer
: &d->stderrChannel.buffer;
d->buffer.read(buffer->reserveFront(d->buffer.size()), d->buffer.size());
}
d->processChannel = channel;
} }
/*! /*!
@ -1458,7 +1451,7 @@ void QProcess::closeWriteChannel()
{ {
Q_D(QProcess); Q_D(QProcess);
d->stdinChannel.closed = true; // closing d->stdinChannel.closed = true; // closing
if (d->stdinChannel.buffer.isEmpty()) if (d->writeBuffer.isEmpty())
d->closeWriteChannel(); d->closeWriteChannel();
} }
@ -1719,11 +1712,7 @@ qint64 QProcess::processId() const
*/ */
bool QProcess::canReadLine() const bool QProcess::canReadLine() const
{ {
Q_D(const QProcess); return QIODevice::canReadLine();
const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
? &d->stderrChannel.buffer
: &d->stdoutChannel.buffer;
return readBuffer->canReadLine() || QIODevice::canReadLine();
} }
/*! /*!
@ -1733,11 +1722,13 @@ bool QProcess::canReadLine() const
*/ */
void QProcess::close() void QProcess::close()
{ {
Q_D(QProcess);
emit aboutToClose(); emit aboutToClose();
while (waitForBytesWritten(-1)) while (waitForBytesWritten(-1))
; ;
kill(); kill();
waitForFinished(-1); waitForFinished(-1);
d->setWriteChannelCount(0);
QIODevice::close(); QIODevice::close();
} }
@ -1748,11 +1739,7 @@ void QProcess::close()
*/ */
bool QProcess::atEnd() const bool QProcess::atEnd() const
{ {
Q_D(const QProcess); return QIODevice::atEnd();
const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
? &d->stderrChannel.buffer
: &d->stdoutChannel.buffer;
return QIODevice::atEnd() && (!isOpen() || readBuffer->isEmpty());
} }
/*! \reimp /*! \reimp
@ -1766,25 +1753,16 @@ bool QProcess::isSequential() const
*/ */
qint64 QProcess::bytesAvailable() const qint64 QProcess::bytesAvailable() const
{ {
Q_D(const QProcess); return QIODevice::bytesAvailable();
const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
? &d->stderrChannel.buffer
: &d->stdoutChannel.buffer;
#if defined QPROCESS_DEBUG
qDebug("QProcess::bytesAvailable() == %i (%s)", readBuffer->size(),
(d->processChannel == QProcess::StandardError) ? "stderr" : "stdout");
#endif
return readBuffer->size() + QIODevice::bytesAvailable();
} }
/*! \reimp /*! \reimp
*/ */
qint64 QProcess::bytesToWrite() const qint64 QProcess::bytesToWrite() const
{ {
Q_D(const QProcess); qint64 size = QIODevice::bytesToWrite();
qint64 size = d->stdinChannel.buffer.size();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
size += d->pipeWriterBytesToWrite(); size += d_func()->pipeWriterBytesToWrite();
#endif #endif
return size; return size;
} }
@ -1923,9 +1901,9 @@ bool QProcess::waitForReadyRead(int msecs)
if (d->processState == QProcess::NotRunning) if (d->processState == QProcess::NotRunning)
return false; return false;
if (d->processChannel == QProcess::StandardOutput && d->stdoutChannel.closed) if (d->currentReadChannel == QProcess::StandardOutput && d->stdoutChannel.closed)
return false; return false;
if (d->processChannel == QProcess::StandardError && d->stderrChannel.closed) if (d->currentReadChannel == QProcess::StandardError && d->stderrChannel.closed)
return false; return false;
return d->waitForReadyRead(msecs); return d->waitForReadyRead(msecs);
} }
@ -2024,47 +2002,12 @@ void QProcess::setupChildProcess()
qint64 QProcess::readData(char *data, qint64 maxlen) qint64 QProcess::readData(char *data, qint64 maxlen)
{ {
Q_D(QProcess); Q_D(QProcess);
Q_UNUSED(data);
if (!maxlen) if (!maxlen)
return 0; return 0;
QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError) if (d->processState == QProcess::NotRunning)
? &d->stderrChannel.buffer
: &d->stdoutChannel.buffer;
if (maxlen == 1 && !readBuffer->isEmpty()) {
int c = readBuffer->getChar();
if (c == -1) {
#if defined QPROCESS_DEBUG
qDebug("QProcess::readData(%p \"%s\", %d) == -1",
data, qt_prettyDebug(data, 1, maxlen).constData(), 1);
#endif
return -1;
}
*data = (char) c;
#if defined QPROCESS_DEBUG
qDebug("QProcess::readData(%p \"%s\", %d) == 1",
data, qt_prettyDebug(data, 1, maxlen).constData(), 1);
#endif
return 1;
}
qint64 bytesToRead = qMin(readBuffer->size(), maxlen);
qint64 readSoFar = 0;
while (readSoFar < bytesToRead) {
const char *ptr = readBuffer->readPointer();
qint64 bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar,
readBuffer->nextDataBlockSize());
memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
readSoFar += bytesToReadFromThisBlock;
readBuffer->free(bytesToReadFromThisBlock);
}
#if defined QPROCESS_DEBUG
qDebug("QProcess::readData(%p \"%s\", %lld) == %lld",
data, qt_prettyDebug(data, readSoFar, 16).constData(), maxlen, readSoFar);
#endif
if (!readSoFar && d->processState == QProcess::NotRunning)
return -1; // EOF return -1; // EOF
return readSoFar; return 0;
} }
/*! \reimp /*! \reimp
@ -2098,7 +2041,7 @@ qint64 QProcess::writeData(const char *data, qint64 len)
#endif #endif
if (len == 1) { if (len == 1) {
d->stdinChannel.buffer.putChar(*data); d->writeBuffer.putChar(*data);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (!d->stdinWriteTrigger->isActive()) if (!d->stdinWriteTrigger->isActive())
d->stdinWriteTrigger->start(); d->stdinWriteTrigger->start();
@ -2113,7 +2056,7 @@ qint64 QProcess::writeData(const char *data, qint64 len)
return 1; return 1;
} }
char *dest = d->stdinChannel.buffer.reserve(len); char *dest = d->writeBuffer.reserve(len);
memcpy(dest, data, len); memcpy(dest, data, len);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (!d->stdinWriteTrigger->isActive()) if (!d->stdinWriteTrigger->isActive())
@ -2271,10 +2214,6 @@ void QProcessPrivate::start(QIODevice::OpenMode mode)
qDebug() << "QProcess::start(" << program << ',' << arguments << ',' << mode << ')'; qDebug() << "QProcess::start(" << program << ',' << arguments << ',' << mode << ')';
#endif #endif
stdinChannel.buffer.clear();
stdoutChannel.buffer.clear();
stderrChannel.buffer.clear();
if (stdinChannel.type != QProcessPrivate::Channel::Normal) if (stdinChannel.type != QProcessPrivate::Channel::Normal)
mode &= ~QIODevice::WriteOnly; // not open for writing mode &= ~QIODevice::WriteOnly; // not open for writing
if (stdoutChannel.type != QProcessPrivate::Channel::Normal && if (stdoutChannel.type != QProcessPrivate::Channel::Normal &&
@ -2295,6 +2234,9 @@ void QProcessPrivate::start(QIODevice::OpenMode mode)
q->QIODevice::open(mode); q->QIODevice::open(mode);
if (q->isReadable() && processChannelMode != QProcess::MergedChannels)
setReadChannelCount(2);
stdinChannel.closed = false; stdinChannel.closed = false;
stdoutChannel.closed = false; stdoutChannel.closed = false;
stderrChannel.closed = false; stderrChannel.closed = false;

View File

@ -235,12 +235,12 @@ public:
QProcess::ExitStatus exitStatus() const; QProcess::ExitStatus exitStatus() const;
// QIODevice // QIODevice
qint64 bytesAvailable() const Q_DECL_OVERRIDE; qint64 bytesAvailable() const Q_DECL_OVERRIDE; // ### Qt6: remove trivial override
qint64 bytesToWrite() const Q_DECL_OVERRIDE; qint64 bytesToWrite() const Q_DECL_OVERRIDE;
bool isSequential() const Q_DECL_OVERRIDE; bool isSequential() const Q_DECL_OVERRIDE;
bool canReadLine() const Q_DECL_OVERRIDE; bool canReadLine() const Q_DECL_OVERRIDE; // ### Qt6: remove trivial override
void close() Q_DECL_OVERRIDE; void close() Q_DECL_OVERRIDE;
bool atEnd() const Q_DECL_OVERRIDE; bool atEnd() const Q_DECL_OVERRIDE; // ### Qt6: remove trivial override
static int execute(const QString &program, const QStringList &arguments); static int execute(const QString &program, const QStringList &arguments);
static int execute(const QString &command); static int execute(const QString &command);

View File

@ -56,7 +56,6 @@
#include "QtCore/qstringlist.h" #include "QtCore/qstringlist.h"
#include "QtCore/qhash.h" #include "QtCore/qhash.h"
#include "QtCore/qshareddata.h" #include "QtCore/qshareddata.h"
#include "private/qringbuffer_p.h"
#include "private/qiodevice_p.h" #include "private/qiodevice_p.h"
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
#include <QtCore/private/qorderedmutexlocker_p.h> #include <QtCore/private/qorderedmutexlocker_p.h>
@ -293,7 +292,6 @@ public:
QWindowsPipeWriter *writer; QWindowsPipeWriter *writer;
}; };
#endif #endif
QRingBuffer buffer;
Q_PIPE pipe[2]; Q_PIPE pipe[2];
unsigned type : 2; unsigned type : 2;
@ -311,7 +309,6 @@ public:
bool _q_startupNotification(); bool _q_startupNotification();
bool _q_processDied(); bool _q_processDied();
QProcess::ProcessChannel processChannel;
QProcess::ProcessChannelMode processChannelMode; QProcess::ProcessChannelMode processChannelMode;
QProcess::InputChannelMode inputChannelMode; QProcess::InputChannelMode inputChannelMode;
QProcess::ProcessError processError; QProcess::ProcessError processError;

View File

@ -877,7 +877,7 @@ bool QProcessPrivate::waitForReadyRead(int msecs)
if (stderrChannel.pipe[0] != -1) if (stderrChannel.pipe[0] != -1)
add_fd(nfds, stderrChannel.pipe[0], &fdread); add_fd(nfds, stderrChannel.pipe[0], &fdread);
if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
add_fd(nfds, stdinChannel.pipe[1], &fdwrite); add_fd(nfds, stdinChannel.pipe[1], &fdwrite);
int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
@ -899,12 +899,12 @@ bool QProcessPrivate::waitForReadyRead(int msecs)
bool readyReadEmitted = false; bool readyReadEmitted = false;
if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) { if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) {
bool canRead = _q_canReadStandardOutput(); bool canRead = _q_canReadStandardOutput();
if (processChannel == QProcess::StandardOutput && canRead) if (currentReadChannel == QProcess::StandardOutput && canRead)
readyReadEmitted = true; readyReadEmitted = true;
} }
if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) { if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) {
bool canRead = _q_canReadStandardError(); bool canRead = _q_canReadStandardError();
if (processChannel == QProcess::StandardError && canRead) if (currentReadChannel == QProcess::StandardError && canRead)
readyReadEmitted = true; readyReadEmitted = true;
} }
if (readyReadEmitted) if (readyReadEmitted)
@ -930,7 +930,7 @@ bool QProcessPrivate::waitForBytesWritten(int msecs)
QElapsedTimer stopWatch; QElapsedTimer stopWatch;
stopWatch.start(); stopWatch.start();
while (!stdinChannel.buffer.isEmpty()) { while (!writeBuffer.isEmpty()) {
fd_set fdread; fd_set fdread;
fd_set fdwrite; fd_set fdwrite;
@ -949,7 +949,7 @@ bool QProcessPrivate::waitForBytesWritten(int msecs)
add_fd(nfds, stderrChannel.pipe[0], &fdread); add_fd(nfds, stderrChannel.pipe[0], &fdread);
if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
add_fd(nfds, stdinChannel.pipe[1], &fdwrite); add_fd(nfds, stdinChannel.pipe[1], &fdwrite);
int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
@ -1015,7 +1015,7 @@ bool QProcessPrivate::waitForFinished(int msecs)
if (processState == QProcess::Running && forkfd != -1) if (processState == QProcess::Running && forkfd != -1)
add_fd(nfds, forkfd, &fdread); add_fd(nfds, forkfd, &fdread);
if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
add_fd(nfds, stdinChannel.pipe[1], &fdwrite); add_fd(nfds, stdinChannel.pipe[1], &fdwrite);
int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed());

View File

@ -652,7 +652,7 @@ bool QProcessPrivate::waitForReadyRead(int msecs)
QIncrementalSleepTimer timer(msecs); QIncrementalSleepTimer timer(msecs);
forever { forever {
if (!stdinChannel.buffer.isEmpty() && !_q_canWrite()) if (!writeBuffer.isEmpty() && !_q_canWrite())
return false; return false;
if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0)) if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
timer.resetIncrements(); timer.resetIncrements();
@ -690,7 +690,7 @@ bool QProcessPrivate::waitForBytesWritten(int msecs)
// If we don't have pending data, and our write buffer is // If we don't have pending data, and our write buffer is
// empty, we fail. // empty, we fail.
if (!pendingDataInPipe && stdinChannel.buffer.isEmpty()) if (!pendingDataInPipe && writeBuffer.isEmpty())
return false; return false;
// If we don't have pending data and we do have data in our // If we don't have pending data and we do have data in our
@ -754,7 +754,7 @@ bool QProcessPrivate::waitForFinished(int msecs)
QIncrementalSleepTimer timer(msecs); QIncrementalSleepTimer timer(msecs);
forever { forever {
if (!stdinChannel.buffer.isEmpty() && !_q_canWrite()) if (!writeBuffer.isEmpty() && !_q_canWrite())
return false; return false;
if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0)) if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
timer.resetIncrements(); timer.resetIncrements();

View File

@ -56,10 +56,14 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#ifndef QRINGBUFFER_CHUNKSIZE
#define QRINGBUFFER_CHUNKSIZE 4096
#endif
class QRingBuffer class QRingBuffer
{ {
public: public:
explicit inline QRingBuffer(int growth = 4096) : explicit inline QRingBuffer(int growth = QRINGBUFFER_CHUNKSIZE) :
head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { } head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { }
inline qint64 nextDataBlockSize() const { inline qint64 nextDataBlockSize() const {

View File

@ -493,9 +493,11 @@ void tst_QProcess::echoTest2()
QCOMPARE(process.error(), QProcess::Timedout); QCOMPARE(process.error(), QProcess::Timedout);
process.write("Hello"); process.write("Hello");
QSignalSpy spy0(&process, &QProcess::channelReadyRead);
QSignalSpy spy1(&process, &QProcess::readyReadStandardOutput); QSignalSpy spy1(&process, &QProcess::readyReadStandardOutput);
QSignalSpy spy2(&process, &QProcess::readyReadStandardError); QSignalSpy spy2(&process, &QProcess::readyReadStandardError);
QVERIFY(spy0.isValid());
QVERIFY(spy1.isValid()); QVERIFY(spy1.isValid());
QVERIFY(spy2.isValid()); QVERIFY(spy2.isValid());
@ -514,6 +516,7 @@ void tst_QProcess::echoTest2()
break; break;
} }
QVERIFY(spy0.count() > 0);
QVERIFY(spy1.count() > 0); QVERIFY(spy1.count() > 0);
QVERIFY(spy2.count() > 0); QVERIFY(spy2.count() > 0);
@ -985,6 +988,9 @@ public:
this, &SoftExitProcess::terminateSlot); this, &SoftExitProcess::terminateSlot);
break; break;
case 4: case 4:
setReadChannelMode(QProcess::MergedChannels);
connect(this, SIGNAL(channelReadyRead(int)), this, SLOT(terminateSlot()));
break;
default: default:
connect(this, &QProcess::stateChanged, connect(this, &QProcess::stateChanged,
this, &SoftExitProcess::terminateSlot); this, &SoftExitProcess::terminateSlot);
@ -1057,7 +1063,7 @@ void tst_QProcess::softExitInSlots()
{ {
QFETCH(QString, appName); QFETCH(QString, appName);
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 6; ++i) {
SoftExitProcess proc(i); SoftExitProcess proc(i);
proc.writeAfterStart("OLEBOLE", 8); // include the \0 proc.writeAfterStart("OLEBOLE", 8); // include the \0
proc.start(appName); proc.start(appName);