From 84d86455e7c475f025c8cafcc4c2685fbaeffec3 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 4 May 2016 19:59:07 +0100 Subject: Secondary instance implementation --- singleapplication.cpp | 213 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 159 insertions(+), 54 deletions(-) (limited to 'singleapplication.cpp') diff --git a/singleapplication.cpp b/singleapplication.cpp index 100ec9c..bd0b2b4 100644 --- a/singleapplication.cpp +++ b/singleapplication.cpp @@ -1,7 +1,8 @@ #include -#include #include +#include +#include #include #include @@ -12,22 +13,74 @@ #include "singleapplication.h" +struct InstancesInfo { + bool primary; + uint8_t secondary; +}; class SingleApplicationPrivate { +public: Q_DECLARE_PUBLIC(SingleApplication) -public: - SingleApplicationPrivate(SingleApplication *q_ptr) : q_ptr(q_ptr) { } + SingleApplicationPrivate(SingleApplication *q_ptr) : q_ptr(q_ptr) { + server = NULL; + } - void startServer(QString &serverName) + ~SingleApplicationPrivate() { - Q_Q(SingleApplication); + cleanUp(); + } - // Start a QLocalServer to listen for connections + void startPrimary( bool resetMemory ) + { + Q_Q(SingleApplication); +#ifdef Q_OS_UNIX + // Handle any further termination signals to ensure the + // QSharedMemory block is deleted even if the process crashes + crashHandler(); +#endif + // Successful creation means that no main process exists + // So we start a QLocalServer to listen for connections server = new QLocalServer(); - QLocalServer::removeServer(serverName); - server->listen(serverName); - QObject::connect(server, SIGNAL(newConnection()), q, SLOT(slotConnectionEstablished())); + QLocalServer::removeServer( memory->key() ); + server->listen( memory->key() ); + QObject::connect( + server, + SIGNAL( newConnection() ), + q, + SLOT( slotConnectionEstablished() ) + ); + + // Reset the number of connections + memory->lock(); + InstancesInfo* inst = (InstancesInfo*)memory->data(); + + if( resetMemory ){ + inst->primary = true; + inst->secondary = 0; + } else { + inst->primary = true; + } + + memory->unlock(); + } + + void startSecondary() + { +#ifdef Q_OS_UNIX + // Handle any further termination signals to ensure the + // QSharedMemory block is deleted even if the process crashes + crashHandler(); +#endif + + // Connect to the Local Server of the main process to notify it + // that a new process had been started + QLocalSocket socket; + socket.connectToServer( memory->key() ); + + // Notify the parent that a new instance had been started; + socket.waitForConnected(100); + socket.close(); } #ifdef Q_OS_UNIX @@ -38,25 +91,26 @@ public: // Which in my opinion is idiotic, but lets handle that too. { sharedMemMutex.lock(); - sharedMem.append(memory); + sharedMem.append( this ); sharedMemMutex.unlock(); } + // Handle any further termination signals to ensure the // QSharedMemory block is deleted even if the process crashes - signal(SIGHUP, SingleApplicationPrivate::terminate); // 1 - signal(SIGINT, SingleApplicationPrivate::terminate); // 2 - signal(SIGQUIT, SingleApplicationPrivate::terminate); // 3 - signal(SIGILL, SingleApplicationPrivate::terminate); // 4 - signal(SIGABRT, SingleApplicationPrivate::terminate); // 6 - signal(SIGBUS, SingleApplicationPrivate::terminate); // 7 - signal(SIGFPE, SingleApplicationPrivate::terminate); // 8 - signal(SIGSEGV, SingleApplicationPrivate::terminate); // 11 - signal(SIGSYS, SingleApplicationPrivate::terminate); // 12 - signal(SIGPIPE, SingleApplicationPrivate::terminate); // 13 - signal(SIGALRM, SingleApplicationPrivate::terminate); // 14 - signal(SIGTERM, SingleApplicationPrivate::terminate); // 15 - signal(SIGXCPU, SingleApplicationPrivate::terminate); // 24 - signal(SIGXFSZ, SingleApplicationPrivate::terminate); // 25 + signal( SIGHUP, SingleApplicationPrivate::terminate ); // 1 + signal( SIGINT, SingleApplicationPrivate::terminate ); // 2 + signal( SIGQUIT, SingleApplicationPrivate::terminate ); // 3 + signal( SIGILL, SingleApplicationPrivate::terminate ); // 4 + signal( SIGABRT, SingleApplicationPrivate::terminate ); // 6 + signal( SIGBUS, SingleApplicationPrivate::terminate ); // 7 + signal( SIGFPE, SingleApplicationPrivate::terminate ); // 8 + signal( SIGSEGV, SingleApplicationPrivate::terminate ); // 11 + signal( SIGSYS, SingleApplicationPrivate::terminate ); // 12 + signal( SIGPIPE, SingleApplicationPrivate::terminate ); // 13 + signal( SIGALRM, SingleApplicationPrivate::terminate ); // 14 + signal( SIGTERM, SingleApplicationPrivate::terminate ); // 15 + signal( SIGXCPU, SingleApplicationPrivate::terminate ); // 24 + signal( SIGXFSZ, SingleApplicationPrivate::terminate ); // 25 } static void terminate(int signum) @@ -65,21 +119,34 @@ public: delete sharedMem.back(); sharedMem.pop_back(); } - ::exit(128 + signum); + ::exit( 128 + signum ); } - static QList sharedMem; + static QList sharedMem; static QMutex sharedMemMutex; #endif + void cleanUp() { + memory->lock(); + InstancesInfo* inst = (InstancesInfo*)memory->data(); + if( server != NULL ) { + server->close(); + inst->primary = false; + } else { + if( inst->secondary > 0 ) + inst->secondary -= 1; + } + memory->unlock(); + delete memory; + } + QSharedMemory *memory; SingleApplication *q_ptr; QLocalServer *server; - QLocalSocket *socket; }; #ifdef Q_OS_UNIX - QList SingleApplicationPrivate::sharedMem; + QList SingleApplicationPrivate::sharedMem; QMutex SingleApplicationPrivate::sharedMemMutex; #endif @@ -89,44 +156,72 @@ public: * @param argc * @param argv */ -SingleApplication::SingleApplication(int &argc, char *argv[]) +SingleApplication::SingleApplication(int &argc, char *argv[], uint8_t secondaryInstances) : app_t(argc, argv), d_ptr(new SingleApplicationPrivate(this)) { Q_D(SingleApplication); + // Check command line arguments for the force primary and secondary flags +#ifdef Q_OS_UNIX + bool forcePrimary = false; +#endif + bool secondary = false; + for( int i = 0; i < argc; ++i ) { + if( strcmp( argv[i], "--secondary" ) == 0 ) { + secondary = true; +#ifndef Q_OS_UNIX + break; +#endif + } +#ifdef Q_OS_UNIX + if( strcmp( argv[i], "--primary" ) == 0 ) { + secondary = false; + forcePrimary = true; + break; + } +#endif + } + QString serverName = app_t::organizationName() + app_t::applicationName(); - serverName.replace(QRegExp("[^\\w\\-. ]"), ""); + serverName.replace( QRegExp("[^\\w\\-. ]"), "" ); // Guarantee thread safe behaviour with a shared memory block d->memory = new QSharedMemory(serverName); // Create a shared memory block with a minimum size of 1 byte - if( d->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->crashHandler(); + if( d->memory->create( sizeof(InstancesInfo) ) || forcePrimary ) { +#else + if( d->memory->create( sizeof(InstancesInfo) ) ) { #endif - // Successful creation means that no main process exists - // So we start a Local Server to listen for connections - d->startServer(serverName); + d->startPrimary( true ); + return; } else { - // Connect to the Local Server of the main process to notify it - // that a new process had been started - d->socket = new QLocalSocket(); - d->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. - d->socket->waitForConnected(100); - delete d->memory; - - // Terminate the program using STDLib's exit function - ::exit(EXIT_SUCCESS); + // Attempt to attach to the memory segment + if( d->memory->attach() ) { + d->memory->lock(); + InstancesInfo* inst = (InstancesInfo*)d->memory->data(); + + if( ! inst->primary ) { + d->startPrimary( false ); + d->memory->unlock(); + return; + } + + // Check if another instance can be started + if( secondary && inst->secondary < secondaryInstances ) { + inst->secondary += 1; + d->startSecondary(); + d->memory->unlock(); + return; + } + + d->memory->unlock(); + } } + + delete d->memory; + ::exit(EXIT_SUCCESS); } /** @@ -135,9 +230,19 @@ SingleApplication::SingleApplication(int &argc, char *argv[]) SingleApplication::~SingleApplication() { Q_D(SingleApplication); + delete d; +} - delete d->memory; - d->server->close(); +bool SingleApplication::isPrimary() +{ + Q_D(SingleApplication); + return d->server != NULL; +} + +bool SingleApplication::isSecondary() +{ + Q_D(SingleApplication); + return d->server == NULL; } /** -- cgit v1.2.1 From 8bccf446e190aedf0ffe11772a6a73d469ce774a Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 4 May 2016 20:41:23 +0100 Subject: Improved restoring QSharedMemory after crash --- singleapplication.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'singleapplication.cpp') diff --git a/singleapplication.cpp b/singleapplication.cpp index bd0b2b4..c5f52dc 100644 --- a/singleapplication.cpp +++ b/singleapplication.cpp @@ -185,8 +185,13 @@ SingleApplication::SingleApplication(int &argc, char *argv[], uint8_t secondaryI QString serverName = app_t::organizationName() + app_t::applicationName(); serverName.replace( QRegExp("[^\\w\\-. ]"), "" ); - // Guarantee thread safe behaviour with a shared memory block - d->memory = new QSharedMemory(serverName); + // Guarantee thread safe behaviour with a shared memory block. Also by + // attaching to it and deleting it we make sure that the memory i deleted + // even if the process had crashed + d->memory = new QSharedMemory( serverName ); + d->memory->attach(); + delete d->memory; + d->memory = new QSharedMemory( serverName ); // Create a shared memory block with a minimum size of 1 byte #ifdef Q_OS_UNIX @@ -246,7 +251,7 @@ bool SingleApplication::isSecondary() } /** - * @brief Executed when the showUp command is sent to LocalServer + * @brief Executed when a connection has been made to the LocalServer */ void SingleApplication::slotConnectionEstablished() { -- cgit v1.2.1 From 6fe44039410e7330349c2e783ec2ccfe68534715 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 4 May 2016 20:51:06 +0100 Subject: Bugfix no showUp notification. Some minor improvements --- singleapplication.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'singleapplication.cpp') diff --git a/singleapplication.cpp b/singleapplication.cpp index c5f52dc..b63abdc 100644 --- a/singleapplication.cpp +++ b/singleapplication.cpp @@ -41,14 +41,14 @@ public: #endif // Successful creation means that no main process exists // So we start a QLocalServer to listen for connections - server = new QLocalServer(); QLocalServer::removeServer( memory->key() ); + server = new QLocalServer(); server->listen( memory->key() ); QObject::connect( server, - SIGNAL( newConnection() ), + &QLocalServer::newConnection, q, - SLOT( slotConnectionEstablished() ) + &SingleApplication::slotConnectionEstablished ); // Reset the number of connections @@ -73,6 +73,11 @@ public: crashHandler(); #endif + notifyPrimary(); + } + + void notifyPrimary() + { // Connect to the Local Server of the main process to notify it // that a new process had been started QLocalSocket socket; @@ -225,7 +230,8 @@ SingleApplication::SingleApplication(int &argc, char *argv[], uint8_t secondaryI } } - delete d->memory; + d->notifyPrimary(); + delete d; ::exit(EXIT_SUCCESS); } -- cgit v1.2.1