Add asserts ensuring that methods are called from right threads

Change-Id: I4f6d8dc706f89c3fb043655d775d02f878c546d6
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2021-08-18 13:40:43 +02:00
parent 09ae643153
commit bd5b6b60c9
2 changed files with 239 additions and 49 deletions

View File

@@ -45,6 +45,7 @@ public:
// Called from caller's thread exclusively. Returns the list of flushed signals. // Called from caller's thread exclusively. Returns the list of flushed signals.
QList<LauncherHandle::SignalType> flush() QList<LauncherHandle::SignalType> flush()
{ {
QTC_ASSERT(isCalledFromCallersThread(), return {});
QList<LauncherHandle::SignalType> oldSignals; QList<LauncherHandle::SignalType> oldSignals;
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
@@ -74,6 +75,7 @@ public:
// Called from caller's thread exclusively. // Called from caller's thread exclusively.
bool shouldFlushFor(LauncherHandle::SignalType signalType) bool shouldFlushFor(LauncherHandle::SignalType signalType)
{ {
QTC_ASSERT(isCalledFromCallersThread(), return false);
// TODO: Should we always flush when the list isn't empty? // TODO: Should we always flush when the list isn't empty?
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
if (m_signals.contains(signalType)) if (m_signals.contains(signalType))
@@ -87,6 +89,7 @@ public:
// Called from launcher's thread exclusively. // Called from launcher's thread exclusively.
void appendSignal(LauncherHandle::SignalType signalType) void appendSignal(LauncherHandle::SignalType signalType)
{ {
QTC_ASSERT(!isCalledFromCallersThread(), return);
if (signalType == LauncherHandle::SignalType::NoSignal) if (signalType == LauncherHandle::SignalType::NoSignal)
return; return;
@@ -103,12 +106,19 @@ signals:
void readyRead(); void readyRead();
void finished(); void finished();
private: private:
// Called from caller's or launcher's thread.
bool isCalledFromCallersThread() const
{
return QThread::currentThread() == thread();
}
QMutex m_mutex; QMutex m_mutex;
QList<LauncherHandle::SignalType> m_signals; QList<LauncherHandle::SignalType> m_signals;
}; };
void LauncherHandle::handlePacket(LauncherPacketType type, const QByteArray &payload) void LauncherHandle::handlePacket(LauncherPacketType type, const QByteArray &payload)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
switch (type) { switch (type) {
case LauncherPacketType::ProcessError: case LauncherPacketType::ProcessError:
handleErrorPacket(payload); handleErrorPacket(payload);
@@ -132,6 +142,7 @@ void LauncherHandle::handlePacket(LauncherPacketType type, const QByteArray &pay
void LauncherHandle::handleErrorPacket(const QByteArray &packetData) void LauncherHandle::handleErrorPacket(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(SignalType::Error); wakeUpIfWaitingFor(SignalType::Error);
@@ -148,6 +159,7 @@ void LauncherHandle::handleErrorPacket(const QByteArray &packetData)
// call me with mutex locked // call me with mutex locked
void LauncherHandle::wakeUpIfWaitingFor(SignalType newSignal) void LauncherHandle::wakeUpIfWaitingFor(SignalType newSignal)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
// TODO: should we always wake up in case m_waitingFor != NoSignal? // TODO: should we always wake up in case m_waitingFor != NoSignal?
// The matching signal came // The matching signal came
const bool signalMatched = (m_waitingFor == newSignal); const bool signalMatched = (m_waitingFor == newSignal);
@@ -173,9 +185,23 @@ void LauncherHandle::sendPacket(const Internal::LauncherPacket &packet)
LauncherInterface::socket()->sendData(packet.serialize()); LauncherInterface::socket()->sendData(packet.serialize());
} }
bool LauncherHandle::isCalledFromLaunchersThread() const
{
return QThread::currentThread() == thread();
}
// call me with mutex locked
bool LauncherHandle::isCalledFromCallersThread() const
{
if (!m_callerHandle)
return false;
return QThread::currentThread() == m_callerHandle->thread();
}
// call me with mutex locked // call me with mutex locked
void LauncherHandle::flushCaller() void LauncherHandle::flushCaller()
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
if (!m_callerHandle) if (!m_callerHandle)
return; return;
@@ -185,6 +211,7 @@ void LauncherHandle::flushCaller()
void LauncherHandle::handleStartedPacket(const QByteArray &packetData) void LauncherHandle::handleStartedPacket(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(SignalType::Started); wakeUpIfWaitingFor(SignalType::Started);
m_processState = QProcess::Running; m_processState = QProcess::Running;
@@ -199,6 +226,7 @@ void LauncherHandle::handleStartedPacket(const QByteArray &packetData)
void LauncherHandle::handleReadyReadStandardOutput(const QByteArray &packetData) void LauncherHandle::handleReadyReadStandardOutput(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(SignalType::ReadyRead); wakeUpIfWaitingFor(SignalType::ReadyRead);
const auto packet = LauncherPacket::extractPacket<ReadyReadStandardOutputPacket>(m_token, packetData); const auto packet = LauncherPacket::extractPacket<ReadyReadStandardOutputPacket>(m_token, packetData);
@@ -215,6 +243,7 @@ void LauncherHandle::handleReadyReadStandardOutput(const QByteArray &packetData)
void LauncherHandle::handleReadyReadStandardError(const QByteArray &packetData) void LauncherHandle::handleReadyReadStandardError(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(SignalType::ReadyRead); wakeUpIfWaitingFor(SignalType::ReadyRead);
const auto packet = LauncherPacket::extractPacket<ReadyReadStandardErrorPacket>(m_token, packetData); const auto packet = LauncherPacket::extractPacket<ReadyReadStandardErrorPacket>(m_token, packetData);
@@ -231,6 +260,7 @@ void LauncherHandle::handleReadyReadStandardError(const QByteArray &packetData)
void LauncherHandle::handleFinishedPacket(const QByteArray &packetData) void LauncherHandle::handleFinishedPacket(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(SignalType::Finished); wakeUpIfWaitingFor(SignalType::Finished);
m_processState = QProcess::NotRunning; m_processState = QProcess::NotRunning;
@@ -251,6 +281,7 @@ void LauncherHandle::handleFinishedPacket(const QByteArray &packetData)
void LauncherHandle::handleSocketReady() void LauncherHandle::handleSocketReady()
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_socketError = false; m_socketError = false;
if (m_processState == QProcess::Starting) if (m_processState == QProcess::Starting)
@@ -259,6 +290,7 @@ void LauncherHandle::handleSocketReady()
void LauncherHandle::handleSocketError(const QString &message) void LauncherHandle::handleSocketError(const QString &message)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_socketError = true; m_socketError = true;
m_errorString = QCoreApplication::translate("Utils::QtcProcess", m_errorString = QCoreApplication::translate("Utils::QtcProcess",
@@ -298,9 +330,16 @@ bool LauncherHandle::waitForSignal(int msecs, SignalType newSignal)
return false; return false;
} }
static void warnAboutWrongSignal(QProcess::ProcessState state, LauncherHandle::SignalType newSignal)
{
qWarning() << "LauncherHandle::doWaitForSignal: Can't wait for" << newSignal <<
"while being in" << state << "state.";
}
bool LauncherHandle::doWaitForSignal(int msecs, SignalType newSignal) bool LauncherHandle::doWaitForSignal(int msecs, SignalType newSignal)
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return false);
QTC_ASSERT(m_waitingFor == SignalType::NoSignal, return false); QTC_ASSERT(m_waitingFor == SignalType::NoSignal, 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
@@ -317,12 +356,15 @@ bool LauncherHandle::doWaitForSignal(int msecs, SignalType newSignal)
m_waitingFor = SignalType::NoSignal; m_waitingFor = SignalType::NoSignal;
return ret; return ret;
} }
// Can't wait for passed signal, should never happen.
QTC_ASSERT(false, warnAboutWrongSignal(m_processState, newSignal));
return false; return false;
} }
// call me with mutex locked // call me with mutex locked
bool LauncherHandle::canWaitFor(SignalType newSignal) const bool LauncherHandle::canWaitFor(SignalType newSignal) const
{ {
QTC_ASSERT(isCalledFromCallersThread(), return false);
switch (newSignal) { switch (newSignal) {
case SignalType::Started: case SignalType::Started:
return m_processState == QProcess::Starting; return m_processState == QProcess::Starting;
@@ -335,9 +377,17 @@ bool LauncherHandle::canWaitFor(SignalType newSignal) const
return false; return false;
} }
QProcess::ProcessState LauncherHandle::state() const
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return QProcess::NotRunning);
return m_processState;
}
void LauncherHandle::cancel() void LauncherHandle::cancel()
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
switch (m_processState) { switch (m_processState) {
case QProcess::NotRunning: case QProcess::NotRunning:
@@ -360,9 +410,45 @@ void LauncherHandle::cancel()
m_awaitingShouldContinue = false; m_awaitingShouldContinue = false;
} }
QByteArray LauncherHandle::readAllStandardOutput()
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return {});
return readAndClear(m_stdout);
}
QByteArray LauncherHandle::readAllStandardError()
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return {});
return readAndClear(m_stderr);
}
qint64 LauncherHandle::processId() const
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return 0);
return m_processId;
}
QString LauncherHandle::errorString() const
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return {});
return m_errorString;
}
void LauncherHandle::setErrorString(const QString &str)
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_errorString = str;
}
void LauncherHandle::start(const QString &program, const QStringList &arguments, const QByteArray &writeData) void LauncherHandle::start(const QString &program, const QStringList &arguments, const QByteArray &writeData)
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
if (m_socketError) { if (m_socketError) {
m_error = QProcess::FailedToStart; m_error = QProcess::FailedToStart;
@@ -382,6 +468,7 @@ void LauncherHandle::start(const QString &program, const QStringList &arguments,
qint64 LauncherHandle::write(const QByteArray &data) qint64 LauncherHandle::write(const QByteArray &data)
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return -1);
if (m_processState != QProcess::Running) if (m_processState != QProcess::Running)
return -1; return -1;
@@ -392,11 +479,93 @@ qint64 LauncherHandle::write(const QByteArray &data)
return data.size(); return data.size();
} }
QProcess::ProcessError LauncherHandle::error() const
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return QProcess::UnknownError);
return m_error;
}
QString LauncherHandle::program() const
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return {});
return m_command;
}
void LauncherHandle::setStandardInputFile(const QString &fileName)
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_standardInputFile = fileName;
}
void LauncherHandle::setProcessChannelMode(QProcess::ProcessChannelMode mode)
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
if (mode != QProcess::SeparateChannels && mode != QProcess::MergedChannels) {
qWarning("setProcessChannelMode: The only supported modes are SeparateChannels and MergedChannels.");
return;
}
m_channelMode = mode;
}
void LauncherHandle::setProcessEnvironment(const QProcessEnvironment &environment)
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_environment = environment;
}
void LauncherHandle::setWorkingDirectory(const QString &dir)
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_workingDirectory = dir;
}
QProcess::ExitStatus LauncherHandle::exitStatus() const
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return QProcess::CrashExit);
return m_exitStatus;
}
void LauncherHandle::setBelowNormalPriority()
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_belowNormalPriority = true;
}
void LauncherHandle::setNativeArguments(const QString &arguments)
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_nativeArguments = arguments;
}
void LauncherHandle::setLowPriority()
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_lowPriority = true;
}
void LauncherHandle::setUnixTerminalDisabled()
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
m_unixTerminalDisabled = true;
}
// Ensure it's called from caller's thread, after moving LauncherHandle into the launcher's thread // Ensure it's called from caller's thread, after moving LauncherHandle into the launcher's thread
void LauncherHandle::createCallerHandle() void LauncherHandle::createCallerHandle()
{ {
QMutexLocker locker(&m_mutex); // may be not needed, as we call it just after Launcher's c'tor QMutexLocker locker(&m_mutex); // may be not needed, as we call it just after Launcher's c'tor
QTC_CHECK(m_callerHandle == nullptr); QTC_ASSERT(!isCalledFromLaunchersThread(), return);
QTC_ASSERT(m_callerHandle == nullptr, return);
m_callerHandle = new CallerHandle(); m_callerHandle = new CallerHandle();
connect(m_callerHandle, &CallerHandle::errorOccurred, this, &LauncherHandle::slotErrorOccurred, Qt::DirectConnection); connect(m_callerHandle, &CallerHandle::errorOccurred, this, &LauncherHandle::slotErrorOccurred, Qt::DirectConnection);
connect(m_callerHandle, &CallerHandle::started, this, &LauncherHandle::slotStarted, Qt::DirectConnection); connect(m_callerHandle, &CallerHandle::started, this, &LauncherHandle::slotStarted, Qt::DirectConnection);
@@ -407,7 +576,8 @@ void LauncherHandle::createCallerHandle()
void LauncherHandle::destroyCallerHandle() void LauncherHandle::destroyCallerHandle()
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_CHECK(m_callerHandle); QTC_ASSERT(isCalledFromCallersThread(), return);
QTC_ASSERT(m_callerHandle, return);
m_callerHandle->deleteLater(); m_callerHandle->deleteLater();
m_callerHandle = nullptr; m_callerHandle = nullptr;
} }
@@ -417,6 +587,7 @@ void LauncherHandle::slotErrorOccurred()
QProcess::ProcessError error = QProcess::UnknownError; QProcess::ProcessError error = QProcess::UnknownError;
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
error = m_error; error = m_error;
} }
emit errorOccurred(error); emit errorOccurred(error);
@@ -424,6 +595,10 @@ void LauncherHandle::slotErrorOccurred()
void LauncherHandle::slotStarted() void LauncherHandle::slotStarted()
{ {
{
QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
}
emit started(); emit started();
} }
@@ -433,6 +608,7 @@ void LauncherHandle::slotReadyRead()
bool hasError = false; bool hasError = false;
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
hasOutput = !m_stdout.isEmpty(); hasOutput = !m_stdout.isEmpty();
hasError = !m_stderr.isEmpty(); hasError = !m_stderr.isEmpty();
} }
@@ -448,6 +624,7 @@ void LauncherHandle::slotFinished()
QProcess::ExitStatus exitStatus = QProcess::NormalExit; QProcess::ExitStatus exitStatus = QProcess::NormalExit;
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QTC_ASSERT(isCalledFromCallersThread(), return);
exitCode = m_exitCode; exitCode = m_exitCode;
exitStatus = m_exitStatus; exitStatus = m_exitStatus;
} }
@@ -495,6 +672,7 @@ void LauncherSocket::sendData(const QByteArray &data)
LauncherHandle *LauncherSocket::registerHandle(quintptr token, ProcessMode mode) LauncherHandle *LauncherSocket::registerHandle(quintptr token, ProcessMode mode)
{ {
QTC_ASSERT(!isCalledFromLaunchersThread(), return nullptr);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
if (m_handles.contains(token)) if (m_handles.contains(token))
return nullptr; // TODO: issue a warning return nullptr; // TODO: issue a warning
@@ -515,6 +693,7 @@ LauncherHandle *LauncherSocket::registerHandle(quintptr token, ProcessMode mode)
void LauncherSocket::unregisterHandle(quintptr token) void LauncherSocket::unregisterHandle(quintptr token)
{ {
QTC_ASSERT(!isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
auto it = m_handles.find(token); auto it = m_handles.find(token);
if (it == m_handles.end()) if (it == m_handles.end())
@@ -528,23 +707,14 @@ void LauncherSocket::unregisterHandle(quintptr token)
LauncherHandle *LauncherSocket::handleForToken(quintptr token) const LauncherHandle *LauncherSocket::handleForToken(quintptr token) const
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return nullptr);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
return m_handles.value(token); return m_handles.value(token);
} }
void LauncherSocket::shutdown()
{
const auto socket = m_socket.exchange(nullptr);
if (!socket)
return;
socket->disconnect();
socket->write(ShutdownPacket().serialize());
socket->waitForBytesWritten(1000);
socket->deleteLater(); // or schedule a queued call to delete later?
}
void LauncherSocket::setSocket(QLocalSocket *socket) void LauncherSocket::setSocket(QLocalSocket *socket)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QTC_ASSERT(!m_socket, return); QTC_ASSERT(!m_socket, return);
m_socket.store(socket); m_socket.store(socket);
m_packetParser.setDevice(m_socket); m_packetParser.setDevice(m_socket);
@@ -562,8 +732,21 @@ void LauncherSocket::setSocket(QLocalSocket *socket)
emit ready(); emit ready();
} }
void LauncherSocket::shutdown()
{
QTC_ASSERT(isCalledFromLaunchersThread(), return);
const auto socket = m_socket.exchange(nullptr);
if (!socket)
return;
socket->disconnect();
socket->write(ShutdownPacket().serialize());
socket->waitForBytesWritten(1000);
socket->deleteLater(); // or schedule a queued call to delete later?
}
void LauncherSocket::handleSocketError() void LauncherSocket::handleSocketError()
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
auto socket = m_socket.load(); auto socket = m_socket.load();
if (socket->error() != QLocalSocket::PeerClosedError) if (socket->error() != QLocalSocket::PeerClosedError)
handleError(QCoreApplication::translate("Utils::LauncherSocket", handleError(QCoreApplication::translate("Utils::LauncherSocket",
@@ -572,6 +755,7 @@ void LauncherSocket::handleSocketError()
void LauncherSocket::handleSocketDataAvailable() void LauncherSocket::handleSocketDataAvailable()
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
try { try {
if (!m_packetParser.parse()) if (!m_packetParser.parse())
return; return;
@@ -605,12 +789,14 @@ void LauncherSocket::handleSocketDataAvailable()
void LauncherSocket::handleSocketDisconnected() void LauncherSocket::handleSocketDisconnected()
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
handleError(QCoreApplication::translate("Utils::LauncherSocket", handleError(QCoreApplication::translate("Utils::LauncherSocket",
"Launcher socket closed unexpectedly")); "Launcher socket closed unexpectedly"));
} }
void LauncherSocket::handleError(const QString &error) void LauncherSocket::handleError(const QString &error)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
const auto socket = m_socket.exchange(nullptr); const auto socket = m_socket.exchange(nullptr);
socket->disconnect(); socket->disconnect();
socket->deleteLater(); socket->deleteLater();
@@ -619,6 +805,7 @@ void LauncherSocket::handleError(const QString &error)
void LauncherSocket::handleRequests() void LauncherSocket::handleRequests()
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return);
const auto socket = m_socket.load(); const auto socket = m_socket.load();
QTC_ASSERT(socket, return); QTC_ASSERT(socket, return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
@@ -627,6 +814,11 @@ void LauncherSocket::handleRequests()
m_requests.clear(); m_requests.clear();
} }
bool LauncherSocket::isCalledFromLaunchersThread() const
{
return QThread::currentThread() == thread();
}
} // namespace Internal } // namespace Internal
} // namespace Utils } // namespace Utils

View File

@@ -59,6 +59,15 @@ class LauncherHandle : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
enum class SignalType {
NoSignal,
Error,
Started,
ReadyRead,
Finished
};
Q_ENUM(SignalType)
// All the public methods in this class are called exclusively from the caller's thread. // All the public methods in this class are called exclusively from the caller's thread.
bool waitForStarted(int msecs) bool waitForStarted(int msecs)
{ return waitForSignal(msecs, SignalType::Started); } { return waitForSignal(msecs, SignalType::Started); }
@@ -67,43 +76,32 @@ public:
bool waitForFinished(int msecs) bool waitForFinished(int msecs)
{ return waitForSignal(msecs, SignalType::Finished); } { return waitForSignal(msecs, SignalType::Finished); }
QProcess::ProcessState state() const QProcess::ProcessState state() const;
{ QMutexLocker locker(&m_mutex); return m_processState; }
void cancel(); void cancel();
QByteArray readAllStandardOutput() QByteArray readAllStandardOutput();
{ QMutexLocker locker(&m_mutex); return readAndClear(m_stdout); } QByteArray readAllStandardError();
QByteArray readAllStandardError()
{ QMutexLocker locker(&m_mutex); return readAndClear(m_stderr); }
qint64 processId() const { QMutexLocker locker(&m_mutex); return m_processId; } qint64 processId() const;
QString errorString() const { QMutexLocker locker(&m_mutex); return m_errorString; } QString errorString() const;
void setErrorString(const QString &str) { QMutexLocker locker(&m_mutex); m_errorString = str; } void setErrorString(const QString &str);
void start(const QString &program, const QStringList &arguments, const QByteArray &writeData); void start(const QString &program, const QStringList &arguments, const QByteArray &writeData);
qint64 write(const QByteArray &data); qint64 write(const QByteArray &data);
QProcess::ProcessError error() const { QMutexLocker locker(&m_mutex); return m_error; } QProcess::ProcessError error() const;
QString program() const { QMutexLocker locker(&m_mutex); return m_command; } QString program() const;
void setStandardInputFile(const QString &fileName) { QMutexLocker locker(&m_mutex); m_standardInputFile = fileName; } void setStandardInputFile(const QString &fileName);
void setProcessChannelMode(QProcess::ProcessChannelMode mode) { void setProcessChannelMode(QProcess::ProcessChannelMode mode);
QMutexLocker locker(&m_mutex); void setProcessEnvironment(const QProcessEnvironment &environment);
if (mode != QProcess::SeparateChannels && mode != QProcess::MergedChannels) { void setWorkingDirectory(const QString &dir);
qWarning("setProcessChannelMode: The only supported modes are SeparateChannels and MergedChannels."); QProcess::ExitStatus exitStatus() const;
return;
}
m_channelMode = mode;
}
void setProcessEnvironment(const QProcessEnvironment &environment)
{ QMutexLocker locker(&m_mutex); m_environment = environment; }
void setWorkingDirectory(const QString &dir) { QMutexLocker locker(&m_mutex); m_workingDirectory = dir; }
QProcess::ExitStatus exitStatus() const { QMutexLocker locker(&m_mutex); return m_exitStatus; }
void setBelowNormalPriority() { m_belowNormalPriority = true; } void setBelowNormalPriority();
void setNativeArguments(const QString &arguments) { m_nativeArguments = arguments; } void setNativeArguments(const QString &arguments);
void setLowPriority() { m_lowPriority = true; } void setLowPriority();
void setUnixTerminalDisabled() { m_unixTerminalDisabled = true; } void setUnixTerminalDisabled();
signals: signals:
void errorOccurred(QProcess::ProcessError error); void errorOccurred(QProcess::ProcessError error);
@@ -112,13 +110,6 @@ signals:
void readyReadStandardOutput(); void readyReadStandardOutput();
void readyReadStandardError(); void readyReadStandardError();
private: private:
enum class SignalType {
NoSignal,
Error,
Started,
ReadyRead,
Finished
};
// Called from caller's thread exclusively. // Called from caller's thread exclusively.
bool waitForSignal(int msecs, SignalType newSignal); bool waitForSignal(int msecs, SignalType newSignal);
@@ -168,6 +159,10 @@ private:
// Called from caller's or launcher's thread. // Called from caller's or launcher's thread.
void sendPacket(const Internal::LauncherPacket &packet); void sendPacket(const Internal::LauncherPacket &packet);
// Called from caller's or launcher's thread.
bool isCalledFromLaunchersThread() const;
bool isCalledFromCallersThread() const;
mutable QMutex m_mutex; mutable QMutex m_mutex;
QWaitCondition m_waitCondition; QWaitCondition m_waitCondition;
const quintptr m_token; const quintptr m_token;
@@ -175,7 +170,7 @@ private:
SignalType m_waitingFor = SignalType::NoSignal; SignalType m_waitingFor = SignalType::NoSignal;
QProcess::ProcessState m_processState = QProcess::NotRunning; QProcess::ProcessState m_processState = QProcess::NotRunning;
// cancel() sets it to false, modified only in caller's thread. // cancel() sets it to false, modified only in caller's thread, don't need to be protected by mutex
bool m_awaitingShouldContinue = false; bool m_awaitingShouldContinue = false;
int m_processId = 0; int m_processId = 0;
int m_exitCode = 0; int m_exitCode = 0;
@@ -241,6 +236,9 @@ private:
void handleError(const QString &error); void handleError(const QString &error);
void handleRequests(); void handleRequests();
// Called from caller's or launcher's thread.
bool isCalledFromLaunchersThread() const;
std::atomic<QLocalSocket *> m_socket{nullptr}; std::atomic<QLocalSocket *> m_socket{nullptr};
PacketParser m_packetParser; PacketParser m_packetParser;
std::vector<QByteArray> m_requests; std::vector<QByteArray> m_requests;