aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOrgad Shaneh <orgad.shaneh@audiocodes.com>2013-06-03 22:54:26 +0300
committerOrgad Shaneh <orgads@gmail.com>2013-06-06 11:54:16 +0200
commit29597d36d253be8e4032e1cfa02af8cf9f9419cd (patch)
tree1d15c961c72b949d4ddd2bd13c9ae537c55746ff
parent3f503c29ebecb0feca5418c5b2e4402c6c1d184f (diff)
Truly support multiple instances
Qt*Single*Application is not designed to support multiple instances. A shared memory is used to list all pids of running instances. When an instance gracefully quits, it removes its own entry and every other entry which is not currently running (i.e. crashed instances). Running qtcreator -client opens the first running instance in this list. Running qtcreator -client -pid works as before. Task-number: QTCREATORBUG-9458 Change-Id: I25d22a81097a224e9e45af093efa2ef2eccf6cb7 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com> Reviewed-by: Daniel Teske <daniel.teske@digia.com>
-rw-r--r--src/shared/qtsingleapplication/qtlocalpeer.cpp23
-rw-r--r--src/shared/qtsingleapplication/qtlocalpeer.h1
-rw-r--r--src/shared/qtsingleapplication/qtsingleapplication.cpp116
-rw-r--r--src/shared/qtsingleapplication/qtsingleapplication.h14
4 files changed, 107 insertions, 47 deletions
diff --git a/src/shared/qtsingleapplication/qtlocalpeer.cpp b/src/shared/qtsingleapplication/qtlocalpeer.cpp
index 07cfc74ed0..43e6bf4268 100644
--- a/src/shared/qtsingleapplication/qtlocalpeer.cpp
+++ b/src/shared/qtsingleapplication/qtlocalpeer.cpp
@@ -48,17 +48,13 @@ namespace SharedTools {
static const char ack[] = "ack";
-QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
- : QObject(parent), id(appId)
+QString QtLocalPeer::appSessionId(const QString &appId)
{
- if (id.isEmpty())
- id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win
-
- QByteArray idc = id.toUtf8();
+ QByteArray idc = appId.toUtf8();
quint16 idNum = qChecksum(idc.constData(), idc.size());
//### could do: two 16bit checksums over separate halves of id, for a 32bit result - improved uniqeness probability. Every-other-char split would be best.
- socketName = QLatin1String("qtsingleapplication-")
+ QString res = QLatin1String("qtsingleapplication-")
+ QString::number(idNum, 16);
#if defined(Q_OS_WIN)
if (!pProcessIdToSessionId) {
@@ -68,12 +64,21 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
if (pProcessIdToSessionId) {
DWORD sessionId = 0;
pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
- socketName += QLatin1Char('-') + QString::number(sessionId, 16);
+ res += QLatin1Char('-') + QString::number(sessionId, 16);
}
#else
- socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
+ res += QLatin1Char('-') + QString::number(::getuid(), 16);
#endif
+ return res;
+}
+
+QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
+ : QObject(parent), id(appId)
+{
+ if (id.isEmpty())
+ id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win
+ socketName = appSessionId(id);
server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
diff --git a/src/shared/qtsingleapplication/qtlocalpeer.h b/src/shared/qtsingleapplication/qtlocalpeer.h
index fad6501790..afff3929dc 100644
--- a/src/shared/qtsingleapplication/qtlocalpeer.h
+++ b/src/shared/qtsingleapplication/qtlocalpeer.h
@@ -45,6 +45,7 @@ public:
bool sendMessage(const QString &message, int timeout, bool block);
QString applicationId() const
{ return id; }
+ static QString appSessionId(const QString &appId);
Q_SIGNALS:
void messageReceived(const QString &message, QObject *socket);
diff --git a/src/shared/qtsingleapplication/qtsingleapplication.cpp b/src/shared/qtsingleapplication/qtsingleapplication.cpp
index f00ebea53c..af924f358d 100644
--- a/src/shared/qtsingleapplication/qtsingleapplication.cpp
+++ b/src/shared/qtsingleapplication/qtsingleapplication.cpp
@@ -30,34 +30,91 @@
#include "qtsingleapplication.h"
#include "qtlocalpeer.h"
-#include <QWidget>
+#include <qtlockedfile.h>
+
+#include <QDir>
#include <QFileOpenEvent>
+#include <QSharedMemory>
+#include <QWidget>
namespace SharedTools {
-void QtSingleApplication::sysInit(const QString &appId)
+static const int instancesSize = 1024;
+
+static QString instancesLockFilename(const QString &appSessionId)
+{
+ const QChar slash(QLatin1Char('/'));
+ QString res = QDir::tempPath();
+ if (!res.endsWith(slash))
+ res += slash;
+ return res + appSessionId + QLatin1String("-instances");
+}
+
+QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
+ : QApplication(argc, argv),
+ firstPeer(-1)
{
+ this->appId = appId;
+
+ const QString appSessionId = QtLocalPeer::appSessionId(appId);
+
+ // This shared memory holds a zero-terminated array of active (or crashed) instances
+ instances = new QSharedMemory(appSessionId, this);
actWin = 0;
block = false;
- firstPeer = new QtLocalPeer(this, appId);
- connect(firstPeer, SIGNAL(messageReceived(QString,QObject*)), SIGNAL(messageReceived(QString,QObject*)));
- pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') + QString::number(QCoreApplication::applicationPid(), 10));
- connect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), SIGNAL(messageReceived(QString,QObject*)));
-}
+ const qint64 appPid = QCoreApplication::applicationPid();
+ pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') + QString::number(appPid));
+ connect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), SIGNAL(messageReceived(QString,QObject*)));
-QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
- : QApplication(argc, argv, GUIenabled)
-{
- sysInit();
+ // First instance creates the shared memory, later instances attach to it
+ const bool created = instances->create(instancesSize);
+ if (!created) {
+ if (!instances->attach()) {
+ qWarning() << "Failed to initialize instances shared memory: "
+ << instances->errorString();
+ delete instances;
+ instances = 0;
+ return;
+ }
+ }
+ // QtLockedFile is used to workaround QTBUG-10364
+ QtLockedFile lockfile(instancesLockFilename(appSessionId));
+
+ lockfile.open(QtLockedFile::ReadWrite);
+ lockfile.lock(QtLockedFile::WriteLock);
+ qint64 *pids = static_cast<qint64 *>(instances->data());
+ if (!created) {
+ // Find the first instance that it still running
+ // The whole list needs to be iterated in order to append to it
+ for (; *pids; ++pids) {
+ if (firstPeer == -1 && isRunning(*pids))
+ firstPeer = *pids;
+ }
+ }
+ // Add current pid to list and terminate it
+ *pids++ = appPid;
+ *pids = 0;
+ lockfile.unlock();
}
-
-QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
- : QApplication(argc, argv)
+QtSingleApplication::~QtSingleApplication()
{
- this->appId = appId;
- sysInit(appId);
+ if (!instances)
+ return;
+ const qint64 appPid = QCoreApplication::applicationPid();
+ QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId)));
+ lockfile.open(QtLockedFile::ReadWrite);
+ lockfile.lock(QtLockedFile::WriteLock);
+ // Rewrite array, removing current pid and previously crashed ones
+ qint64 *pids = static_cast<qint64 *>(instances->data());
+ qint64 *newpids = pids;
+ for (; *pids; ++pids) {
+ if (*pids != appPid && isRunning(*pids))
+ *newpids++ = *pids;
+ }
+ *newpids = 0;
+ lockfile.unlock();
}
bool QtSingleApplication::event(QEvent *event)
@@ -72,8 +129,11 @@ bool QtSingleApplication::event(QEvent *event)
bool QtSingleApplication::isRunning(qint64 pid)
{
- if (pid == -1)
- return firstPeer->isClient();
+ if (pid == -1) {
+ pid = firstPeer;
+ if (pid == -1)
+ return false;
+ }
QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
return peer.isClient();
@@ -81,24 +141,21 @@ bool QtSingleApplication::isRunning(qint64 pid)
void QtSingleApplication::initialize(bool)
{
- firstPeer->isClient();
pidPeer->isClient();
}
bool QtSingleApplication::sendMessage(const QString &message, int timeout, qint64 pid)
{
- if (pid == -1)
- return firstPeer->sendMessage(message, timeout, block);
+ if (pid == -1) {
+ pid = firstPeer;
+ if (pid == -1)
+ return false;
+ }
QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
return peer.sendMessage(message, timeout, block);
}
-QString QtSingleApplication::id() const
-{
- return firstPeer->applicationId();
-}
-
QString QtSingleApplication::applicationId() const
{
return appId;
@@ -112,13 +169,10 @@ void QtSingleApplication::setBlock(bool value)
void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessage)
{
actWin = aw;
- if (activateOnMessage) {
- connect(firstPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow()));
+ if (activateOnMessage)
connect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow()));
- } else {
- disconnect(firstPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow()));
+ else
disconnect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow()));
- }
}
diff --git a/src/shared/qtsingleapplication/qtsingleapplication.h b/src/shared/qtsingleapplication/qtsingleapplication.h
index 47f290a925..a54d081291 100644
--- a/src/shared/qtsingleapplication/qtsingleapplication.h
+++ b/src/shared/qtsingleapplication/qtsingleapplication.h
@@ -29,6 +29,8 @@
#include <QApplication>
+QT_FORWARD_DECLARE_CLASS(QSharedMemory)
+
namespace SharedTools {
class QtLocalPeer;
@@ -38,13 +40,11 @@ class QtSingleApplication : public QApplication
Q_OBJECT
public:
- QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
+ ~QtSingleApplication();
bool isRunning(qint64 pid = -1);
- QString id() const;
-
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
QWidget* activationWindow() const;
bool event(QEvent *event);
@@ -56,18 +56,18 @@ public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000, qint64 pid = -1);
void activateWindow();
-//Obsolete methods:
public:
void initialize(bool = true);
-// end obsolete methods
Q_SIGNALS:
void messageReceived(const QString &message, QObject *socket);
void fileOpenRequest(const QString &file);
private:
- void sysInit(const QString &appId = QString());
- QtLocalPeer *firstPeer;
+ QString instancesFileName(const QString &appId);
+
+ qint64 firstPeer;
+ QSharedMemory *instances;
QtLocalPeer *pidPeer;
QWidget *actWin;
QString appId;