aboutsummaryrefslogtreecommitdiff
path: root/singleapplication.cpp
diff options
context:
space:
mode:
authorItay Grudev <itay-grudev@users.noreply.github.com>2016-05-04 20:52:25 +0100
committerItay Grudev <itay-grudev@users.noreply.github.com>2016-05-04 20:52:25 +0100
commitfd8004c706b1f63f8f6f114c054bb044028c2b1d (patch)
tree455ed73cf7eabf01703830e9f36bb3d577248782 /singleapplication.cpp
parentBug fix possible situation in which memory is detached (diff)
parentBugfix no showUp notification. Some minor improvements (diff)
downloadsingleapplication-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.cpp230
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()
{