From 2dd9a4e553226897940948e946c64504b7f14fca Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sat, 6 Jun 2015 00:26:41 +0300 Subject: Fixed race condition #3! Library version 2.1; Explained implementation in README --- singleapplication.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 14 deletions(-) (limited to 'singleapplication.cpp') 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 +#include +#include +#include #include +#ifdef Q_OS_UNIX + #include + #include +#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 sharedMem; + static QMutex sharedMemMutex; +#endif + + QSharedMemory *memory; + SingleApplication *q_ptr; + QLocalServer *server; + QLocalSocket *socket; +}; + +#ifdef Q_OS_UNIX + QList 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(); -- cgit v1.2.1