diff options
author | Itay Grudev <itay-grudev@users.noreply.github.com> | 2016-05-04 20:52:25 +0100 |
---|---|---|
committer | Itay Grudev <itay-grudev@users.noreply.github.com> | 2016-05-04 20:52:25 +0100 |
commit | fd8004c706b1f63f8f6f114c054bb044028c2b1d (patch) | |
tree | 455ed73cf7eabf01703830e9f36bb3d577248782 /singleapplication.cpp | |
parent | Bug fix possible situation in which memory is detached (diff) | |
parent | Bugfix no showUp notification. Some minor improvements (diff) | |
download | singleapplication-fd8004c706b1f63f8f6f114c054bb044028c2b1d.tar.xz |
Merge pull request #6 from itay-grudev/secondary
Added stability improvements and support for secondary instances.
Diffstat (limited to 'singleapplication.cpp')
-rw-r--r-- | singleapplication.cpp | 230 |
1 files changed, 173 insertions, 57 deletions
diff --git a/singleapplication.cpp b/singleapplication.cpp index 100ec9c..b63abdc 100644 --- a/singleapplication.cpp +++ b/singleapplication.cpp @@ -1,7 +1,8 @@ #include <cstdlib> -#include <QtCore/QSharedMemory> #include <QtCore/QMutex> +#include <QtCore/QSemaphore> +#include <QtCore/QSharedMemory> #include <QtNetwork/QLocalSocket> #include <QtNetwork/QLocalServer> @@ -12,22 +13,79 @@ #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 + QLocalServer::removeServer( memory->key() ); server = new QLocalServer(); - QLocalServer::removeServer(serverName); - server->listen(serverName); - QObject::connect(server, SIGNAL(newConnection()), q, SLOT(slotConnectionEstablished())); + server->listen( memory->key() ); + QObject::connect( + server, + &QLocalServer::newConnection, + q, + &SingleApplication::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 + + notifyPrimary(); + } + + void notifyPrimary() + { + // 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 +96,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 +124,34 @@ public: delete sharedMem.back(); sharedMem.pop_back(); } - ::exit(128 + signum); + ::exit( 128 + signum ); } - static QList<QSharedMemory*> sharedMem; + static QList<SingleApplicationPrivate*> 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<QSharedMemory*> SingleApplicationPrivate::sharedMem; + QList<SingleApplicationPrivate*> SingleApplicationPrivate::sharedMem; QMutex SingleApplicationPrivate::sharedMemMutex; #endif @@ -89,44 +161,78 @@ 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); + // 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 - 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(); + } } + + d->notifyPrimary(); + delete d; + ::exit(EXIT_SUCCESS); } /** @@ -135,13 +241,23 @@ 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; } /** - * @brief Executed when the showUp command is sent to LocalServer + * @brief Executed when a connection has been made to the LocalServer */ void SingleApplication::slotConnectionEstablished() { |