summary refs log tree commit diff
path: root/third_party/SingleApplication-3.0.19/singleapplication.cpp
diff options
context:
space:
mode:
authorJoseph Donofry <joedonofry@gmail.com>2020-02-28 19:10:39 -0500
committerGitHub <noreply@github.com>2020-02-28 19:10:39 -0500
commit30cb7c5b02bb8a21a8d7257cfe5a8bd3db891606 (patch)
treeddadd16c86600ce937b47798a9b3d5ff2fdec998 /third_party/SingleApplication-3.0.19/singleapplication.cpp
parentMerge pull request #129 from nico202/master (diff)
parentMerge branch 'master' into 0.7.0-dev (diff)
downloadnheko-30cb7c5b02bb8a21a8d7257cfe5a8bd3db891606.tar.xz
Merge pull request #130 from Nheko-Reborn/0.7.0-dev
0.7.0 dev merge to master
Diffstat (limited to 'third_party/SingleApplication-3.0.19/singleapplication.cpp')
-rw-r--r--third_party/SingleApplication-3.0.19/singleapplication.cpp189
1 files changed, 189 insertions, 0 deletions
diff --git a/third_party/SingleApplication-3.0.19/singleapplication.cpp b/third_party/SingleApplication-3.0.19/singleapplication.cpp
new file mode 100644

index 00000000..8ff8747a --- /dev/null +++ b/third_party/SingleApplication-3.0.19/singleapplication.cpp
@@ -0,0 +1,189 @@ +// The MIT License (MIT) +// +// Copyright (c) Itay Grudev 2015 - 2018 +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include <QtCore/QElapsedTimer> +#include <QtCore/QThread> +#include <QtCore/QByteArray> +#include <QtCore/QSharedMemory> +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include <QtCore/QRandomGenerator> +#else +#include <QtCore/QDateTime> +#endif + +#include "singleapplication.h" +#include "singleapplication_p.h" + +/** + * @brief Constructor. Checks and fires up LocalServer or closes the program + * if another instance already exists + * @param argc + * @param argv + * @param {bool} allowSecondaryInstances + */ +SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout ) + : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) ) +{ + Q_D(SingleApplication); + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + // On Android and iOS since the library is not supported fallback to + // standard QApplication behaviour by simply returning at this point. + qWarning() << "SingleApplication is not supported on Android and iOS systems."; + return; +#endif + + // Store the current mode of the program + d->options = options; + + // Generating an application ID used for identifying the shared memory + // block and QLocalServer + d->genBlockServerName(); + +#ifdef Q_OS_UNIX + // By explicitly attaching it and then deleting it we make sure that the + // memory is deleted even after the process has crashed on Unix. + d->memory = new QSharedMemory( d->blockServerName ); + d->memory->attach(); + delete d->memory; +#endif + // Guarantee thread safe behaviour with a shared memory block. + d->memory = new QSharedMemory( d->blockServerName ); + + // Create a shared memory block + if( d->memory->create( sizeof( InstancesInfo ) ) ) { + // Initialize the shared memory block + d->memory->lock(); + d->initializeMemoryBlock(); + d->memory->unlock(); + } else { + // Attempt to attach to the memory segment + if( ! d->memory->attach() ) { + qCritical() << "SingleApplication: Unable to attach to shared memory block."; + qCritical() << d->memory->errorString(); + delete d; + ::exit( EXIT_FAILURE ); + } + } + + InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() ); + QElapsedTimer time; + time.start(); + + // Make sure the shared memory block is initialised and in consistent state + while( true ) { + d->memory->lock(); + + if( d->blockChecksum() == inst->checksum ) break; + + if( time.elapsed() > 5000 ) { + qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure."; + d->initializeMemoryBlock(); + } + + d->memory->unlock(); + + // Random sleep here limits the probability of a collision between two racing apps +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + QThread::sleep( QRandomGenerator::global()->bounded( 8u, 18u ) ); +#else + qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() ); + QThread::sleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ) ); +#endif + } + + if( inst->primary == false) { + d->startPrimary(); + d->memory->unlock(); + return; + } + + // Check if another instance can be started + if( allowSecondary ) { + inst->secondary += 1; + inst->checksum = d->blockChecksum(); + d->instanceNumber = inst->secondary; + d->startSecondary(); + if( d->options & Mode::SecondaryNotification ) { + d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance ); + } + d->memory->unlock(); + return; + } + + d->memory->unlock(); + + d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance ); + + delete d; + + ::exit( EXIT_SUCCESS ); +} + +/** + * @brief Destructor + */ +SingleApplication::~SingleApplication() +{ + Q_D(SingleApplication); + delete d; +} + +bool SingleApplication::isPrimary() +{ + Q_D(SingleApplication); + return d->server != nullptr; +} + +bool SingleApplication::isSecondary() +{ + Q_D(SingleApplication); + return d->server == nullptr; +} + +quint32 SingleApplication::instanceId() +{ + Q_D(SingleApplication); + return d->instanceNumber; +} + +qint64 SingleApplication::primaryPid() +{ + Q_D(SingleApplication); + return d->primaryPid(); +} + +bool SingleApplication::sendMessage( QByteArray message, int timeout ) +{ + Q_D(SingleApplication); + + // Nobody to connect to + if( isPrimary() ) return false; + + // Make sure the socket is connected + d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ); + + d->socket->write( message ); + bool dataWritten = d->socket->waitForBytesWritten( timeout ); + d->socket->flush(); + return dataWritten; +}