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