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:
Marco Bubke
2016-08-17 15:29:37 +02:00
parent 45c667d52c
commit f70bf3d2d1
44 changed files with 1170 additions and 351 deletions

View File

@@ -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