1
#include "kdsingleapplicationguard.h"
3
#if QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)
4
#ifndef QT_NO_SHAREDMEMORY
6
#include "kdsharedmemorylocker.h"
7
#include "kdlockedsharedmemorypointer.h"
10
#include <QCoreApplication>
11
#include <QSharedMemory>
12
#include <QSharedData>
13
#include <QBasicTimer>
29
#ifndef _SSIZE_T_DEFINED
30
typedef signed int ssize_t;
34
using namespace kdtools;
36
#ifndef KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS
37
#define KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS 10
40
#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES
41
#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 10
44
#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE
45
#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 32768
48
static unsigned int KDSINGLEAPPLICATIONGUARD_SHM_VERSION = 0;
50
Q_GLOBAL_STATIC_WITH_ARGS( int, registerInstanceType,
51
(qRegisterMetaType<KDSingleApplicationGuard::Instance>()) )
54
\class KDSingleApplicationGuard::Instance
55
\relates KDSingleApplicationGuard
57
\brief Information about instances a KDSingleApplicationGuard knows about
59
Instance represents instances of applications under
60
KDSingleApplicationGuard protection, and allows access to their
61
pid() and the arguments() they were started with.
64
class KDSingleApplicationGuard::Instance::Private : public QSharedData {
65
friend class ::KDSingleApplicationGuard::Instance;
67
Private( const QStringList & args, bool truncated, qint64 pid )
68
: pid( pid ), arguments( args ), truncated( truncated ) {}
72
QStringList arguments;
81
class KDSingleApplicationGuard::Private
83
friend class ::KDSingleApplicationGuard;
84
friend class ::KDSingleApplicationGuard::Instance;
85
friend struct ::ProcessInfo;
86
KDSingleApplicationGuard * const q;
88
Private( Policy policy, KDSingleApplicationGuard* qq );
91
void create( const QStringList& arguments );
93
bool checkOperational( const char * function, const char * act ) const;
94
bool checkOperationalPrimary( const char * function, const char * act ) const;
101
static void sharedmem_free( char* );
102
static char* sharedmem_malloc( size_t size );
105
void shutdownInstance();
109
static KDSingleApplicationGuard* primaryInstance;
123
KDSingleApplicationGuard::Instance::Instance( const QStringList & args, bool truncated, qint64 p )
124
: d( new Private( args, truncated, p ) )
127
(void)registerInstanceType();
131
Default constructor. Constructs in Instance that is \link isNull()
136
KDSingleApplicationGuard::Instance::Instance() : d( 0 ) {}
141
KDSingleApplicationGuard::Instance::Instance( const Instance & other )
151
KDSingleApplicationGuard::Instance::~Instance()
153
if ( d && !d->ref.deref() )
158
\fn KDSingleApplicationGuard::Instance::swap( Instance & other )
160
Swaps the contents of this and \a other.
162
This function never throws exceptions.
166
\fn KDSingleApplicationGuard::Instance::operator=( Instance other )
168
Assigns the contents of \a other to this.
170
This function is strongly exception-safe.
174
\fn std::swap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
175
\relates KDSingleApplicationGuard::Instance
177
Specialisation of std::swap() for
178
KDSingleApplicationGuard::Instance. Calls swap().
182
\fn qSwap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
183
\relates KDSingleApplicationGuard::Instance
185
Specialisation of qSwap() for
186
KDSingleApplicationGuard::Instance. Calls swap().
190
\fn KDSingleApplicationGuard::Instance::isNull() const
192
Returns whether this instance is null.
196
Returns whether this instance is valid. A valid instance is neither
197
null, nor does it have a negative PID.
199
bool KDSingleApplicationGuard::Instance::isValid() const
201
return d && d->pid >= 0 ;
205
Returns whether the #arguments are complete (\c false) or not (\c
206
true), e.g. because they have been truncated due to limited storage
211
bool KDSingleApplicationGuard::Instance::areArgumentsTruncated() const
213
return d && d->truncated;
217
Returns the arguments that this instance was started with.
219
\sa areArgumentsTruncated()
221
const QStringList & KDSingleApplicationGuard::Instance::arguments() const
225
static const QStringList empty;
230
Returns the process-id (PID) of this instance.
232
qint64 KDSingleApplicationGuard::Instance::pid() const
241
\class KDSingleApplicationGuard KDSingleApplicationGuard
243
\brief A guard to protect an application from having several instances.
245
KDSingleApplicationGuard can be used to make sure only one instance of an
246
application is running at the same time.
248
\note As KDSingleApplicationGuard currently uses QSharedMemory, Qt
249
4.4 or later is required.
253
\fn void KDSingleApplicationGuard::instanceStarted(const KDSingleApplicationGuard::Instance & instance)
255
This signal is emitted by the primary instance whenever another
256
instance \a instance started.
260
\fn void KDSingleApplicationGuard::instanceExited(const KDSingleApplicationGuard::Instance & instance)
262
This signal is emitted by the primary instance whenever another
263
instance \a instance exited.
267
\fn void KDSingleApplicationGuard::raiseRequested()
269
This signal is emitted when the current running application is requested
270
to raise its main window.
274
\fn void KDSingleApplicationGuard::exitRequested()
276
This signal is emitted when the current running application has been asked to exit
277
by calling kill on the instance.
281
\fn void KDSingleApplicationGuard::becamePrimaryInstance()
283
This signal is emitted when the current running application becomes
284
the new primary application. The old primary application has quit.
288
\fn void KDSingleApplicationGuard::becameSecondaryInstance()
290
This signal is emmited when the primary instance became secondary instance.
291
This happens when the instance doesn't update its status for some (default 10) seconds. Another instance
292
got primary instance in that case.
296
\fn void KDSingleApplicationGuard::policyChanged( KDSingleApplicationGuard::Policy policy )
298
This signal is emitted when the #policy of the system changes.
304
ExitedInstance = 0x01,
307
ShutDownCommand = 0x08,
309
BecomePrimaryCommand = 0x20,
313
static const quint16 PrematureEndOfOptions = -1;
314
static const quint16 RegularEndOfOptions = -2;
318
static const size_t MarkerSize = sizeof(quint16);
320
explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 )
326
setArguments( arguments );
329
void setArguments( const QStringList & arguments );
330
QStringList arguments( bool * prematureEnd ) const;
338
static inline bool operator==( const ProcessInfo & lhs, const ProcessInfo & rhs )
340
return lhs.command == rhs.command &&
341
( lhs.commandline == rhs.commandline || ( lhs.commandline != 0 && rhs.commandline != 0 && ::strcmp( lhs.commandline, rhs.commandline ) == 0 ) );
344
static inline bool operator!=( const ProcessInfo & lhs, const ProcessInfo & rhs )
346
return !operator==( lhs, rhs );
350
This struct contains information about the managed process system.
353
struct InstanceRegister
355
explicit InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy )
357
maxInstances( KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ),
360
std::fill_n( commandLines, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, 0 );
361
::memcpy( magicCookie, "kdsingleapp", 12 );
365
Returns whether this register was properly initialized by the first instance.
369
return ::strcmp( magicCookie, "kdsingleapp" ) == 0;
372
char magicCookie[ 12 ];
373
unsigned int policy : 8;
374
quint32 maxInstances : 20;
375
unsigned int version : 4;
376
ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ];
378
char commandLines[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ];
380
Q_DISABLE_COPY( InstanceRegister )
383
void ProcessInfo::setArguments( const QStringList & arguments )
385
if( commandline != 0 )
386
KDSingleApplicationGuard::Private::sharedmem_free( commandline );
389
if( arguments.isEmpty() )
392
size_t totalsize = MarkerSize;
393
Q_FOREACH( const QString& arg, arguments )
395
const QByteArray utf8 = arg.toUtf8();
396
totalsize += utf8.size() + MarkerSize;
398
InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
399
this->commandline = KDSingleApplicationGuard::Private::sharedmem_malloc( totalsize );
400
if( this->commandline == 0 )
402
qWarning("KDSingleApplicationguard: out of memory when trying to save arguments.\n");
406
char* const commandline = this->commandline + reinterpret_cast<qptrdiff>(reg->commandLines);
409
Q_FOREACH( const QString & arg, arguments )
411
const QByteArray utf8 = arg.toUtf8();
412
const int required = MarkerSize + utf8.size() + MarkerSize ;
413
const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ;
414
if ( required > available || utf8.size() > std::numeric_limits<quint16>::max() ) {
415
// write a premature-eoo marker, and quit
416
memcpy( commandline + argpos, &PrematureEndOfOptions, MarkerSize );
417
argpos += MarkerSize;
418
qWarning( "KDSingleApplicationGuard: argument list is too long (bytes required: %d, used: %d, available: %d",
419
required, argpos - 2, available );
422
const quint16 len16 = utf8.size();
423
// write the size of the data...
424
memcpy( commandline + argpos, &len16, MarkerSize );
425
argpos += MarkerSize;
427
memcpy( commandline + argpos, utf8.data(), len16 );
431
const ssize_t available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos;
432
assert( available >= static_cast<ssize_t>( MarkerSize ) );
433
memcpy( commandline + argpos, &RegularEndOfOptions, MarkerSize );
434
argpos += MarkerSize;
437
QStringList ProcessInfo::arguments( bool * prematureEnd ) const
440
if( commandline == 0 )
443
*prematureEnd = true;
447
InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
448
const char* const commandline = this->commandline + reinterpret_cast<qptrdiff>(reg->commandLines);
452
const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ;
453
assert( available >= 2 );
456
memcpy( &marker, commandline + argpos, MarkerSize );
457
argpos += MarkerSize;
459
if ( marker == PrematureEndOfOptions ) {
460
if ( prematureEnd ) *prematureEnd = true;
463
if ( marker == RegularEndOfOptions ) {
464
if ( prematureEnd ) *prematureEnd = false;
468
const int requested = MarkerSize + marker + MarkerSize ;
469
if ( requested > available ) {
470
const long long int p = pid;
471
qWarning( "KDSingleApplicationGuard: inconsistency detected when parsing command-line argument for process %lld", p );
472
if ( prematureEnd ) *prematureEnd = true;
476
result.push_back( QString::fromUtf8( commandline + argpos, marker ) );
483
KDSingleApplicationGuard::Private::~Private()
485
if( primaryInstance == q )
489
bool KDSingleApplicationGuard::Private::checkOperational( const char * function, const char * act ) const
494
qWarning( "KDSingleApplicationGuard::%s: need to be operational to %s", function, act );
498
bool KDSingleApplicationGuard::Private::checkOperationalPrimary( const char * function, const char * act ) const
500
if ( !checkOperational( function, act ) )
503
qWarning( "KDSingleApplicationGuard::%s: need to be primary to %s", function, act );
512
void KDSingleApplicationGuard::Private::sharedmem_free( char* pointer )
514
InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
515
char* const heap = reg->commandLines;
516
char* const heap_ptr = heap + reinterpret_cast<qptrdiff>(pointer) - sizeof( segmentheader );
517
const segmentheader* const header = reinterpret_cast< const segmentheader* >( heap_ptr );
518
const size_t size = header->size;
520
char* end = heap + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE;
522
std::copy( heap_ptr + size, end, heap_ptr );
523
std::fill( end - size, end, 0 );
525
for( uint i = 0; i < reg->maxInstances; ++i )
527
if( reg->info[ i ].commandline > pointer )
528
reg->info[ i ].commandline -= size + sizeof( segmentheader );
532
char* KDSingleApplicationGuard::Private::sharedmem_malloc( size_t size )
534
InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
535
char* heap = reg->commandLines;
537
while( heap + sizeof( segmentheader ) + size < reg->commandLines + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE )
539
segmentheader* const header = reinterpret_cast< segmentheader* >( heap );
540
if( header->size == 0 )
543
return heap + sizeof( segmentheader ) - reinterpret_cast<qptrdiff>(reg->commandLines);
545
heap += sizeof( header ) + header->size;
550
void KDSingleApplicationGuard::Private::shutdownInstance()
552
KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem );
553
instances->info[ q->d->id ].command |= ExitedInstance;
555
if( q->isPrimaryInstance() )
557
// ohh... we need a new primary instance...
558
for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
560
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 )
562
instances->info[ i ].command |= BecomePrimaryCommand;
566
// none found? then my species is dead :-(
570
KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0;
573
Requests that the instance kills itself (by emitting exitRequested).
575
If the instance has since exited, does nothing.
577
\sa shutdown(), raise()
579
void KDSingleApplicationGuard::Instance::kill()
581
KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
582
for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
584
if( instances->info[ i ].pid != d->pid )
586
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
587
instances->info[ i ].command = KillCommand;
592
Requests that the instance shuts itself down (by calling QCoreApplication::quit()).
594
If the instance has since exited, does nothing.
598
void KDSingleApplicationGuard::Instance::shutdown()
600
KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
601
for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
603
if( instances->info[ i ].pid != d->pid )
605
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
606
instances->info[ i ].command = ShutDownCommand;
612
Requests that the instance raises its main window.
614
The effects are implementation-defined: the KDSingleApplicationGuard
615
corresponding to the instance will emit its \link
616
KDSingleApplicationGuard::raiseRequested() raiseRequested()\endlink
619
If the instance has since exited, does nothing.
621
\sa kill(), shutdown()
623
void KDSingleApplicationGuard::Instance::raise()
625
KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
626
for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
628
if( instances->info[ i ].pid != d->pid )
630
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
631
instances->info[ i ].command = RaiseCommand;
638
void KDSingleApplicationGuard::SIGINT_handler( int sig )
640
if( sig == SIGINT && Private::primaryInstance != 0 )
641
Private::primaryInstance->d->shutdownInstance();
647
\enum KDSingleApplicationGuard::Policy
649
Defines the policy that a KDSingleApplicationGuard can enforce:
653
\var KDSingleApplicationGuard::NoPolicy
655
instanceStarted() is emitted, and the new instance allowed to continue.
659
\var KDSingleApplicationGuard::AutoKillOtherInstances
661
instanceStarted() is emitted, and the new instance is killed (Instance::kill()).
665
Creates a new KDSingleApplicationGuard with arguments
666
QCoreApplication::arguments() and policy AutoKillOtherInstances,
667
passing \a parent to the base class constructor, as usual.
669
KDSingleApplicationGuard::KDSingleApplicationGuard( QObject * parent )
670
: QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
672
d->create( QCoreApplication::arguments() );
676
Creates a new KDSingleApplicationGuard with arguments
677
QCoreApplication::arguments() and policy \a policy, passing \a
678
parent to the base class constructor, as usual.
680
KDSingleApplicationGuard::KDSingleApplicationGuard( Policy policy, QObject * parent )
681
: QObject( parent ), d( new Private( policy, this ) )
683
d->create( QCoreApplication::arguments() );
687
Creates a new KDSingleApplicationGuard with arguments \a arguments
688
and policy AutoKillOtherInstances, passing \a parent to the base
689
class constructor, as usual.
691
KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, QObject * parent )
692
: QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
694
d->create( arguments );
698
Creates a new KDSingleApplicationGuard with arguments \a arguments
699
and policy \a policy, passing \a parent to the base class
700
constructor, as usual.
702
KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent )
703
: QObject( parent ), d( new Private( policy, this ) )
705
d->create( arguments );
708
KDSingleApplicationGuard::Private::Private( Policy policy_, KDSingleApplicationGuard * qq )
712
operational( false ),
713
exitRequested( false )
717
void KDSingleApplicationGuard::Private::create( const QStringList & arguments )
719
if ( !QCoreApplication::instance() ) {
720
qWarning( "KDSingleApplicationGuard: you need to construct a Q(Core)Application before you can construct a KDSingleApplicationGuard" );
724
const QString name = QCoreApplication::applicationName();
725
if ( name.isEmpty() ) {
726
qWarning( "KDSingleApplicationGuard: QCoreApplication::applicationName must not be emty" );
730
(void)registerInstanceType();
731
if ( primaryInstance == 0 )
736
// if another instance crashed, the shared memory segment is still there on Unix
737
// the following lines trigger deletion in that case
743
const bool created = mem.create( sizeof( InstanceRegister ) );
747
if( mem.error() != QSharedMemory::NoError && mem.error() != QSharedMemory::AlreadyExists )
748
errorMsg += QString::fromLatin1( "QSharedMemomry::create() failed: %1" ).arg( mem.errorString() );
752
if( mem.error() != QSharedMemory::NoError )
753
errorMsg += QString::fromLatin1( "QSharedMemomry::attach() failed: %1" ).arg( mem.errorString() );
755
qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." );
756
qWarning( "%s\n", errorMsg.toLocal8Bit().constData() );
760
const int maxWaitMSecs = 1000 * 60; // stop waiting after 60 seconds
764
// lets wait till the other instance initialized the register
765
bool initialized = false;
766
while( !initialized && waitTimer.elapsed() < maxWaitMSecs )
768
const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
769
initialized = instances->isValid();
777
const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
778
if ( instances->version != 0 ) {
779
qWarning( "KDSingleApplicationGuard: Detected version mismatch. "
780
"Highest supported version: %ud, actual version: %ud",
781
KDSINGLEAPPLICATIONGUARD_SHM_VERSION, instances->version );
788
KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
792
assert( instances->isValid() );
794
// we're _not_ the first instance
796
bool killOurSelf = false;
798
// find a new slot...
799
id = std::find( instances->info, instances->info + instances->maxInstances, ProcessInfo() ) - instances->info;
800
ProcessInfo& info = instances->info[ id ];
801
info = ProcessInfo( NewInstance, arguments, QCoreApplication::applicationPid() );
802
killOurSelf = instances->policy == AutoKillOtherInstances;
803
policy = static_cast<Policy>( instances->policy );
805
// but the signal that we tried to start was sent to the primary application
807
exitRequested = true;
811
// ok.... we are the first instance
812
new ( instances.get() ) InstanceRegister( policy ); // create a new list (in shared memory)
813
id = 0; // our id = 0
814
// and we've no command
815
instances->info[ 0 ] = ProcessInfo( NoCommand, arguments, QCoreApplication::applicationPid() );
819
::signal( SIGINT, SIGINT_handler );
822
// now listen for commands
823
timer.start( 750, q );
829
Destroys this SingleApplicationGuard.
830
If this instance has been the primary instance and no other instance is existing anymore,
831
the application is shut down completely. Otherwise the destructor selects another instance to
832
be the primary instances.
834
KDSingleApplicationGuard::~KDSingleApplicationGuard()
839
d->shutdownInstance();
843
\property KDSingleApplicationGuard::operational
845
Contains whether this KDSingleApplicationGuard is operational.
847
A non-operational KDSingleApplicationGuard cannot be used in any meaningful way.
849
Reasons for a KDSingleApplicationGuard being non-operational include:
850
\li it was constructed before QApplication (or at least QCoreApplication) was constructed
851
\li it failed to create or attach to the shared memory segment that is used for communication
853
Get this property's value using %isOperational().
855
bool KDSingleApplicationGuard::isOperational() const
857
return d->operational;
861
\property KDSingleApplicationGuard::exitRequested
863
Contains wheter this istance has been requested to exit. This will happen when this instance
864
was just started, but the policy is AutoKillOtherInstances or by explicitely calling kill on
867
Get this property's value using %isExitRequested().
869
bool KDSingleApplicationGuard::isExitRequested() const
871
return d->exitRequested;
875
\property KDSingleApplicationGuard::primaryInstance
877
Contains whether this instance is the primary instance.
879
The primary instance is the first instance which was started or else the instance which
880
got selected by KDSingleApplicationGuard's destructor, when the primary instance was
883
Get this property's value using %isPrimaryInstance(), and monitor changes to it
884
using becamePrimaryInstance().
886
bool KDSingleApplicationGuard::isPrimaryInstance() const
892
\property KDSingleApplicationGuard::policy
893
Specifies the policy KDSingleApplicationGuard is using when new instances are started.
894
This can only be set in the primary instance.
896
Get this property's value using %policy(), set it using %setPolicy(), and monitor changes
897
to it using policyChanged().
899
KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const
904
void KDSingleApplicationGuard::setPolicy( Policy policy )
906
if ( !d->checkOperationalPrimary( "setPolicy", "change the policy" ) )
909
if( d->policy == policy )
913
emit policyChanged( policy );
914
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
915
instances->policy = policy;
919
Returns a list of all currently running instances.
921
QVector<KDSingleApplicationGuard::Instance>
922
KDSingleApplicationGuard::instances() const
924
if ( !d->checkOperational( "instances", "report on other instances" ) )
925
return QVector<Instance>();
927
if ( Private::primaryInstance == 0 ) {
928
Private::primaryInstance = const_cast<KDSingleApplicationGuard*>( this );
931
QVector<Instance> result;
932
const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) );
933
for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
935
const ProcessInfo& info = instances->info[ i ];
936
if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 )
939
const QStringList arguments = info.arguments( &truncated );
940
result.push_back( Instance( arguments, truncated, info.pid ) );
947
Shuts down all other instances. This can only be called from the
948
the primary instance.
949
Shut down is done gracefully via QCoreApplication::quit().
951
void KDSingleApplicationGuard::shutdownOtherInstances()
953
if ( !d->checkOperationalPrimary( "shutdownOtherInstances", "shut other instances down" ) )
956
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
957
for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
959
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
960
instances->info[ i ].command = ShutDownCommand;
965
Kills all other instances. This can only be called from the
966
the primary instance.
967
Killing is done via emitting exitRequested. It's up to the receiving
968
instance to react properly.
970
void KDSingleApplicationGuard::killOtherInstances()
972
if ( !d->checkOperationalPrimary( "killOtherInstances", "kill other instances" ) )
975
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
976
for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
978
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
979
instances->info[ i ].command = KillCommand;
983
bool KDSingleApplicationGuard::event( QEvent * event )
985
if ( event->type() == QEvent::Timer ) {
986
const QTimerEvent * const te = static_cast<QTimerEvent*>( event );
987
if ( te->timerId() == d->timer.timerId() ) {
992
return QObject::event( event );
995
void KDSingleApplicationGuard::Private::poll() {
997
const quint32 now = QDateTime::currentDateTime().toTime_t();
999
if ( primaryInstance == 0 ) {
1000
primaryInstance = q;
1003
if ( q->isPrimaryInstance() )
1005
// only the primary instance will get notified about new instances
1006
QVector< Instance > exitedInstances;
1007
QVector< Instance > startedInstances;
1010
KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
1012
if( instances->info[ id ].pid != QCoreApplication::applicationPid() )
1014
for ( int i = 1, end = instances->maxInstances ; i < end && id == 0 ; ++i )
1016
if( instances->info[ i ].pid == QCoreApplication::applicationPid() )
1019
emit q->becameSecondaryInstance();
1023
instances->info[ id ].timestamp = now;
1025
for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
1027
ProcessInfo& info = instances->info[ i ];
1028
if( info.command & NewInstance )
1031
const QStringList arguments = info.arguments( &truncated );
1032
startedInstances.push_back( Instance( arguments, truncated, info.pid ) );
1033
info.command &= ~NewInstance; // clear NewInstance flag
1035
if( info.command & ExitedInstance )
1038
const QStringList arguments = info.arguments( &truncated );
1039
exitedInstances.push_back( Instance( arguments, truncated, info.pid ) );
1040
info.command = FreeInstance; // set FreeInstance flag
1045
// one signal for every new instance - _after_ the memory segment was unlocked again
1046
for( QVector< Instance >::const_iterator it = startedInstances.constBegin(); it != startedInstances.constEnd(); ++it )
1047
emit q->instanceStarted( *it );
1048
for( QVector< Instance >::const_iterator it = exitedInstances.constBegin(); it != exitedInstances.constEnd(); ++it )
1049
emit q->instanceExited( *it );
1053
// do we have a command?
1054
bool killOurSelf = false;
1055
bool shutDownOurSelf = false;
1056
bool policyDidChange = false;
1059
KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
1061
const Policy oldPolicy = policy;
1062
policy = static_cast<Policy>( instances->policy );
1063
policyDidChange = policy != oldPolicy;
1065
// check for the primary instance health status
1066
if( now - instances->info[ 0 ].timestamp > KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS )
1068
std::swap( instances->info[ 0 ], instances->info[ id ] );
1070
instances->info[ id ].timestamp = now;
1071
emit q->becamePrimaryInstance();
1072
instances->info[ id ].command &= ~BecomePrimaryCommand; // afterwards, reset the flag
1075
if( instances->info[ id ].command & BecomePrimaryCommand )
1077
// we became primary!
1078
instances->info[ 0 ] = instances->info[ id ];
1079
instances->info[ id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free
1081
instances->info[ id ].timestamp = now;
1082
emit q->becamePrimaryInstance();
1085
if( instances->info[ id ].command & RaiseCommand )
1088
emit q->raiseRequested();
1089
instances->info[ id ].command &= ~RaiseCommand; // afterwards, reset the flag
1093
killOurSelf = instances->info[ id ].command & KillCommand; // check for kill command
1094
shutDownOurSelf = instances->info[ id ].command & ShutDownCommand; // check for shut down command
1095
instances->info[ id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags
1098
instances->info[ id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag
1099
id = -1; // becauso our d'tor won't be called anymore
1103
if( killOurSelf ) // kill our self takes precedence
1105
exitRequested = true;
1106
emit q->exitRequested();
1108
else if( shutDownOurSelf )
1110
else if( policyDidChange )
1111
emit q->policyChanged( policy );
1115
#include "moc_kdsingleapplicationguard.cpp"
1117
#ifdef KDTOOLSCORE_UNITTESTS
1119
#include <kdunittest/test.h>
1121
#include "kdautopointer.h"
1125
#include <QtCore/QTime>
1126
#include <QtCore/QUuid>
1127
#include <QtTest/QSignalSpy>
1129
static void wait( int msec, QSignalSpy * spy=0, int expectedCount=INT_MAX )
1133
while ( ( !spy || spy->count() < expectedCount ) && t.elapsed() < msec )
1135
qApp->processEvents( QEventLoop::WaitForMoreEvents, qMax( 10, msec - t.elapsed() ) );
1139
static std::ostream& operator<<( std::ostream& stream, const QStringList& list )
1141
stream << "QStringList(";
1142
for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
1144
stream << " " << it->toLocal8Bit().data();
1145
if( it + 1 != list.end() )
1153
class ApplicationNameSaver {
1154
Q_DISABLE_COPY( ApplicationNameSaver )
1155
const QString oldname;
1157
explicit ApplicationNameSaver( const QString & name )
1158
: oldname( QCoreApplication::applicationName() )
1160
QCoreApplication::setApplicationName( name );
1162
~ApplicationNameSaver() {
1163
QCoreApplication::setApplicationName( oldname );
1168
KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) {
1170
// set it to an unique name
1171
const ApplicationNameSaver saver( QUuid::createUuid().toString() );
1173
KDAutoPointer<KDSingleApplicationGuard> guard3;
1174
KDAutoPointer<QSignalSpy> spy3;
1175
KDAutoPointer<QSignalSpy> spy4;
1178
KDSingleApplicationGuard guard1;
1179
assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances );
1180
assertEqual( guard1.instances().count(), 1 );
1181
assertTrue( guard1.isPrimaryInstance() );
1183
guard1.setPolicy( KDSingleApplicationGuard::NoPolicy );
1184
assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy );
1186
QSignalSpy spy1( &guard1, SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );
1188
KDSingleApplicationGuard guard2;
1189
assertEqual( guard1.instances().count(), 2 );
1190
assertEqual( guard2.instances().count(), 2 );
1191
assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy );
1192
assertFalse( guard2.isPrimaryInstance() );
1194
wait( 1000, &spy1, 1 );
1196
assertEqual( spy1.count(), 1 );
1197
guard3.reset( new KDSingleApplicationGuard );
1198
spy3.reset( new QSignalSpy( guard3.get(), SIGNAL(becamePrimaryInstance()) ) );
1199
spy4.reset( new QSignalSpy( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance) ) ) );
1200
assertFalse( guard3->isPrimaryInstance() );
1203
wait( 1000, spy3.get(), 1 );
1204
wait( 1000, spy4.get(), 1 );
1205
assertEqual( spy3->count(), 1 );
1206
assertEqual( guard3->instances().count(), 1 );
1207
assertTrue( guard3->isPrimaryInstance() );
1208
guard3.reset( new KDSingleApplicationGuard );
1210
assertEqual( guard3->instances().first().arguments(), qApp->arguments() );
1212
QSignalSpy spyStarted( guard3.get(), SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );
1213
QSignalSpy spyExited( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance)) );
1216
KDSingleApplicationGuard guard1;
1217
KDSingleApplicationGuard guard2;
1219
wait( 1000, &spyStarted, 2 );
1221
assertEqual( spyStarted.count(), 2 );
1224
wait( 1000, &spyExited, 2 );
1225
assertEqual( spyExited.count(), 2 );
1231
// check arguments-too-long handling:
1233
for ( unsigned int i = 0, end = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE/16 ; i != end ; ++i )
1234
args.push_back( QLatin1String( "0123456789ABCDEF" ) );
1235
KDSingleApplicationGuard guard3( args );
1237
wait( 1000, &spyStarted, 1 );
1239
const QVector<KDSingleApplicationGuard::Instance> instances = guard3.instances();
1240
assertEqual( instances.size(), 2 );
1242
assertTrue( instances[1].areArgumentsTruncated() );
1246
#endif // KDTOOLSCORE_UNITTESTS
1248
#endif // QT_NO_SHAREDMEMORY
1249
#endif // QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)