forked from qt-creator/qt-creator
Clang: Move QLocalServer in ConnectionClient
Before the QLocalServer was in the ConnectionServer so more than one client could connect to the server. But we never used that possibility which made the hand shaking much more difficult. It is now moved in the client, so that there is always a QLocalServer. Change-Id: Ifa357074b0c0809434c49d23b1cee38496f72f43 Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io> Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
@@ -26,6 +26,10 @@
|
||||
#include "connectionclient.h"
|
||||
|
||||
#include "clangsupportdebugutils.h"
|
||||
#include "processstartedevent.h"
|
||||
#include "processexception.h"
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QMetaMethod>
|
||||
@@ -34,50 +38,52 @@
|
||||
|
||||
namespace ClangBackEnd {
|
||||
|
||||
ConnectionClient::ConnectionClient()
|
||||
ConnectionClient::ConnectionClient(const QString &connectionName)
|
||||
: m_connectionName(connectionName)
|
||||
{
|
||||
m_processCreator.setArguments({connectionName});
|
||||
m_processCreator.setObserver(this);
|
||||
|
||||
listenForConnections();
|
||||
|
||||
m_processAliveTimer.setInterval(10000);
|
||||
resetTemporaryDir();
|
||||
resetTemporaryDirectory();
|
||||
|
||||
static const bool startAliveTimer = !qEnvironmentVariableIntValue("QTC_CLANG_NO_ALIVE_TIMER");
|
||||
|
||||
if (startAliveTimer)
|
||||
connectAliveTimer();
|
||||
|
||||
connectLocalSocketError();
|
||||
connectLocalSocketConnected();
|
||||
connectLocalSocketDisconnected();
|
||||
connectNewConnection();
|
||||
}
|
||||
|
||||
ConnectionClient::~ConnectionClient()
|
||||
{
|
||||
QLocalServer::removeServer(connectionName());
|
||||
}
|
||||
|
||||
void ConnectionClient::startProcessAndConnectToServerAsynchronously()
|
||||
{
|
||||
m_process = startProcess();
|
||||
}
|
||||
m_processIsStarting = true;
|
||||
|
||||
bool ConnectionClient::disconnectFromServer()
|
||||
{
|
||||
localSocket.disconnectFromServer();
|
||||
if (localSocket.state() != QLocalSocket::UnconnectedState)
|
||||
return localSocket.waitForDisconnected();
|
||||
|
||||
return true;
|
||||
m_processFuture = m_processCreator.createProcess();
|
||||
}
|
||||
|
||||
bool ConnectionClient::isConnected() const
|
||||
{
|
||||
return localSocket.state() == QLocalSocket::ConnectedState;
|
||||
return m_localSocket && m_localSocket->state() == QLocalSocket::ConnectedState;
|
||||
}
|
||||
|
||||
void ConnectionClient::ensureMessageIsWritten()
|
||||
{
|
||||
while (isConnected() && localSocket.bytesToWrite() > 0)
|
||||
localSocket.waitForBytesWritten(50);
|
||||
while (isConnected() && m_localSocket->bytesToWrite() > 0)
|
||||
m_localSocket->waitForBytesWritten(50);
|
||||
}
|
||||
|
||||
void ConnectionClient::sendEndMessage()
|
||||
{
|
||||
sendEndCommand();
|
||||
localSocket.flush();
|
||||
m_localSocket->flush();
|
||||
ensureMessageIsWritten();
|
||||
}
|
||||
|
||||
@@ -108,7 +114,7 @@ QProcessEnvironment ConnectionClient::processEnvironment() const
|
||||
|
||||
const QTemporaryDir &ConnectionClient::temporaryDirectory() const
|
||||
{
|
||||
return *m_temporaryDirectory;
|
||||
return m_processCreator.temporaryDirectory();
|
||||
}
|
||||
|
||||
LinePrefixer &ConnectionClient::stdErrPrefixer()
|
||||
@@ -121,29 +127,31 @@ LinePrefixer &ConnectionClient::stdOutPrefixer()
|
||||
return m_stdOutPrefixer;
|
||||
}
|
||||
|
||||
std::unique_ptr<QProcess> ConnectionClient::startProcess()
|
||||
QString ConnectionClient::connectionName() const
|
||||
{
|
||||
m_processIsStarting = true;
|
||||
return m_connectionName;
|
||||
}
|
||||
|
||||
auto process = std::unique_ptr<QProcess>(new QProcess);
|
||||
connectProcessFinished(process.get());
|
||||
connectProcessStarted(process.get());
|
||||
connectStandardOutputAndError(process.get());
|
||||
process->setProcessEnvironment(processEnvironment());
|
||||
process->start(processPath(), {connectionName()});
|
||||
resetProcessAliveTimer();
|
||||
bool ConnectionClient::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == int(ProcessStartedEvent::ProcessStarted)) {
|
||||
getProcessFromFuture();
|
||||
|
||||
return process;
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConnectionClient::restartProcessAsynchronously()
|
||||
{
|
||||
if (!m_processIsStarting) {
|
||||
finishProcess(std::move(m_process));
|
||||
resetTemporaryDir(); // clear left-over preambles
|
||||
getProcessFromFuture();
|
||||
|
||||
finishProcess(std::move(m_process));
|
||||
resetTemporaryDirectory(); // clear left-over preambles
|
||||
|
||||
startProcessAndConnectToServerAsynchronously();
|
||||
|
||||
startProcessAndConnectToServerAsynchronously();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty()
|
||||
@@ -153,23 +161,15 @@ void ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty()
|
||||
return; // Already reset, but we were scheduled after.
|
||||
}
|
||||
|
||||
if (localSocket.bytesAvailable() > 0)
|
||||
if (!m_localSocket || m_localSocket->bytesAvailable() > 0)
|
||||
return; // We come first, the incoming data was not yet processed.
|
||||
|
||||
restartProcessAsynchronously();
|
||||
}
|
||||
|
||||
void ConnectionClient::connectToLocalSocket()
|
||||
{
|
||||
if (!isConnected()) {
|
||||
localSocket.connectToServer(connectionName());
|
||||
QTimer::singleShot(20, this, &ConnectionClient::connectToLocalSocket);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionClient::endProcess(QProcess *process)
|
||||
{
|
||||
if (isProcessIsRunning() && isConnected()) {
|
||||
if (isProcessRunning(process) && isConnected()) {
|
||||
sendEndMessage();
|
||||
process->waitForFinished();
|
||||
}
|
||||
@@ -177,32 +177,33 @@ void ConnectionClient::endProcess(QProcess *process)
|
||||
|
||||
void ConnectionClient::terminateProcess(QProcess *process)
|
||||
{
|
||||
Q_UNUSED(process)
|
||||
#ifndef Q_OS_WIN32
|
||||
if (isProcessIsRunning()) {
|
||||
if (!Utils::HostOsInfo::isWindowsHost() && isProcessRunning()) {
|
||||
process->terminate();
|
||||
process->waitForFinished();
|
||||
process->waitForFinished(1000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConnectionClient::killProcess(QProcess *process)
|
||||
{
|
||||
if (isProcessIsRunning()) {
|
||||
if (isProcessRunning(process)) {
|
||||
process->kill();
|
||||
process->waitForFinished();
|
||||
process->waitForFinished(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionClient::resetProcessIsStarting()
|
||||
void ConnectionClient::finishConnection()
|
||||
{
|
||||
m_processIsStarting = false;
|
||||
if (m_localSocket) {
|
||||
if (m_localSocket->state() != QLocalSocket::UnconnectedState)
|
||||
m_localSocket->disconnectFromServer();
|
||||
m_localSocket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionClient::printLocalSocketError(QLocalSocket::LocalSocketError socketError)
|
||||
{
|
||||
if (socketError != QLocalSocket::ServerNotFoundError)
|
||||
qWarning() << outputName() << "LocalSocket Error:" << localSocket.errorString();
|
||||
if (m_localSocket && socketError != QLocalSocket::ServerNotFoundError)
|
||||
qWarning() << outputName() << "LocalSocket Error:" << m_localSocket->errorString();
|
||||
}
|
||||
|
||||
void ConnectionClient::printStandardOutput()
|
||||
@@ -215,55 +216,74 @@ void ConnectionClient::printStandardError()
|
||||
qDebug("%s", m_stdErrPrefixer.prefix(m_process->readAllStandardError()).constData());
|
||||
}
|
||||
|
||||
void ConnectionClient::resetTemporaryDir()
|
||||
void ConnectionClient::resetTemporaryDirectory()
|
||||
{
|
||||
m_temporaryDirectory = std::make_unique<Utils::TemporaryDirectory>("clang-XXXXXX");
|
||||
m_processCreator.resetTemporaryDirectory();
|
||||
}
|
||||
|
||||
void ConnectionClient::connectLocalSocketConnected()
|
||||
void ConnectionClient::initializeProcess(QProcess *process)
|
||||
{
|
||||
connect(&localSocket,
|
||||
&QLocalSocket::connected,
|
||||
this,
|
||||
&ConnectionClient::connectedToLocalSocket);
|
||||
connectStandardOutputAndError(process);
|
||||
|
||||
connect(&localSocket,
|
||||
&QLocalSocket::connected,
|
||||
this,
|
||||
&ConnectionClient::resetProcessIsStarting);
|
||||
resetProcessAliveTimer();
|
||||
}
|
||||
|
||||
void ConnectionClient::connectLocalSocketDisconnected()
|
||||
{
|
||||
connect(&localSocket,
|
||||
connect(m_localSocket,
|
||||
&QLocalSocket::disconnected,
|
||||
this,
|
||||
&ConnectionClient::disconnectedFromLocalSocket);
|
||||
connect(m_localSocket,
|
||||
&QLocalSocket::disconnected,
|
||||
this,
|
||||
&ConnectionClient::restartProcessAsynchronously);
|
||||
}
|
||||
|
||||
void ConnectionClient::disconnectLocalSocketDisconnected()
|
||||
{
|
||||
if (m_localSocket) {
|
||||
disconnect(m_localSocket,
|
||||
&QLocalSocket::disconnected,
|
||||
this,
|
||||
&ConnectionClient::restartProcessAsynchronously);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionClient::finishProcess()
|
||||
{
|
||||
finishProcess(std::move(m_process));
|
||||
emit processFinished();
|
||||
}
|
||||
|
||||
void ConnectionClient::finishProcess(std::unique_ptr<QProcess> &&process)
|
||||
bool ConnectionClient::isProcessRunning()
|
||||
{
|
||||
getProcessFromFuture();
|
||||
|
||||
return isProcessRunning(m_process.get());
|
||||
}
|
||||
|
||||
void ConnectionClient::finishProcess(QProcessUniquePointer &&process)
|
||||
{
|
||||
disconnectLocalSocketDisconnected();
|
||||
|
||||
if (process) {
|
||||
m_processAliveTimer.stop();
|
||||
|
||||
disconnectProcessFinished(process.get());
|
||||
endProcess(process.get());
|
||||
disconnectFromServer();
|
||||
finishConnection();
|
||||
terminateProcess(process.get());
|
||||
killProcess(process.get());
|
||||
|
||||
resetCounter();
|
||||
} else {
|
||||
finishConnection();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionClient::waitForEcho()
|
||||
{
|
||||
return localSocket.waitForReadyRead();
|
||||
return m_localSocket->waitForReadyRead();
|
||||
}
|
||||
|
||||
bool ConnectionClient::waitForConnected()
|
||||
@@ -271,7 +291,7 @@ bool ConnectionClient::waitForConnected()
|
||||
bool isConnected = false;
|
||||
|
||||
for (int counter = 0; counter < 100; counter++) {
|
||||
isConnected = localSocket.waitForConnected(20);
|
||||
isConnected = m_localSocket && m_localSocket->waitForConnected(20);
|
||||
if (isConnected)
|
||||
return isConnected;
|
||||
else {
|
||||
@@ -280,52 +300,28 @@ bool ConnectionClient::waitForConnected()
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << outputName() << "cannot connect:" << localSocket.errorString();
|
||||
if (m_localSocket)
|
||||
qWarning() << outputName() << "cannot connect:" << m_localSocket->errorString();
|
||||
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
|
||||
QProcess *ConnectionClient::processForTestOnly() const
|
||||
QProcess *ConnectionClient::processForTestOnly()
|
||||
{
|
||||
getProcessFromFuture();
|
||||
|
||||
return m_process.get();
|
||||
}
|
||||
|
||||
QIODevice *ConnectionClient::ioDevice()
|
||||
{
|
||||
return &localSocket;
|
||||
return m_localSocket;
|
||||
}
|
||||
|
||||
bool ConnectionClient::isProcessIsRunning() const
|
||||
bool ConnectionClient::isProcessRunning(QProcess *process)
|
||||
{
|
||||
return m_process && m_process->state() == QProcess::Running;
|
||||
}
|
||||
|
||||
void ConnectionClient::connectProcessFinished(QProcess *process) const
|
||||
{
|
||||
connect(process,
|
||||
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this,
|
||||
&ConnectionClient::restartProcessAsynchronously);
|
||||
|
||||
}
|
||||
|
||||
void ConnectionClient::connectProcessStarted(QProcess *process) const
|
||||
{
|
||||
connect(process,
|
||||
&QProcess::started,
|
||||
this,
|
||||
&ConnectionClient::connectToLocalSocket);
|
||||
}
|
||||
|
||||
void ConnectionClient::disconnectProcessFinished(QProcess *process) const
|
||||
{
|
||||
if (process) {
|
||||
disconnect(process,
|
||||
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this,
|
||||
&ConnectionClient::restartProcessAsynchronously);
|
||||
}
|
||||
return process && process->state() == QProcess::Running;
|
||||
}
|
||||
|
||||
void ConnectionClient::connectStandardOutputAndError(QProcess *process) const
|
||||
@@ -336,7 +332,7 @@ void ConnectionClient::connectStandardOutputAndError(QProcess *process) const
|
||||
|
||||
void ConnectionClient::connectLocalSocketError() const
|
||||
{
|
||||
connect(&localSocket,
|
||||
connect(m_localSocket,
|
||||
static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
|
||||
this,
|
||||
&ConnectionClient::printLocalSocketError);
|
||||
@@ -350,14 +346,52 @@ void ConnectionClient::connectAliveTimer()
|
||||
&ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty);
|
||||
}
|
||||
|
||||
const QString &ConnectionClient::processPath() const
|
||||
void ConnectionClient::connectNewConnection()
|
||||
{
|
||||
return m_processPath;
|
||||
QObject::connect(&m_localServer,
|
||||
&QLocalServer::newConnection,
|
||||
this,
|
||||
&ConnectionClient::handleNewConnection);
|
||||
}
|
||||
|
||||
void ConnectionClient::handleNewConnection()
|
||||
{
|
||||
m_localSocket = m_localServer.nextPendingConnection();
|
||||
|
||||
connectLocalSocketError();
|
||||
connectLocalSocketDisconnected();
|
||||
|
||||
newConnectedServer(m_localSocket);
|
||||
|
||||
emit connectedToLocalSocket();
|
||||
}
|
||||
|
||||
void ConnectionClient::getProcessFromFuture()
|
||||
{
|
||||
try {
|
||||
if (m_processFuture.valid()) {
|
||||
m_process = m_processFuture.get();
|
||||
m_processIsStarting = false;
|
||||
|
||||
initializeProcess(m_process.get());
|
||||
}
|
||||
} catch (const ProcessException &processExeption) {
|
||||
qWarning() << "Clang backend process is not working."
|
||||
<< QLatin1String(processExeption.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionClient::listenForConnections()
|
||||
{
|
||||
bool isListing = m_localServer.listen(connectionName());
|
||||
|
||||
if (!isListing)
|
||||
qWarning() << "ConnectionClient: QLocalServer is not listing for connections!";
|
||||
}
|
||||
|
||||
void ConnectionClient::setProcessPath(const QString &processPath)
|
||||
{
|
||||
m_processPath = processPath;
|
||||
m_processCreator.setProcessPath(processPath);
|
||||
}
|
||||
|
||||
} // namespace ClangBackEnd
|
||||
|
||||
Reference in New Issue
Block a user