1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-08 23:26:40 +02:00

don't assume that exit() will clean up the stack

This commit is contained in:
Leo Franchi
2011-04-06 21:34:59 -04:00
parent ed0574f6b7
commit db7b2d2523

View File

@@ -8,11 +8,11 @@
#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024 #define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024
#endif #endif
KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p ) KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p )
: arguments( args ), : arguments( args ),
pid( p ) pid( p )
{ {
} }
@@ -61,31 +61,31 @@ void KDSingleApplicationGuard::timerEvent( QTimerEvent* )
using namespace kdtools; using namespace kdtools;
/*! /*!
\class KDSingleApplicationGuard KDSingleApplicationGuard * \class KDSingleApplicationGuard KDSingleApplicationGuard
\brief A guard to protect an application from having several instances. * \brief A guard to protect an application from having several instances.
*
KDSingleApplicationGuard can be used to make sure only one instance of an * KDSingleApplicationGuard can be used to make sure only one instance of an
application is running at the same time. * application is running at the same time.
*
\note As KDSingleApplicationGuard uses QSharedMemory Qt 4.4 or later is required * \note As KDSingleApplicationGuard uses QSharedMemory Qt 4.4 or later is required
*/ */
/*! /*!
\fn void KDSingleApplicationGuard::instanceStarted() * \fn void KDSingleApplicationGuard::instanceStarted()
This signal is emitted by the primary instance when ever one other * This signal is emitted by the primary instance when ever one other
instance was started. * instance was started.
*/ */
/*! /*!
\fn void KDSingleApplicationGuard::instanceExited() * \fn void KDSingleApplicationGuard::instanceExited()
This signal is emitted by the primary instance when ever one other * This signal is emitted by the primary instance when ever one other
instance was exited. * instance was exited.
*/ */
/*! /*!
\fn void KDSingleApplicationGuard::becamePrimaryInstance() * \fn void KDSingleApplicationGuard::becamePrimaryInstance()
This signal is emitted, when the current running application gets the new * This signal is emitted, when the current running application gets the new
primary application. The old primary application has quit. * primary application. The old primary application has quit.
*/ */
enum Command enum Command
@@ -105,8 +105,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( Commands )
struct ProcessInfo struct ProcessInfo
{ {
explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 ) explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 )
: command( c ), : command( c ),
pid( p ) pid( p )
{ {
std::fill_n( commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, '\0' ); std::fill_n( commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, '\0' );
@@ -149,7 +149,7 @@ struct ProcessInfo
bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs ) bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs )
{ {
return lhs.command == rhs.command && return lhs.command == rhs.command &&
::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0; ::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0;
} }
bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs ) bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs )
@@ -158,21 +158,21 @@ bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs )
} }
/*! /*!
This struct contains information about the managed process system. * This struct contains information about the managed process system.
\internal * \internal
*/ */
struct InstanceRegister struct InstanceRegister
{ {
InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy )
: policy( policy ) : policy( policy )
{ {
std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ); std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() );
::memcpy( magicCookie, "kdsingleapp", 12 ); ::memcpy( magicCookie, "kdsingleapp", 12 );
} }
/*! /*!
Returns wheter this register was properly initialized by the first instance. * Returns wheter this register was properly initialized by the first instance.
*/ */
bool isValid() const bool isValid() const
{ {
return ::strcmp( magicCookie, "kdsingleapp" ) == 0; return ::strcmp( magicCookie, "kdsingleapp" ) == 0;
@@ -192,18 +192,18 @@ bool operator==( const InstanceRegister& lhs, const InstanceRegister& rhs )
if( lhs.info[ i ] != rhs.info[ i ] ) if( lhs.info[ i ] != rhs.info[ i ] )
return false; return false;
return true; return true;
} }
/*! /*!
\internal * \internal
*/ */
class KDSingleApplicationGuard::Private class KDSingleApplicationGuard::Private
{ {
public: public:
Private( KDSingleApplicationGuard* qq ) Private( KDSingleApplicationGuard* qq )
: q( qq ), : q( qq ),
id( -1 ) id( -1 )
{ {
if( primaryInstance == 0 ) if( primaryInstance == 0 )
primaryInstance = q; primaryInstance = q;
@@ -258,28 +258,28 @@ void SIGINT_handler( int sig )
#endif #endif
/*! /*!
Creates a new KDSingleApplicationGuard guarding \a parent from mulitply instances. * Creates a new KDSingleApplicationGuard guarding \a parent from mulitply instances.
If \a policy is AutoKillOtherInstances (the default), all instances, which try to start, * If \a policy is AutoKillOtherInstances (the default), all instances, which try to start,
are killed automatically and instanceStarted() is emitted. * are killed automatically and instanceStarted() is emitted.
If \a policy is NoPolicy, the other instance will run and instanceStarted() is emitted. * If \a policy is NoPolicy, the other instance will run and instanceStarted() is emitted.
*/ */
KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Policy policy ) KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Policy policy )
: QObject( parent ), : QObject( parent ),
d( new Private( this ) ) d( new Private( this ) )
{ {
const QString name = parent->applicationName(); const QString name = parent->applicationName();
Q_ASSERT_X( !name.isEmpty(), "KDSingleApplicationGuard::KDSingleApplicationGuard", "applicationName must not be emty" ); Q_ASSERT_X( !name.isEmpty(), "KDSingleApplicationGuard::KDSingleApplicationGuard", "applicationName must not be emty" );
d->mem.setKey( name ); d->mem.setKey( name );
// if another instance crashed, the shared memory segment is still there on Unix // if another instance crashed, the shared memory segment is still there on Unix
// the following lines trigger deletion in that case // the following lines trigger deletion in that case
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
d->mem.attach(); d->mem.attach();
d->mem.detach(); d->mem.detach();
#endif #endif
d->policy = policy; d->policy = policy;
const bool created = d->mem.create( sizeof( InstanceRegister ) ); const bool created = d->mem.create( sizeof( InstanceRegister ) );
if( !created ) if( !created )
{ {
@@ -288,7 +288,7 @@ KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Po
qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." ); qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." );
return; return;
} }
// lets wait till the other instance initialized the register // lets wait till the other instance initialized the register
bool initialized = false; bool initialized = false;
while( !initialized ) while( !initialized )
@@ -298,52 +298,57 @@ KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Po
} }
} }
bool killMyself = false;
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
if( !created )
{ {
// we're _not_ the first instance KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
// but the
bool killOurSelf = false;
// find a new slot...
d->id = std::find( instances->info, instances->info + KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ) - instances->info;
ProcessInfo& info = instances->info[ d->id ];
info = ProcessInfo( NewInstance, parent->arguments(), QCoreApplication::applicationPid() );
killOurSelf = instances->policy == AutoKillOtherInstances;
d->policy = instances->policy;
// but the signal that we tried to start was sent to the primary application if( !created )
if( killOurSelf )
{ {
info.command |= ExitedInstance; // we're _not_ the first instance
exit( 1 ); // but the
bool killOurSelf = false;
// find a new slot...
d->id = std::find( instances->info, instances->info + KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ) - instances->info;
ProcessInfo& info = instances->info[ d->id ];
info = ProcessInfo( NewInstance, parent->arguments(), QCoreApplication::applicationPid() );
killOurSelf = instances->policy == AutoKillOtherInstances;
d->policy = instances->policy;
// but the signal that we tried to start was sent to the primary application
if( killOurSelf )
{
info.command |= ExitedInstance;
killMyself = true;
}
}
else
{
// ok.... we are the first instance
InstanceRegister reg( policy ); // create a new list
d->id = 0; // our id = 0
// and we've no command
reg.info[ 0 ] = ProcessInfo( NoCommand, parent->arguments(), QCoreApplication::applicationPid() );
*instances = reg; // push this is the process list into shared memory
} }
} }
else // call exit after we let the locker release our memory, as exit() is not guaranteed to clean up objects on the stack
{ if ( killMyself )
// ok.... we are the first instance exit( 1 );
InstanceRegister reg( policy ); // create a new list
d->id = 0; // our id = 0
// and we've no command
reg.info[ 0 ] = ProcessInfo( NoCommand, parent->arguments(), QCoreApplication::applicationPid() );
*instances = reg; // push this is the process list into shared memory
}
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
::signal( SIGINT, SIGINT_handler ); ::signal( SIGINT, SIGINT_handler );
#endif #endif
// now listen for commands // now listen for commands
startTimer( 250 ); startTimer( 250 );
} }
/*! /*!
Destroys this SingleApplicationGuard. * Destroys this SingleApplicationGuard.
If this instance has been the primary instance and no other instance is existing anymore, * If this instance has been the primary instance and no other instance is existing anymore,
the application is shut down completely. Otherwise the destructor selects another instance to * the application is shut down completely. Otherwise the destructor selects another instance to
be the primary instances. * be the primary instances.
*/ */
KDSingleApplicationGuard::~KDSingleApplicationGuard() KDSingleApplicationGuard::~KDSingleApplicationGuard()
{ {
@@ -352,16 +357,16 @@ KDSingleApplicationGuard::~KDSingleApplicationGuard()
d->shutdownInstance(); d->shutdownInstance();
} }
/*!
\property KDSingleApplicationGuard::primaryInstance
Determines wheter this instance is the primary instance.
The primary instance is the first instance which was started or an instance which
got selected by KDSingleApplicationGuard's destructor, when the primary instance was
shut down.
Get this property's value using %isPrimaryInstance(), and monitor changes to it /*!
using becamePrimaryInstance(). * \property KDSingleApplicationGuard::primaryInstance
* Determines wheter this instance is the primary instance.
* The primary instance is the first instance which was started or an instance which
* got selected by KDSingleApplicationGuard's destructor, when the primary instance was
* shut down.
*
* Get this property's value using %isPrimaryInstance(), and monitor changes to it
* using becamePrimaryInstance().
*/ */
bool KDSingleApplicationGuard::isPrimaryInstance() const bool KDSingleApplicationGuard::isPrimaryInstance() const
{ {
@@ -369,12 +374,12 @@ bool KDSingleApplicationGuard::isPrimaryInstance() const
} }
/*! /*!
\property KDSingleApplicationGuard::Policy * \property KDSingleApplicationGuard::Policy
Specifies the policy KDSingleApplicationGuard is using when new instances are started. * Specifies the policy KDSingleApplicationGuard is using when new instances are started.
This can only be set in the primary instance. * This can only be set in the primary instance.
*
Get this property's value using %policy(), set it using %setPolicy(), and monitor changes * Get this property's value using %policy(), set it using %setPolicy(), and monitor changes
to it using policyChanged(). * to it using policyChanged().
*/ */
KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const
{ {
@@ -394,7 +399,7 @@ void KDSingleApplicationGuard::setPolicy( Policy policy )
} }
/*! /*!
Returns a list of all currently running instances. * Returns a list of all currently running instances.
*/ */
QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const
{ {
@@ -409,10 +414,10 @@ QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances(
return result; return result;
} }
/*! /*!
Shuts down all other instances. This can only be called from the * Shuts down all other instances. This can only be called from the
the primary instance. * the primary instance.
Shut down is done gracefully via QCoreApplication::quit(). * Shut down is done gracefully via QCoreApplication::quit().
*/ */
void KDSingleApplicationGuard::shutdownOtherInstances() void KDSingleApplicationGuard::shutdownOtherInstances()
{ {
@@ -426,9 +431,9 @@ void KDSingleApplicationGuard::shutdownOtherInstances()
} }
/*! /*!
Kills all other instances. This can only be called from the * Kills all other instances. This can only be called from the
the primary instance. * the primary instance.
Killing is done via exit(1) * Killing is done via exit(1)
*/ */
void KDSingleApplicationGuard::killOtherInstances() void KDSingleApplicationGuard::killOtherInstances()
{ {
@@ -442,7 +447,7 @@ void KDSingleApplicationGuard::killOtherInstances()
} }
/*! /*!
\reimp * \reimp
*/ */
void KDSingleApplicationGuard::timerEvent( QTimerEvent* event ) void KDSingleApplicationGuard::timerEvent( QTimerEvent* event )
{ {
@@ -456,11 +461,11 @@ void KDSingleApplicationGuard::timerEvent( QTimerEvent* event )
{ {
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i )
{ {
ProcessInfo& info = instances->info[ i ]; ProcessInfo& info = instances->info[ i ];
if( info.command & NewInstance ) if( info.command & NewInstance )
{ {
startedInstances.push_back( Instance( info.arguments(), info.pid ) ); startedInstances.push_back( Instance( info.arguments(), info.pid ) );
info.command &= ~NewInstance; // clear NewInstance flag info.command &= ~NewInstance; // clear NewInstance flag
@@ -472,7 +477,7 @@ void KDSingleApplicationGuard::timerEvent( QTimerEvent* event )
} }
} }
} }
// one signal for every new instance - _after_ the memory segment was unlocked again // one signal for every new instance - _after_ the memory segment was unlocked again
for( QList< Instance >::const_iterator it = startedInstances.begin(); it != startedInstances.end(); ++it ) for( QList< Instance >::const_iterator it = startedInstances.begin(); it != startedInstances.end(); ++it )
emit instanceStarted( *it ); emit instanceStarted( *it );
@@ -495,7 +500,7 @@ void KDSingleApplicationGuard::timerEvent( QTimerEvent* event )
if( instances->info[ d->id ].command & BecomePrimaryCommand ) if( instances->info[ d->id ].command & BecomePrimaryCommand )
{ {
// we became primary! // we became primary!
instances->info[ 0 ] = instances->info[ d->id ]; instances->info[ 0 ] = instances->info[ d->id ];
instances->info[ d->id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free instances->info[ d->id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free
d->id = 0; d->id = 0;
emit becamePrimaryInstance(); emit becamePrimaryInstance();
@@ -510,7 +515,7 @@ void KDSingleApplicationGuard::timerEvent( QTimerEvent* event )
d->id = -1; // becauso our d'tor won't be called anymore d->id = -1; // becauso our d'tor won't be called anymore
} }
} }
if( killOurSelf ) // kill our self takes precedence if( killOurSelf ) // kill our self takes precedence
exit( 1 ); exit( 1 );
else if( shutDownOurSelf ) else if( shutDownOurSelf )
@@ -590,14 +595,14 @@ KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) {
spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) ); spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) );
assertFalse( guard3->isPrimaryInstance() ); assertFalse( guard3->isPrimaryInstance() );
} }
wait( 1000 ); wait( 1000 );
assertEqual( spy3->count(), 1 ); assertEqual( spy3->count(), 1 );
assertEqual( guard3->instances().count(), 1 ); assertEqual( guard3->instances().count(), 1 );
assertTrue( guard3->isPrimaryInstance() ); assertTrue( guard3->isPrimaryInstance() );
assertEqual( guard3->instances().first().arguments, qApp->arguments() ); assertEqual( guard3->instances().first().arguments, qApp->arguments() );
QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) );
QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) ); QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) );
@@ -609,13 +614,13 @@ KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) {
assertEqual( spyStarted.count(), 2 ); assertEqual( spyStarted.count(), 2 );
} }
wait( 1000 ); wait( 1000 );
assertEqual( spyExited.count(), 2 ); assertEqual( spyExited.count(), 2 );
delete spy3; delete spy3;
delete guard3; delete guard3;
} }
#endif // KDTOOLSCORE_UNITTESTS #endif // KDTOOLSCORE_UNITTESTS