aboutsummaryrefslogtreecommitdiff
path: root/singleapplication.cpp
diff options
context:
space:
mode:
authorItay Grudev <itay@grudev.com>2015-06-06 00:26:41 +0300
committerItay Grudev <itay@grudev.com>2015-06-06 00:32:50 +0300
commit2dd9a4e553226897940948e946c64504b7f14fca (patch)
tree7e2ec99e04e94db2c6a21fc9c83cddb886b4d222 /singleapplication.cpp
parentAdded known bug remark (diff)
downloadsingleapplication-2dd9a4e553226897940948e946c64504b7f14fca.tar.xz
Fixed race condition #3! Library version 2.1; Explained implementation in README
Diffstat (limited to 'singleapplication.cpp')
-rw-r--r--singleapplication.cpp113
1 files changed, 99 insertions, 14 deletions
diff --git a/singleapplication.cpp b/singleapplication.cpp
index 18044da..ca2b716 100644
--- a/singleapplication.cpp
+++ b/singleapplication.cpp
@@ -1,6 +1,73 @@
#include "singleapplication.h"
+#include <QSharedMemory>
+#include <QLocalSocket>
+#include <QLocalServer>
+#include <QMutex>
#include <cstdlib>
+#ifdef Q_OS_UNIX
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
+class SingleApplicationPrivate {
+public:
+ SingleApplicationPrivate(SingleApplication *q_ptr) : q_ptr(q_ptr) { }
+
+ void startServer(QString &serverName)
+ {
+ // Start a QLocalServer to listen for connections
+ server = new QLocalServer();
+ server->removeServer(serverName);
+ server->listen(serverName);
+ QObject::connect(server, SIGNAL(newConnection()), q_ptr, SLOT(slotConnectionEstablished()));
+ }
+
+#ifdef Q_OS_UNIX
+ void crashHandler()
+ {
+ // This guarantees the program will work even with multiple
+ // instances of SingleApplication in different threads
+ // Which in my opinion is idiotic, but lets handle that too
+ {
+ sharedMemMutex.lock();
+ sharedMem.append(memory);
+ sharedMemMutex.unlock();
+ }
+ // Handle any further termination signals to ensure the
+ // QSharedMemory block is deleted even if the process crashes
+ signal(SIGSEGV, SingleApplicationPrivate::terminate);
+ signal(SIGABRT, SingleApplicationPrivate::terminate);
+ signal(SIGFPE, SingleApplicationPrivate::terminate);
+ signal(SIGILL, SingleApplicationPrivate::terminate);
+ signal(SIGINT, SingleApplicationPrivate::terminate);
+ signal(SIGTERM, SingleApplicationPrivate::terminate);
+ }
+
+ static void terminate(int signum)
+ {
+ while( ! sharedMem.empty() ) {
+ delete sharedMem.back();
+ sharedMem.pop_back();
+ }
+ ::exit(128 + signum);
+ }
+
+ static QList<QSharedMemory*> sharedMem;
+ static QMutex sharedMemMutex;
+#endif
+
+ QSharedMemory *memory;
+ SingleApplication *q_ptr;
+ QLocalServer *server;
+ QLocalSocket *socket;
+};
+
+#ifdef Q_OS_UNIX
+ QList<QSharedMemory*> SingleApplicationPrivate::sharedMem;
+ QMutex SingleApplicationPrivate::sharedMemMutex;
+#endif
+
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program
* if another instance already exists
@@ -8,23 +75,40 @@
* @param argv
*/
SingleApplication::SingleApplication(int &argc, char *argv[])
- : QApplication(argc, argv)
+ : QApplication(argc, argv), d_ptr(new SingleApplicationPrivate(this))
{
QString serverName = QApplication::organizationName() + QApplication::applicationName();
serverName.replace(QRegExp("[^\\w\\-. ]"), "");
- // Attempt to connect to the LocalServer
- socket = new QLocalSocket();
- socket->connectToServer(serverName);
- if(socket->waitForConnected(1000)){
- ::exit(EXIT_SUCCESS); // Terminate the program using STDLib's exit function
+ // Garantee thread safe behaviour with a shared memory block
+ d_ptr->memory = new QSharedMemory(serverName);
+
+ // Create a shared memory block with a minimum size of 1 byte
+ if( d_ptr->memory->create(1, QSharedMemory::ReadOnly) )
+ {
+#ifdef Q_OS_UNIX
+ // Handle any further termination signals to ensure the
+ // QSharedMemory block is deleted even if the process crashes
+ d_ptr->crashHandler();
+#endif
+ // Successful creation means that no main process exists
+ // So we start a Local Server to listen for connections
+ d_ptr->startServer(serverName);
} else {
- // If the connection is insuccessful, this is the main process
- // So we create a Local Server
- server = new QLocalServer();
- server->removeServer(serverName);
- server->listen(serverName);
- QObject::connect(server, SIGNAL(newConnection()), this, SLOT(slotConnectionEstablished()));
+ // Connect to the Local Server of the main process to notify it
+ // that a new process had been started
+ d_ptr->socket = new QLocalSocket();
+ d_ptr->socket->connectToServer(serverName);
+
+ // Even though a shared memory block exists, the original application might have crashed
+ // So only after a successful connection is the second instance terminated
+ if( d_ptr->socket->waitForConnected(100) )
+ {
+ ::exit(EXIT_SUCCESS); // Terminate the program using STDLib's exit function
+ } else {
+ delete d_ptr->memory;
+ ::exit(EXIT_SUCCESS);
+ }
}
}
@@ -33,7 +117,8 @@ SingleApplication::SingleApplication(int &argc, char *argv[])
*/
SingleApplication::~SingleApplication()
{
- server->close();
+ delete d_ptr->memory;
+ d_ptr->server->close();
}
/**
@@ -41,7 +126,7 @@ SingleApplication::~SingleApplication()
*/
void SingleApplication::slotConnectionEstablished()
{
- QLocalSocket *socket = server->nextPendingConnection();
+ QLocalSocket *socket = d_ptr->server->nextPendingConnection();
socket->close();
delete socket;
emit showUp();