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

@@ -0,0 +1,61 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "baseserverproxy.h"
#include "messageenvelop.h"
#include <QIODevice>
namespace ClangBackEnd {
BaseServerProxy::BaseServerProxy(IpcClientInterface *client, QIODevice *ioDevice)
: m_writeMessageBlock(ioDevice),
m_readMessageBlock(ioDevice),
m_client(client)
{
if (ioDevice)
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { BaseServerProxy::readMessages(); });
}
void BaseServerProxy::readMessages()
{
for (const auto &message : m_readMessageBlock.readAll())
m_client->dispatch(message);
}
void BaseServerProxy::resetCounter()
{
m_writeMessageBlock.resetCounter();
m_readMessageBlock.resetCounter();
}
void BaseServerProxy::setIoDevice(QIODevice *ioDevice)
{
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { BaseServerProxy::readMessages(); });
m_writeMessageBlock.setIoDevice(ioDevice);
m_readMessageBlock.setIoDevice(ioDevice);
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "ipcclientinterface.h"
#include "readmessageblock.h"
#include "writemessageblock.h"
namespace ClangBackEnd {
class CLANGSUPPORT_EXPORT BaseServerProxy
{
BaseServerProxy(const BaseServerProxy&) = delete;
BaseServerProxy &operator=(const BaseServerProxy&) = delete;
public:
BaseServerProxy(IpcClientInterface *client, QIODevice *ioDevice);
void readMessages();
void resetCounter();
void setIoDevice(QIODevice *ioDevice);
protected:
ClangBackEnd::WriteMessageBlock m_writeMessageBlock;
ClangBackEnd::ReadMessageBlock m_readMessageBlock;
IpcClientInterface *m_client;
};
} // namespace ClangBackEnd

View File

@@ -25,6 +25,8 @@
#include "clangcodemodelconnectionclient.h"
#include <utils/temporarydirectory.h>
#include <QCoreApplication>
namespace ClangBackEnd {
@@ -40,8 +42,14 @@ QString currentProcessId()
ClangCodeModelConnectionClient::ClangCodeModelConnectionClient(
ClangCodeModelClientInterface *client)
: serverProxy_(client, ioDevice())
: ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath()
+ QStringLiteral("/ClangBackEnd-")
+ currentProcessId()),
m_serverProxy(client, nullptr),
m_client(client)
{
m_processCreator.setTemporaryDirectoryPattern("clangbackend-XXXXXX");
stdErrPrefixer().setPrefix("clangbackend.stderr: ");
stdOutPrefixer().setPrefix("clangbackend.stdout: ");
}
@@ -53,22 +61,17 @@ ClangCodeModelConnectionClient::~ClangCodeModelConnectionClient()
ClangCodeModelServerProxy &ClangCodeModelConnectionClient::serverProxy()
{
return serverProxy_;
return m_serverProxy;
}
void ClangCodeModelConnectionClient::sendEndCommand()
{
serverProxy_.end();
m_serverProxy.end();
}
void ClangCodeModelConnectionClient::resetCounter()
{
serverProxy_.resetCounter();
}
QString ClangCodeModelConnectionClient::connectionName() const
{
return temporaryDirectory().path() + QStringLiteral("/ClangBackEnd-") + currentProcessId();
m_serverProxy.resetCounter();
}
QString ClangCodeModelConnectionClient::outputName() const
@@ -76,4 +79,9 @@ QString ClangCodeModelConnectionClient::outputName() const
return QStringLiteral("ClangCodeModelConnectionClient");
}
void ClangCodeModelConnectionClient::newConnectedServer(QIODevice *ioDevice)
{
m_serverProxy.setIoDevice(ioDevice);
}
} // namespace ClangBackEnd

View File

@@ -35,17 +35,17 @@ public:
ClangCodeModelConnectionClient(ClangCodeModelClientInterface *client);
~ClangCodeModelConnectionClient();
ClangCodeModelServerProxy &serverProxy();
protected:
void sendEndCommand() override;
void resetCounter() override;
QString connectionName() const override;
QString outputName() const override;
void newConnectedServer(QIODevice *ioDevice) override;
private:
ClangCodeModelServerProxy serverProxy_;
ClangCodeModelServerProxy m_serverProxy;
ClangCodeModelClientInterface *m_client;
};
} // namespace ClangBackEnd

View File

@@ -29,30 +29,11 @@
#include <clangcodemodelservermessages.h>
#include <messageenvelop.h>
#include <QLocalServer>
#include <QLocalSocket>
#include <QProcess>
namespace ClangBackEnd {
ClangCodeModelServerProxy::ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice)
: m_writeMessageBlock(ioDevice),
m_readMessageBlock(ioDevice),
m_client(client)
: BaseServerProxy(client, ioDevice)
{
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () {ClangCodeModelServerProxy::readMessages();});
}
void ClangCodeModelServerProxy::readMessages()
{
for (const auto &message : m_readMessageBlock.readAll())
m_client->dispatch(message);
}
void ClangCodeModelServerProxy::resetCounter()
{
m_writeMessageBlock.resetCounter();
m_readMessageBlock.resetCounter();
}
void ClangCodeModelServerProxy::end()

View File

@@ -24,9 +24,9 @@
****************************************************************************/
#pragma once
#include "baseserverproxy.h"
#include "clangcodemodelserverinterface.h"
#include "readmessageblock.h"
#include "writemessageblock.h"
#include <QtGlobal>
#include <QTimer>
@@ -42,12 +42,11 @@ QT_END_NAMESPACE
namespace ClangBackEnd {
class CLANGSUPPORT_EXPORT ClangCodeModelServerProxy : public ClangCodeModelServerInterface
class CLANGSUPPORT_EXPORT ClangCodeModelServerProxy : public BaseServerProxy,
public ClangCodeModelServerInterface
{
public:
ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice);
ClangCodeModelServerProxy(const ClangCodeModelServerProxy&) = delete;
ClangCodeModelServerProxy &operator=(const ClangCodeModelServerProxy&) = delete;
void end() override;
void registerTranslationUnitsForEditor(const RegisterTranslationUnitForEditorMessage &message) override;
@@ -62,15 +61,6 @@ public:
void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
void readMessages();
void resetCounter();
private:
ClangBackEnd::WriteMessageBlock m_writeMessageBlock;
ClangBackEnd::ReadMessageBlock m_readMessageBlock;
ClangCodeModelClientInterface *m_client;
};
} // namespace ClangBackEnd

View File

@@ -74,6 +74,9 @@ SOURCES += \
$$PWD/sourcelocationscontainer.cpp \
$$PWD/sourcelocationsforrenamingmessage.cpp \
$$PWD/sourcerangecontainer.cpp \
$$PWD/processcreator.cpp \
$$PWD/processexception.cpp \
$$PWD/processstartedevent.cpp \
$$PWD/sourcerangecontainerv2.cpp \
$$PWD/sourcerangesanddiagnosticsforquerymessage.cpp \
$$PWD/sourcerangescontainer.cpp \
@@ -85,7 +88,8 @@ SOURCES += \
$$PWD/updatevisibletranslationunitsmessage.cpp \
$$PWD/writemessageblock.cpp \
$$PWD/filepathcaching.cpp \
$$PWD/filepathid.cpp
$$PWD/filepathid.cpp \
$$PWD/baseserverproxy.cpp
HEADERS += \
$$PWD/cancelmessage.h \
@@ -161,6 +165,11 @@ HEADERS += \
$$PWD/sourcelocationscontainer.h \
$$PWD/sourcelocationsforrenamingmessage.h \
$$PWD/sourcerangecontainer.h \
$$PWD/filepath.h \
$$PWD/processcreator.h \
$$PWD/processexception.h \
$$PWD/processhandle.h \
$$PWD/processstartedevent.h \
$$PWD/sourcerangecontainerv2.h \
$$PWD/sourcerangesanddiagnosticsforquerymessage.h \
$$PWD/sourcerangescontainer.h \
@@ -187,6 +196,7 @@ HEADERS += \
$$PWD/filepathcachinginterface.h \
$$PWD/filepathcaching.h \
$$PWD/filepathcachingfwd.h \
$$PWD/baseserverproxy.h \
$$PWD/nativefilepathview.h \
$$PWD/filepath.h \
$$PWD/nativefilepath.h \

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

View File

@@ -27,14 +27,15 @@
#include "clangcodemodelserverproxy.h"
#include "lineprefixer.h"
#include "processcreator.h"
#include <utils/temporarydirectory.h>
#include <QLocalServer>
#include <QLocalSocket>
#include <QProcessEnvironment>
#include <QScopedPointer>
#include <QTemporaryDir>
#include <future>
#include <memory>
QT_BEGIN_NAMESPACE
@@ -53,10 +54,10 @@ class CLANGSUPPORT_EXPORT ConnectionClient : public QObject
Q_OBJECT
public:
ConnectionClient();
ConnectionClient(const QString &connectionName);
virtual ~ConnectionClient();
void startProcessAndConnectToServerAsynchronously();
bool disconnectFromServer();
bool isConnected() const;
void sendEndMessage();
@@ -64,18 +65,17 @@ public:
void resetProcessAliveTimer();
void setProcessAliveTimerInterval(int processTimerInterval);
const QString &processPath() const;
void setProcessPath(const QString &processPath);
void restartProcessAsynchronously();
void restartProcessIfTimerIsNotResettedAndSocketIsEmpty();
void finishProcess();
bool isProcessIsRunning() const;
bool isProcessRunning();
bool waitForEcho();
bool waitForConnected();
QProcess *processForTestOnly() const;
QProcess *processForTestOnly();
signals:
void connectedToLocalSocket();
@@ -90,48 +90,55 @@ protected:
virtual void sendEndCommand() = 0;
virtual void resetCounter() = 0;
virtual QString connectionName() const = 0;
virtual QString outputName() const = 0;
QString connectionName() const;
bool event(QEvent* event);
virtual void newConnectedServer(QIODevice *ioDevice) = 0;
private:
std::unique_ptr<QProcess> startProcess();
void finishProcess(std::unique_ptr<QProcess> &&process);
void connectToLocalSocket();
static bool isProcessRunning(QProcess *process);
void finishProcess(QProcessUniquePointer &&process);
void endProcess(QProcess *process);
void terminateProcess(QProcess *process);
void killProcess(QProcess *process);
void resetProcessIsStarting();
void finishConnection();
void printLocalSocketError(QLocalSocket::LocalSocketError socketError);
void printStandardOutput();
void printStandardError();
void initializeProcess(QProcess *process);
void resetTemporaryDir();
void resetTemporaryDirectory();
void connectLocalSocketConnected();
void connectLocalSocketDisconnected();
void connectProcessFinished(QProcess *process) const;
void connectProcessStarted(QProcess *process) const;
void disconnectProcessFinished(QProcess *process) const;
void disconnectLocalSocketDisconnected();
void connectStandardOutputAndError(QProcess *process) const;
void connectLocalSocketError() const;
void connectAliveTimer();
void connectNewConnection();
void handleNewConnection();
void getProcessFromFuture();
void listenForConnections();
void ensureMessageIsWritten();
QProcessEnvironment processEnvironment() const;
protected:
ProcessCreator m_processCreator;
private:
LinePrefixer m_stdErrPrefixer;
LinePrefixer m_stdOutPrefixer;
mutable std::unique_ptr<QProcess> m_process;
QLocalSocket localSocket;
std::unique_ptr<Utils::TemporaryDirectory> m_temporaryDirectory;
QLocalServer m_localServer;
std::future<QProcessUniquePointer> m_processFuture;
mutable QProcessUniquePointer m_process;
QLocalSocket *m_localSocket = nullptr;
QTimer m_processAliveTimer;
QString m_processPath;
QString m_connectionName;
bool m_isAliveTimerResetted = false;
bool m_processIsStarting = false;
};
} // namespace ClangBackEnd

View File

@@ -27,7 +27,5 @@
namespace ClangBackEnd {
QString ConnectionName::connectionName;
} // namespace ClangBackEnd

View File

@@ -31,6 +31,7 @@
#include <QLocalServer>
#include <QLocalSocket>
#include <QTimer>
#include <QDebug>
#include <cstdlib>
#include <memory>
@@ -41,46 +42,28 @@ namespace ClangBackEnd {
class ClangCodeModelServerInterface;
class ClangCodeModelClientProxy;
struct CLANGSUPPORT_EXPORT ConnectionName {
static QString connectionName;
};
template <typename ServerInterface,
typename ClientProxy>
class ConnectionServer
{
public:
ConnectionServer(const QString &connectionName)
ConnectionServer()
{
ConnectionName::connectionName = connectionName;
m_aliveTimer.start(5000);
m_localServer.setMaxPendingConnections(1);
QObject::connect(&m_localServer,
&QLocalServer::newConnection,
[&] { handleNewConnection(); });
QObject::connect(&m_aliveTimer,
&QTimer::timeout,
[&] { sendAliveMessage(); });
std::atexit(&ConnectionServer::removeServer);
#if defined(_GLIBCXX_HAVE_AT_QUICK_EXIT)
std::at_quick_exit(&ConnectionServer::removeServer);
#endif
std::set_terminate(&ConnectionServer::removeServer);
connectAliveTimer();
connectLocalSocketDisconnet();
}
~ConnectionServer()
{
removeServer();
if (m_localSocket.state() != QLocalSocket::UnconnectedState)
m_localSocket.disconnectFromServer();
}
void start()
void start(const QString &connectionName)
{
QLocalServer::removeServer(ConnectionName::connectionName);
m_localServer.listen(ConnectionName::connectionName);
connectToLocalServer(connectionName);
}
void setServer(ServerInterface *ipcServer)
@@ -89,17 +72,17 @@ public:
}
static void removeServer()
{
QLocalServer::removeServer(ConnectionName::connectionName);
}
private:
void handleNewConnection()
void connectToLocalServer(const QString &connectionName)
{
m_localSocket = nextPendingConnection();
QObject::connect(&m_localSocket,
static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
[&] (QLocalSocket::LocalSocketError) {
qWarning() << "ConnectionServer error:" << m_localSocket.errorString() << connectionName;
});
m_ipcClientProxy.reset(new ClientProxy(m_ipcServer, m_localSocket));
m_localSocket.connectToServer(connectionName);
m_ipcClientProxy = std::make_unique<ClientProxy>(m_ipcServer, &m_localSocket);
m_ipcServer->setClient(m_ipcClientProxy.get());
}
@@ -114,40 +97,40 @@ private:
{
m_ipcClientProxy.reset();
m_localSocket = nullptr;
delayedExitApplicationIfNoSockedIsConnected();
}
QLocalSocket *nextPendingConnection()
{
QLocalSocket *localSocket = m_localServer.nextPendingConnection();
QObject::connect(localSocket,
&QLocalSocket::disconnected,
[&] { handleSocketDisconnect(); });
return localSocket;
}
void delayedExitApplicationIfNoSockedIsConnected()
{
if (m_localSocket == nullptr)
QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); });
QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); });
}
void exitApplicationIfNoSockedIsConnected()
{
if (m_localSocket == nullptr)
QCoreApplication::exit();
if (m_localSocket.state() != QLocalSocket::UnconnectedState)
m_localSocket.disconnectFromServer();
QCoreApplication::exit();
}
void connectAliveTimer()
{
QObject::connect(&m_aliveTimer,
&QTimer::timeout,
[&] { sendAliveMessage(); });
}
void connectLocalSocketDisconnet()
{
QObject::connect(&m_localSocket,
&QLocalSocket::disconnected,
[&] { handleSocketDisconnect(); });
}
private:
std::unique_ptr<ClientProxy> m_ipcClientProxy;
QLocalSocket* m_localSocket;
ServerInterface *m_ipcServer;
QLocalServer m_localServer;
QLocalSocket m_localSocket;
QTimer m_aliveTimer;
std::unique_ptr<ClientProxy> m_ipcClientProxy;
ServerInterface *m_ipcServer;
};
} // namespace ClangBackEnd

View File

@@ -37,38 +37,23 @@
namespace ClangBackEnd {
PchManagerServerProxy::PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice)
: writeMessageBlock(ioDevice),
readMessageBlock(ioDevice),
client(client)
: BaseServerProxy(client, ioDevice)
{
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { readMessages(); });
}
void PchManagerServerProxy::end()
{
writeMessageBlock.write(EndMessage());
m_writeMessageBlock.write(EndMessage());
}
void PchManagerServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message)
{
writeMessageBlock.write(message);
m_writeMessageBlock.write(message);
}
void PchManagerServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message)
{
writeMessageBlock.write(message);
}
void PchManagerServerProxy::readMessages()
{
for (const auto &message : readMessageBlock.readAll())
client->dispatch(message);
}
void PchManagerServerProxy::resetCounter()
{
writeMessageBlock.resetCounter();
readMessageBlock.resetCounter();
m_writeMessageBlock.write(message);
}
} // namespace ClangBackEnd

View File

@@ -25,6 +25,7 @@
#pragma once
#include "baseserverproxy.h"
#include "clangsupport_global.h"
#include "pchmanagerserverinterface.h"
#include "readmessageblock.h"
@@ -42,25 +43,15 @@ namespace ClangBackEnd {
class PchManagerClientInterface;
class CLANGSUPPORT_EXPORT PchManagerServerProxy final : public PchManagerServerInterface
class CLANGSUPPORT_EXPORT PchManagerServerProxy final : public BaseServerProxy,
public PchManagerServerInterface
{
public:
explicit PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice);
PchManagerServerProxy(const PchManagerServerProxy&) = delete;
const PchManagerServerProxy &operator=(const PchManagerServerProxy&) = delete;
void end() override;
void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override;
void removePchProjectParts(RemovePchProjectPartsMessage &&message) override;
void readMessages();
void resetCounter();
private:
ClangBackEnd::WriteMessageBlock writeMessageBlock;
ClangBackEnd::ReadMessageBlock readMessageBlock;
PchManagerClientInterface *client = nullptr;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,173 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "processcreator.h"
#include "processexception.h"
#include "processstartedevent.h"
#include <QCoreApplication>
#include <QFileInfo>
#include <QTemporaryDir>
namespace ClangBackEnd {
using namespace std::chrono_literals;
ProcessCreator::ProcessCreator()
{
}
void ProcessCreator::setTemporaryDirectoryPattern(const QString &temporaryDirectoryPattern)
{
m_temporaryDirectoryPattern = temporaryDirectoryPattern;
resetTemporaryDirectory();
}
void ProcessCreator::setProcessPath(const QString &processPath)
{
m_processPath = processPath;
}
void ProcessCreator::setArguments(const QStringList &arguments)
{
m_arguments = arguments;
}
std::future<QProcessUniquePointer> ProcessCreator::createProcess() const
{
return std::async(std::launch::async, [&] {
checkIfProcessPathExists();
auto process = QProcessUniquePointer(new QProcess);
process->setProcessChannelMode(QProcess::QProcess::ForwardedChannels);
process->setProcessEnvironment(processEnvironment());
process->start(m_processPath, m_arguments);
process->waitForStarted(5000);
checkIfProcessWasStartingSuccessful(process.get());
postProcessStartedEvent();
process->moveToThread(QCoreApplication::instance()->thread());
return process;
});
}
void ProcessCreator::setObserver(QObject *observer)
{
this->m_observer = observer;
}
void ProcessCreator::checkIfProcessPathExists() const
{
if (!QFileInfo::exists(m_processPath)) {
const QString messageTemplate = QCoreApplication::translate("ProcessCreator",
"Executable does not exists: %1");
throwProcessException(messageTemplate.arg(m_processPath));
}
}
void ProcessCreator::checkIfProcessWasStartingSuccessful(QProcess *process) const
{
if (process->exitStatus() == QProcess::CrashExit || process->exitCode() != 0)
dispatchProcessError(process);
}
void ProcessCreator::dispatchProcessError(QProcess *process) const
{
switch (process->error()) {
case QProcess::UnknownError: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Unknown error happend.");
throwProcessException(message);
};
case QProcess::Crashed: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Process crashed.");
throwProcessException(message);
};
case QProcess::FailedToStart: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Process failed at startup.");
throwProcessException(message);
};
case QProcess::Timedout: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Process timeouted.");
throwProcessException(message);
};
case QProcess::WriteError: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Cannot write to process.");
throwProcessException(message);
};
case QProcess::ReadError: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Cannot read from process.");
throwProcessException(message);
};
}
throwProcessException("Internal impossible error!");
}
void ProcessCreator::postProcessStartedEvent() const
{
if (m_observer)
QCoreApplication::postEvent(m_observer, new ProcessStartedEvent);
}
void ProcessCreator::throwProcessException(const QString &message) const
{
postProcessStartedEvent();
throw ProcessException(message);
}
const QTemporaryDir &ProcessCreator::temporaryDirectory() const
{
return *m_temporaryDirectory.get();
}
void ProcessCreator::resetTemporaryDirectory()
{
m_temporaryDirectory = std::make_unique<Utils::TemporaryDirectory>(m_temporaryDirectoryPattern);
}
QProcessEnvironment ProcessCreator::processEnvironment() const
{
auto processEnvironment = QProcessEnvironment::systemEnvironment();
if (temporaryDirectory().isValid()) {
const QString temporaryDirectoryPath = temporaryDirectory().path();
processEnvironment.insert("TMPDIR", temporaryDirectoryPath);
processEnvironment.insert("TMP", temporaryDirectoryPath);
processEnvironment.insert("TEMP", temporaryDirectoryPath);
}
return processEnvironment;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <clangsupport_global.h>
#include "processhandle.h"
#include <utils/temporarydirectory.h>
#include <QStringList>
#include <future>
#include <memory>
QT_BEGIN_NAMESPACE
class QTemporaryDir;
class QProcessEnvironment;
QT_END_NAMESPACE
namespace ClangBackEnd {
class CLANGSUPPORT_EXPORT ProcessCreator
{
public:
ProcessCreator();
void setTemporaryDirectoryPattern(const QString &temporaryDirectoryPattern);
void setProcessPath(const QString &m_processPath);
void setArguments(const QStringList &m_arguments);
void setObserver(QObject *m_observer);
std::future<QProcessUniquePointer> createProcess() const;
const QTemporaryDir &temporaryDirectory() const;
void resetTemporaryDirectory();
private:
void checkIfProcessPathExists() const;
void checkIfProcessWasStartingSuccessful(QProcess *process) const;
[[noreturn]] void dispatchProcessError(QProcess *process) const;
void postProcessStartedEvent() const;
[[noreturn]] void throwProcessException(const QString &message) const;
QProcessEnvironment processEnvironment() const;
private:
std::unique_ptr<Utils::TemporaryDirectory> m_temporaryDirectory;
QString m_processPath;
QString m_temporaryDirectoryPattern;
QStringList m_arguments;
QObject *m_observer = nullptr;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,40 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "processexception.h"
namespace ClangBackEnd {
ProcessException::ProcessException(Utils::SmallString &&what)
: what_(std::move(what))
{
}
const char *ProcessException::what() const noexcept
{
return what_.data();
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/smallstring.h>
#include <exception>
namespace ClangBackEnd {
class ProcessException : public std::exception
{
public:
ProcessException() = default;
ProcessException(Utils::SmallString &&what);
const char *what() const noexcept final;
private:
Utils::SmallString what_;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QProcess>
#include <memory>
namespace ClangBackEnd {
class QProcessUniquePointerDeleter
{
public:
void operator()(QProcess* process)
{
process->kill();
process->waitForFinished();
}
};
using QProcessUniquePointer = std::unique_ptr<QProcess, QProcessUniquePointerDeleter>;
} // namespace ClangBackEnd

View File

@@ -0,0 +1,34 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "processstartedevent.h"
namespace ClangBackEnd {
ProcessStartedEvent::~ProcessStartedEvent()
{
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,44 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include <QEvent>
namespace ClangBackEnd {
class ProcessStartedEvent : public QEvent
{
public:
enum Type {
ProcessStarted = QEvent::User + 3456
};
ProcessStartedEvent()
: QEvent(static_cast<QEvent::Type>(ProcessStarted))
{}
~ProcessStartedEvent() override;
};
} // namespace ClangBackEnd

View File

@@ -77,14 +77,14 @@ MessageEnvelop ReadMessageBlock::read()
return message;
}
QVector<MessageEnvelop> ReadMessageBlock::readAll()
std::vector<MessageEnvelop> ReadMessageBlock::readAll()
{
QVector<MessageEnvelop> messages;
std::vector<MessageEnvelop> messages;
while (true) {
const MessageEnvelop message = read();
MessageEnvelop message = read();
if (message.isValid())
messages.append(message);
messages.push_back(std::move(message));
else
return messages;
}
@@ -97,6 +97,11 @@ void ReadMessageBlock::resetCounter()
m_messageCounter = 0;
}
void ReadMessageBlock::setIoDevice(QIODevice *ioDevice)
{
m_ioDevice = ioDevice;
}
bool ReadMessageBlock::isTheWholeMessageReadable(QDataStream &in)
{
if (m_ioDevice->bytesAvailable() < qint64(sizeof(m_blockSize)))

View File

@@ -27,6 +27,8 @@
#include <QtGlobal>
#include <vector>
QT_BEGIN_NAMESPACE
class QDataStream;
class QIODevice;
@@ -42,10 +44,12 @@ public:
ReadMessageBlock(QIODevice *ioDevice = nullptr);
MessageEnvelop read();
QVector<MessageEnvelop> readAll();
std::vector<MessageEnvelop> readAll();
void resetCounter();
void setIoDevice(QIODevice *ioDevice);
private:
bool isTheWholeMessageReadable(QDataStream &in);
bool checkIfMessageIsLost(QDataStream &in);

View File

@@ -35,58 +35,43 @@
namespace ClangBackEnd {
RefactoringServerProxy::RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice)
: writeMessageBlock(ioDevice),
readMessageBlock(ioDevice),
client(client)
: BaseServerProxy(client, ioDevice)
{
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { readMessages(); });
}
void RefactoringServerProxy::end()
{
writeMessageBlock.write(EndMessage());
m_writeMessageBlock.write(EndMessage());
}
void RefactoringServerProxy::requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message)
{
writeMessageBlock.write(message);
m_writeMessageBlock.write(message);
}
void RefactoringServerProxy::requestSourceRangesAndDiagnosticsForQueryMessage(RequestSourceRangesAndDiagnosticsForQueryMessage &&message)
{
writeMessageBlock.write(message);
m_writeMessageBlock.write(message);
}
void RefactoringServerProxy::requestSourceRangesForQueryMessage(RequestSourceRangesForQueryMessage &&message)
{
writeMessageBlock.write(message);
m_writeMessageBlock.write(message);
}
void RefactoringServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message)
{
writeMessageBlock.write(message);
m_writeMessageBlock.write(message);
}
void RefactoringServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message)
{
writeMessageBlock.write(message);
m_writeMessageBlock.write(message);
}
void RefactoringServerProxy::cancel()
{
writeMessageBlock.write(CancelMessage());
}
void RefactoringServerProxy::readMessages()
{
for (const auto &message : readMessageBlock.readAll())
client->dispatch(message);
}
void RefactoringServerProxy::resetCounter()
{
writeMessageBlock.resetCounter();
readMessageBlock.resetCounter();
m_writeMessageBlock.write(CancelMessage());
}
} // namespace ClangBackEnd

View File

@@ -25,6 +25,7 @@
#pragma once
#include "baseserverproxy.h"
#include "clangsupport_global.h"
#include "refactoringserverinterface.h"
#include "readmessageblock.h"
@@ -42,12 +43,11 @@ namespace ClangBackEnd {
class RefactoringClientInterface;
class CLANGSUPPORT_EXPORT RefactoringServerProxy final : public RefactoringServerInterface
class CLANGSUPPORT_EXPORT RefactoringServerProxy final : public BaseServerProxy,
public RefactoringServerInterface
{
public:
explicit RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice);
RefactoringServerProxy(const RefactoringServerProxy&) = delete;
const RefactoringServerProxy &operator=(const RefactoringServerProxy&) = delete;
void end() override;
void requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message) override;
@@ -56,15 +56,6 @@ public:
void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override;
void removePchProjectParts(RemovePchProjectPartsMessage &&message) override;
void cancel() override;
void readMessages();
void resetCounter();
private:
ClangBackEnd::WriteMessageBlock writeMessageBlock;
ClangBackEnd::ReadMessageBlock readMessageBlock;
RefactoringClientInterface *client = nullptr;
};
} // namespace ClangBackEnd

View File

@@ -72,6 +72,10 @@ void WriteMessageBlock::resetCounter()
m_messageCounter = 0;
}
void WriteMessageBlock::setIoDevice(QIODevice *ioDevice)
{
m_ioDevice = ioDevice;
}
} // namespace ClangBackEnd

View File

@@ -48,6 +48,8 @@ public:
void resetCounter();
void setIoDevice(QIODevice *ioDevice);
private:
qint64 m_messageCounter;
QIODevice *m_ioDevice;

View File

@@ -25,8 +25,9 @@
#include "pchmanagerconnectionclient.h"
#include <utils/temporarydirectory.h>
#include <QCoreApplication>
#include <QTemporaryDir>
namespace ClangPchManager {
@@ -41,30 +42,35 @@ QString currentProcessId()
ClangPchManager::PchManagerConnectionClient::PchManagerConnectionClient(
ClangBackEnd::PchManagerClientInterface *client)
: serverProxy_(client, ioDevice())
: ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath()
+ QStringLiteral("/ClangPchManagerBackEnd-")
+ currentProcessId()),
m_serverProxy(client, ioDevice())
{
m_processCreator.setTemporaryDirectoryPattern("clangpchmanagerbackend-XXXXXX");
stdErrPrefixer().setPrefix("PchManagerConnectionClient.stderr: ");
stdOutPrefixer().setPrefix("PchManagerConnectionClient.stdout: ");
}
PchManagerConnectionClient::~PchManagerConnectionClient()
{
finishProcess();
}
ClangBackEnd::PchManagerServerProxy &ClangPchManager::PchManagerConnectionClient::serverProxy()
{
return serverProxy_;
return m_serverProxy;
}
void ClangPchManager::PchManagerConnectionClient::sendEndCommand()
{
serverProxy_.end();
m_serverProxy.end();
}
void PchManagerConnectionClient::resetCounter()
{
serverProxy_.resetCounter();
}
QString ClangPchManager::PchManagerConnectionClient::connectionName() const
{
return temporaryDirectory().path() + QStringLiteral("/ClangPchManagerBackEnd-") + currentProcessId();
m_serverProxy.resetCounter();
}
QString PchManagerConnectionClient::outputName() const
@@ -72,4 +78,9 @@ QString PchManagerConnectionClient::outputName() const
return QStringLiteral("PchManagerConnectionClient");
}
void PchManagerConnectionClient::newConnectedServer(QIODevice *ioDevice)
{
m_serverProxy.setIoDevice(ioDevice);
}
} // namespace ClangPchManager

View File

@@ -34,17 +34,18 @@ class PchManagerConnectionClient : public ClangBackEnd::ConnectionClient
{
public:
PchManagerConnectionClient(ClangBackEnd::PchManagerClientInterface *client);
~PchManagerConnectionClient();
ClangBackEnd::PchManagerServerProxy &serverProxy();
protected:
void sendEndCommand() override;
void resetCounter() override;
QString connectionName() const override;
QString outputName() const override;
void newConnectedServer(QIODevice *ioDevice) override;
private:
ClangBackEnd::PchManagerServerProxy serverProxy_;
ClangBackEnd::PchManagerServerProxy m_serverProxy;
};
} // namespace ClangPchManager

View File

@@ -25,6 +25,8 @@
#include "refactoringconnectionclient.h"
#include <utils/temporarydirectory.h>
#include <QCoreApplication>
namespace ClangBackEnd {
@@ -39,8 +41,13 @@ QString currentProcessId()
}
RefactoringConnectionClient::RefactoringConnectionClient(RefactoringClientInterface *client)
: serverProxy_(client, ioDevice())
: ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath()
+ QStringLiteral("/ClangRefactoringBackEnd-")
+ currentProcessId()),
m_serverProxy(client, nullptr)
{
m_processCreator.setTemporaryDirectoryPattern("clangrefactoringbackend-XXXXXX");
stdErrPrefixer().setPrefix("RefactoringConnectionClient.stderr: ");
stdOutPrefixer().setPrefix("RefactoringConnectionClient.stdout: ");
}
@@ -52,22 +59,17 @@ RefactoringConnectionClient::~RefactoringConnectionClient()
RefactoringServerProxy &RefactoringConnectionClient::serverProxy()
{
return serverProxy_;
return m_serverProxy;
}
void RefactoringConnectionClient::sendEndCommand()
{
serverProxy_.end();
m_serverProxy.end();
}
void RefactoringConnectionClient::resetCounter()
{
serverProxy_.resetCounter();
}
QString RefactoringConnectionClient::connectionName() const
{
return temporaryDirectory().path() + QStringLiteral("/ClangRefactoringBackEnd-") + currentProcessId();
m_serverProxy.resetCounter();
}
QString RefactoringConnectionClient::outputName() const
@@ -75,4 +77,9 @@ QString RefactoringConnectionClient::outputName() const
return QStringLiteral("RefactoringConnectionClient");
}
void RefactoringConnectionClient::newConnectedServer(QIODevice *ioDevice)
{
m_serverProxy.setIoDevice(ioDevice);
}
} // namespace ClangBackEnd

View File

@@ -43,11 +43,11 @@ public:
protected:
void sendEndCommand() override;
void resetCounter() override;
QString connectionName() const override;
QString outputName() const override;
void newConnectedServer(QIODevice *ioDevice) override;
private:
RefactoringServerProxy serverProxy_;
RefactoringServerProxy m_serverProxy;
};
} // namespace ClangBackEnd

View File

@@ -41,8 +41,8 @@ namespace ClangRefactoring {
class RefactoringEngine : public CppTools::RefactoringEngineInterface
{
public:
RefactoringEngine(ClangBackEnd::RefactoringServerInterface &m_server,
ClangBackEnd::RefactoringClientInterface &m_client,
RefactoringEngine(ClangBackEnd::RefactoringServerInterface &server,
ClangBackEnd::RefactoringClientInterface &client,
ClangBackEnd::FilePathCachingInterface &filePathCache,
SymbolQueryInterface &symbolQuery);
~RefactoringEngine() override;

View File

@@ -72,9 +72,9 @@ int main(int argc, char *argv[])
clang_enableStackTraces();
ClangCodeModelServer clangCodeModelServer;
ConnectionServer<ClangCodeModelServer, ClangCodeModelClientProxy> connectionServer(connection);
connectionServer.start();
ConnectionServer<ClangCodeModelServer, ClangCodeModelClientProxy> connectionServer;
connectionServer.setServer(&clangCodeModelServer);
connectionServer.start(connection);
return application.exec();
}

View File

@@ -103,7 +103,7 @@ int main(int argc, char *argv[])
QCoreApplication application(argc, argv);
const QString connection = processArguments(application);
const QString connectionName = processArguments(application);
Sqlite::Database database{Utils::PathString{QDir::tempPath() + "/symbol.db"}};
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database};
@@ -120,9 +120,9 @@ int main(int argc, char *argv[])
includeWatcher.setNotifier(&clangPchManagerServer);
pchGenerator.setNotifier(&clangPchManagerServer);
ConnectionServer<PchManagerServer, PchManagerClientProxy> connectionServer(connection);
connectionServer.start();
ConnectionServer<PchManagerServer, PchManagerClientProxy> connectionServer;
connectionServer.setServer(&clangPchManagerServer);
connectionServer.start(connectionName);
return application.exec();
}

View File

@@ -76,9 +76,9 @@ try {
FilePathCaching filePathCache{database};
SymbolIndexing symbolIndexing{database, filePathCache};
RefactoringServer clangCodeModelServer{symbolIndexing, filePathCache};
ConnectionServer<RefactoringServer, RefactoringClientProxy> connectionServer(connection);
connectionServer.start();
ConnectionServer<RefactoringServer, RefactoringClientProxy> connectionServer;
connectionServer.setServer(&clangCodeModelServer);
connectionServer.start(connection);
return application.exec();

View File

@@ -31,7 +31,6 @@
#include <QCoreApplication>
#include <QDebug>
namespace ClangBackEnd {
void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message)
@@ -41,7 +40,6 @@ void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message)
void EchoClangCodeModelServer::end()
{
ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy>::removeServer();
QCoreApplication::quit();
}

View File

@@ -43,15 +43,18 @@ int main(int argc, char *argv[])
QCoreApplication application(argc, argv);
if (application.arguments().count() != 2) {
qWarning() << "wrong argument count";
if (application.arguments().count() < 2)
return 1;
}
else if (application.arguments().count() == 3)
*(int*)0 = 0;
else if (application.arguments().contains("connectionName"))
return 0;
EchoClangCodeModelServer echoClangCodeModelServer;
ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy> connectionServer(application.arguments()[1]);
connectionServer.start();
ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy> connectionServer;
connectionServer.setServer(&echoClangCodeModelServer);
connectionServer.start(application.arguments()[1]);
return application.exec();
}

View File

@@ -51,7 +51,7 @@ using ::testing::SizeIs;
struct Data {
Data() : client(&mockClangCodeModelClient) {}
MockClangCodeModelClient mockClangCodeModelClient;
NiceMock<MockClangCodeModelClient> mockClangCodeModelClient;
ClangBackEnd::ClangCodeModelConnectionClient client;
};
@@ -80,7 +80,7 @@ TEST_F(ClientServerOutsideProcessSlowTest, RestartProcessAsynchronously)
client.restartProcessAsynchronously();
ASSERT_TRUE(clientSpy.wait(100000));
ASSERT_TRUE(client.isProcessIsRunning());
ASSERT_TRUE(client.isProcessRunning());
ASSERT_TRUE(client.isConnected());
}
@@ -200,6 +200,6 @@ void ClientServerOutsideProcess::TearDown()
client.setProcessAliveTimerInterval(1000000);
client.waitForConnected();
ASSERT_TRUE(client.isProcessIsRunning());
ASSERT_TRUE(client.isProcessRunning());
ASSERT_TRUE(client.isConnected());
}

View File

@@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "eventspy.h"
#include <QCoreApplication>
#include <QEvent>
using namespace std::literals::chrono_literals;
EventSpy::EventSpy(uint eventType)
: startTime(std::chrono::steady_clock::now()),
eventType(eventType)
{
}
bool EventSpy::waitForEvent()
{
while (shouldRun())
QCoreApplication::processEvents();
return eventHappened;
}
bool EventSpy::event(QEvent *event)
{
if (event->type() == eventType) {
eventHappened = true;
return true;
}
return false;
}
bool EventSpy::shouldRun() const
{
return !eventHappened
&& (std::chrono::steady_clock::now() - startTime) < 1s;
}

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QObject>
#include <chrono>
class EventSpy : public QObject
{
Q_OBJECT
public:
EventSpy(uint eventType);
bool waitForEvent();
protected:
bool event(QEvent *event) override;
private:
bool shouldRun() const;
private:
std::chrono::steady_clock::time_point startTime;
uint eventType;
bool eventHappened = false;
};

View File

@@ -33,7 +33,6 @@ using testing::AnyNumber;
using testing::AnyOf;
using testing::Contains;
using testing::ElementsAre;
using testing::Eq;
using testing::Field;
using testing::HasSubstr;
using testing::InSequence;
@@ -51,3 +50,10 @@ using testing::SizeIs;
using testing::StrEq;
using testing::Throw;
using testing::UnorderedElementsAre;
using testing::Eq;
using testing::Ge;
using testing::Gt;
using testing::Le;
using testing::Lt;
using testing::Ne;

View File

@@ -0,0 +1,121 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include "eventspy.h"
#include <processcreator.h>
#include <processexception.h>
#include <processstartedevent.h>
#include <utils/hostosinfo.h>
#include <QProcess>
#include <future>
using testing::NotNull;
using ClangBackEnd::ProcessCreator;
using ClangBackEnd::ProcessException;
using ClangBackEnd::ProcessStartedEvent;
namespace {
class ProcessCreator : public testing::Test
{
protected:
void SetUp();
protected:
::ProcessCreator processCreator;
QStringList m_arguments = {QStringLiteral("connectionName")};
};
TEST_F(ProcessCreator, ProcessIsNotNull)
{
auto future = processCreator.createProcess();
auto process = future.get();
ASSERT_THAT(process.get(), NotNull());
}
TEST_F(ProcessCreator, ProcessIsRunning)
{
auto future = processCreator.createProcess();
auto process = future.get();
ASSERT_THAT(process->state(), QProcess::Running);
}
TEST_F(ProcessCreator, ProcessPathIsNotExisting)
{
processCreator.setProcessPath(Utils::HostOsInfo::withExecutableSuffix(ECHOSERVER"fail"));
auto future = processCreator.createProcess();
ASSERT_THROW(future.get(), ProcessException);
}
TEST_F(ProcessCreator, ProcessStartIsSucessfull)
{
auto future = processCreator.createProcess();
ASSERT_NO_THROW(future.get());
}
TEST_F(ProcessCreator, ProcessObserverGetsEvent)
{
EventSpy eventSpy(ProcessStartedEvent::ProcessStarted);
processCreator.setObserver(&eventSpy);
auto future = processCreator.createProcess();
eventSpy.waitForEvent();
}
TEST_F(ProcessCreator, TemporayPathIsSetForDefaultInitialization)
{
QString path = processCreator.temporaryDirectory().path();
ASSERT_THAT(path.size(), Gt(0));
}
TEST_F(ProcessCreator, TemporayPathIsResetted)
{
std::string oldPath = processCreator.temporaryDirectory().path().toStdString();
processCreator.resetTemporaryDirectory();
ASSERT_THAT(processCreator.temporaryDirectory().path().toStdString(),
AllOf(Not(IsEmpty()), Ne(oldPath)));
}
void ProcessCreator::SetUp()
{
processCreator.setTemporaryDirectoryPattern("process-XXXXXXX");
processCreator.resetTemporaryDirectory();
processCreator.setProcessPath(Utils::HostOsInfo::withExecutableSuffix(ECHOSERVER));
processCreator.setArguments(m_arguments);
}
}

View File

@@ -119,7 +119,7 @@ TEST_F(ReadAndWriteMessageBlock, ReadThreeMessagesAndTestCount)
writeMessageBlock.write(ClangBackEnd::EndMessage());
buffer.seek(0);
ASSERT_EQ(3, readMessageBlock.readAll().count());
ASSERT_THAT(readMessageBlock.readAll(), SizeIs(3));
}
TEST_F(ReadAndWriteMessageBlock, CompareEndMessage)

View File

@@ -41,6 +41,7 @@
#include <utils/smallstringvector.h>
#include <QBuffer>
#include <QTextCursor>
#include <QTextDocument>
@@ -74,9 +75,10 @@ protected:
NiceMock<MockSearchHandle> mockSearchHandle;
NiceMock<MockSymbolQuery> mockSymbolQuery;
MockRefactoringClientCallBack callbackMock;
QBuffer ioDevice;
ClangRefactoring::RefactoringClient client;
ClangBackEnd::RefactoringConnectionClient connectionClient{&client};
RefactoringEngine engine{connectionClient.serverProxy(), client, mockFilePathCaching, mockSymbolQuery};
ClangBackEnd::RefactoringServerProxy serverProxy{&client, &ioDevice};
RefactoringEngine engine{serverProxy, client, mockFilePathCaching, mockSymbolQuery};
QString fileContent{QStringLiteral("int x;\nint y;")};
QTextDocument textDocument{fileContent};
QTextCursor cursor{&textDocument};

View File

@@ -70,6 +70,7 @@ SOURCES += \
spydummy.cpp \
symbolindexer-test.cpp \
stringcache-test.cpp \
eventspy.cpp \
unittests-main.cpp \
utf8-test.cpp \
symbolstorage-test.cpp \
@@ -82,6 +83,7 @@ SOURCES += \
filepathcache-test.cpp \
filepathstorage-test.cpp \
filepathstoragesqlitestatementfactory-test.cpp \
processcreator-test.cpp \
nativefilepath-test.cpp \
nativefilepathview-test.cpp
@@ -176,6 +178,7 @@ HEADERS += \
conditionally-disabled-tests.h \
dummyclangipcclient.h \
dynamicastmatcherdiagnosticcontainer-matcher.h \
eventspy.h \
fakeprocess.h \
faketimer.h \
filesystem-utilities.h \