~ubuntu-branches/ubuntu/trusty/tomahawk/trusty-proposed

« back to all changes in this revision

Viewing changes to src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp

  • Committer: Package Import Robot
  • Author(s): Harald Sitter
  • Date: 2013-03-07 21:50:13 UTC
  • Revision ID: package-import@ubuntu.com-20130307215013-6gdjkdds7i9uenvs
Tags: upstream-0.6.0+dfsg
ImportĀ upstreamĀ versionĀ 0.6.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "kdsingleapplicationguard.h"
 
2
 
 
3
#if QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)
 
4
#ifndef QT_NO_SHAREDMEMORY
 
5
 
 
6
#include "kdsharedmemorylocker.h"
 
7
#include "kdlockedsharedmemorypointer.h"
 
8
 
 
9
#include <QVector>
 
10
#include <QCoreApplication>
 
11
#include <QSharedMemory>
 
12
#include <QSharedData>
 
13
#include <QBasicTimer>
 
14
#include <QTime>
 
15
 
 
16
#include <algorithm>
 
17
#include <limits>
 
18
#include <cstdlib>
 
19
#include <cstring>
 
20
#include <cassert>
 
21
 
 
22
#ifndef Q_WS_WIN
 
23
#include <csignal>
 
24
#include <unistd.h>
 
25
#endif
 
26
 
 
27
#ifdef Q_WS_WIN
 
28
#include <windows.h>
 
29
#ifndef _SSIZE_T_DEFINED
 
30
typedef signed int ssize_t;
 
31
#endif
 
32
#endif
 
33
 
 
34
using namespace kdtools;
 
35
 
 
36
#ifndef KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS
 
37
#define KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS 10
 
38
#endif
 
39
 
 
40
#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES
 
41
#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 10
 
42
#endif
 
43
 
 
44
#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE
 
45
#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 32768
 
46
#endif
 
47
 
 
48
static unsigned int KDSINGLEAPPLICATIONGUARD_SHM_VERSION = 0;
 
49
 
 
50
Q_GLOBAL_STATIC_WITH_ARGS( int, registerInstanceType,
 
51
                           (qRegisterMetaType<KDSingleApplicationGuard::Instance>()) )
 
52
 
 
53
/*!
 
54
  \class KDSingleApplicationGuard::Instance
 
55
  \relates KDSingleApplicationGuard
 
56
  \ingroup core
 
57
  \brief Information about instances a KDSingleApplicationGuard knows about
 
58
 
 
59
  Instance represents instances of applications under
 
60
  KDSingleApplicationGuard protection, and allows access to their
 
61
  pid() and the arguments() they were started with.
 
62
*/
 
63
 
 
64
class KDSingleApplicationGuard::Instance::Private : public QSharedData {
 
65
    friend class ::KDSingleApplicationGuard::Instance;
 
66
public:
 
67
    Private( const QStringList & args, bool truncated, qint64 pid )
 
68
        : pid( pid ), arguments( args ), truncated( truncated ) {}
 
69
 
 
70
private:
 
71
    qint64 pid;
 
72
    QStringList arguments;
 
73
    bool truncated;
 
74
};
 
75
 
 
76
struct ProcessInfo;
 
77
 
 
78
/*!
 
79
 \internal
 
80
 */
 
81
class KDSingleApplicationGuard::Private
 
82
{
 
83
    friend class ::KDSingleApplicationGuard;
 
84
    friend class ::KDSingleApplicationGuard::Instance;
 
85
    friend struct ::ProcessInfo;
 
86
    KDSingleApplicationGuard * const q;
 
87
public:
 
88
    Private( Policy policy, KDSingleApplicationGuard* qq );
 
89
    ~Private();
 
90
 
 
91
    void create( const QStringList& arguments );
 
92
 
 
93
    bool checkOperational( const char * function, const char * act ) const;
 
94
    bool checkOperationalPrimary( const char * function, const char * act ) const;
 
95
 
 
96
    struct segmentheader
 
97
    {
 
98
        size_t size : 16;
 
99
    };
 
100
 
 
101
    static void sharedmem_free( char* );
 
102
    static char* sharedmem_malloc( size_t size );
 
103
 
 
104
private:
 
105
    void shutdownInstance();
 
106
    void poll();
 
107
 
 
108
private:
 
109
    static KDSingleApplicationGuard* primaryInstance;
 
110
 
 
111
private:
 
112
    QBasicTimer timer;
 
113
    QSharedMemory mem;
 
114
    int id;
 
115
    Policy policy;
 
116
    bool operational;
 
117
    bool exitRequested;
 
118
};
 
119
 
 
120
/*!
 
121
  \internal
 
122
*/
 
123
KDSingleApplicationGuard::Instance::Instance( const QStringList & args, bool truncated, qint64 p )
 
124
    : d( new Private( args, truncated, p ) )
 
125
{
 
126
    d->ref.ref();
 
127
    (void)registerInstanceType();
 
128
}
 
129
 
 
130
/*!
 
131
  Default constructor. Constructs in Instance that is \link isNull()
 
132
  null\endlink.
 
133
 
 
134
  \sa isNull()
 
135
*/
 
136
KDSingleApplicationGuard::Instance::Instance() : d( 0 ) {}
 
137
 
 
138
/*!
 
139
  Copy constructor.
 
140
*/
 
141
KDSingleApplicationGuard::Instance::Instance( const Instance & other )
 
142
    : d( other.d )
 
143
{
 
144
    if ( d )
 
145
        d->ref.ref();
 
146
}
 
147
 
 
148
/*!
 
149
  Destructor.
 
150
*/
 
151
KDSingleApplicationGuard::Instance::~Instance()
 
152
{
 
153
    if ( d && !d->ref.deref() )
 
154
        delete d;
 
155
}
 
156
 
 
157
/*!
 
158
  \fn KDSingleApplicationGuard::Instance::swap( Instance & other )
 
159
 
 
160
  Swaps the contents of this and \a other.
 
161
 
 
162
  This function never throws exceptions.
 
163
*/
 
164
 
 
165
/*!
 
166
  \fn KDSingleApplicationGuard::Instance::operator=( Instance other )
 
167
 
 
168
  Assigns the contents of \a other to this.
 
169
 
 
170
  This function is strongly exception-safe.
 
171
*/
 
172
 
 
173
/*!
 
174
  \fn std::swap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
 
175
  \relates KDSingleApplicationGuard::Instance
 
176
 
 
177
  Specialisation of std::swap() for
 
178
  KDSingleApplicationGuard::Instance. Calls swap().
 
179
*/
 
180
 
 
181
/*!
 
182
  \fn qSwap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs )
 
183
  \relates KDSingleApplicationGuard::Instance
 
184
 
 
185
  Specialisation of qSwap() for
 
186
  KDSingleApplicationGuard::Instance. Calls swap().
 
187
*/
 
188
 
 
189
/*!
 
190
  \fn KDSingleApplicationGuard::Instance::isNull() const
 
191
 
 
192
  Returns whether this instance is null.
 
193
*/
 
194
 
 
195
/*!
 
196
  Returns whether this instance is valid. A valid instance is neither
 
197
  null, nor does it have a negative PID.
 
198
*/
 
199
bool KDSingleApplicationGuard::Instance::isValid() const
 
200
{
 
201
    return d && d->pid >= 0 ;
 
202
}
 
203
 
 
204
/*!
 
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
 
207
  space.
 
208
 
 
209
  \sa arguments()
 
210
*/
 
211
bool KDSingleApplicationGuard::Instance::areArgumentsTruncated() const
 
212
{
 
213
    return d && d->truncated;
 
214
}
 
215
 
 
216
/*!
 
217
  Returns the arguments that this instance was started with.
 
218
 
 
219
  \sa areArgumentsTruncated()
 
220
*/
 
221
const QStringList & KDSingleApplicationGuard::Instance::arguments() const
 
222
{
 
223
    if ( d )
 
224
        return d->arguments;
 
225
    static const QStringList empty;
 
226
    return empty;
 
227
}
 
228
 
 
229
/*!
 
230
  Returns the process-id (PID) of this instance.
 
231
*/
 
232
qint64 KDSingleApplicationGuard::Instance::pid() const
 
233
{
 
234
    if ( d )
 
235
        return d->pid;
 
236
    else
 
237
        return -1;
 
238
}
 
239
 
 
240
/*!
 
241
  \class KDSingleApplicationGuard KDSingleApplicationGuard
 
242
  \ingroup core
 
243
  \brief A guard to protect an application from having several instances.
 
244
 
 
245
  KDSingleApplicationGuard can be used to make sure only one instance of an
 
246
  application is running at the same time.
 
247
 
 
248
  \note As KDSingleApplicationGuard currently uses QSharedMemory, Qt
 
249
  4.4 or later is required.
 
250
 */
 
251
 
 
252
/*!
 
253
  \fn void KDSingleApplicationGuard::instanceStarted(const KDSingleApplicationGuard::Instance & instance)
 
254
 
 
255
  This signal is emitted by the primary instance whenever another
 
256
  instance \a instance started.
 
257
 */
 
258
 
 
259
/*!
 
260
  \fn void KDSingleApplicationGuard::instanceExited(const KDSingleApplicationGuard::Instance & instance)
 
261
 
 
262
  This signal is emitted by the primary instance whenever another
 
263
  instance \a instance exited.
 
264
 */
 
265
 
 
266
/*!
 
267
  \fn void KDSingleApplicationGuard::raiseRequested()
 
268
 
 
269
  This signal is emitted when the current running application is requested
 
270
  to raise its main window.
 
271
*/
 
272
 
 
273
/*!
 
274
  \fn void KDSingleApplicationGuard::exitRequested()
 
275
 
 
276
  This signal is emitted when the current running application has been asked to exit
 
277
  by calling kill on the instance.
 
278
*/
 
279
 
 
280
/*!
 
281
  \fn void KDSingleApplicationGuard::becamePrimaryInstance()
 
282
 
 
283
  This signal is emitted when the current running application becomes
 
284
  the new primary application. The old primary application has quit.
 
285
 */
 
286
 
 
287
/*!
 
288
  \fn void KDSingleApplicationGuard::becameSecondaryInstance()
 
289
 
 
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.
 
293
  */
 
294
 
 
295
/*!
 
296
  \fn void KDSingleApplicationGuard::policyChanged( KDSingleApplicationGuard::Policy policy )
 
297
 
 
298
  This signal is emitted when the #policy of the system changes.
 
299
*/
 
300
 
 
301
enum Command
 
302
{
 
303
    NoCommand = 0x00,
 
304
    ExitedInstance = 0x01,
 
305
    NewInstance = 0x02,
 
306
    FreeInstance = 0x04,
 
307
    ShutDownCommand = 0x08,
 
308
    KillCommand = 0x10,
 
309
    BecomePrimaryCommand = 0x20,
 
310
    RaiseCommand = 0x40
 
311
};
 
312
 
 
313
static const quint16 PrematureEndOfOptions = -1;
 
314
static const quint16 RegularEndOfOptions   = -2;
 
315
 
 
316
struct ProcessInfo
 
317
{
 
318
    static const size_t MarkerSize = sizeof(quint16);
 
319
 
 
320
    explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 )
 
321
        : pid( p ),
 
322
          command( c ),
 
323
          timestamp( 0 ),
 
324
          commandline( 0 )
 
325
    {
 
326
        setArguments( arguments );
 
327
    }
 
328
 
 
329
    void setArguments( const QStringList & arguments );
 
330
    QStringList arguments( bool * prematureEnd  ) const;
 
331
 
 
332
    qint64 pid;
 
333
    quint32 command;
 
334
    quint32 timestamp;
 
335
    char* commandline;
 
336
};
 
337
 
 
338
static inline bool operator==( const ProcessInfo & lhs, const ProcessInfo & rhs )
 
339
{
 
340
    return lhs.command == rhs.command &&
 
341
           ( lhs.commandline == rhs.commandline || ( lhs.commandline != 0 && rhs.commandline != 0 && ::strcmp( lhs.commandline, rhs.commandline ) == 0 ) );
 
342
}
 
343
 
 
344
static inline bool operator!=( const ProcessInfo & lhs, const ProcessInfo & rhs )
 
345
{
 
346
    return !operator==( lhs, rhs );
 
347
}
 
348
 
 
349
/*!
 
350
  This struct contains information about the managed process system.
 
351
  \internal
 
352
 */
 
353
struct InstanceRegister
 
354
{
 
355
    explicit InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy )
 
356
        : policy( policy ),
 
357
          maxInstances( KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ),
 
358
          version( 0 )
 
359
    {
 
360
        std::fill_n( commandLines, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, 0 );
 
361
        ::memcpy( magicCookie, "kdsingleapp", 12 );
 
362
    }
 
363
 
 
364
    /*!
 
365
      Returns whether this register was properly initialized by the first instance.
 
366
      */
 
367
    bool isValid() const
 
368
    {
 
369
        return ::strcmp( magicCookie, "kdsingleapp" ) == 0;
 
370
    }
 
371
 
 
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 ];
 
377
 
 
378
    char commandLines[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ];
 
379
 
 
380
    Q_DISABLE_COPY( InstanceRegister )
 
381
};
 
382
 
 
383
void ProcessInfo::setArguments( const QStringList & arguments )
 
384
{
 
385
    if( commandline != 0 )
 
386
        KDSingleApplicationGuard::Private::sharedmem_free( commandline );
 
387
 
 
388
    commandline = 0;
 
389
    if( arguments.isEmpty() )
 
390
        return;
 
391
 
 
392
    size_t totalsize = MarkerSize;
 
393
    Q_FOREACH( const QString& arg, arguments )
 
394
    {
 
395
        const QByteArray utf8 = arg.toUtf8();
 
396
        totalsize += utf8.size() + MarkerSize;
 
397
    }
 
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 )
 
401
    {
 
402
        qWarning("KDSingleApplicationguard: out of memory when trying to save arguments.\n");
 
403
        return;
 
404
    }
 
405
 
 
406
    char* const commandline = this->commandline + reinterpret_cast<qptrdiff>(reg->commandLines);
 
407
 
 
408
    int argpos = 0;
 
409
    Q_FOREACH( const QString & arg, arguments )
 
410
    {
 
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 );
 
420
            return;
 
421
        } else {
 
422
            const quint16 len16 = utf8.size();
 
423
            // write the size of the data...
 
424
            memcpy( commandline + argpos, &len16, MarkerSize );
 
425
            argpos += MarkerSize;
 
426
            // then the data
 
427
            memcpy( commandline + argpos, utf8.data(), len16 );
 
428
            argpos += len16;
 
429
        }
 
430
    }
 
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;
 
435
}
 
436
 
 
437
QStringList ProcessInfo::arguments( bool * prematureEnd  ) const
 
438
{
 
439
    QStringList result;
 
440
    if( commandline == 0 )
 
441
    {
 
442
        if( prematureEnd )
 
443
            *prematureEnd = true;
 
444
        return result;
 
445
    }
 
446
 
 
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);
 
449
 
 
450
    int argpos = 0;
 
451
    while ( true ) {
 
452
        const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ;
 
453
        assert( available >= 2 );
 
454
 
 
455
        quint16 marker;
 
456
        memcpy( &marker, commandline + argpos, MarkerSize );
 
457
        argpos += MarkerSize;
 
458
 
 
459
        if ( marker == PrematureEndOfOptions ) {
 
460
            if ( prematureEnd ) *prematureEnd = true;
 
461
            break;
 
462
        }
 
463
        if ( marker == RegularEndOfOptions ) {
 
464
            if ( prematureEnd ) *prematureEnd = false;
 
465
            break;
 
466
        }
 
467
 
 
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;
 
473
            break;
 
474
        }
 
475
 
 
476
        result.push_back( QString::fromUtf8( commandline + argpos, marker ) );
 
477
        argpos += marker;
 
478
    }
 
479
 
 
480
    return result;
 
481
}
 
482
 
 
483
KDSingleApplicationGuard::Private::~Private()
 
484
{
 
485
    if( primaryInstance == q )
 
486
        primaryInstance = 0;
 
487
}
 
488
 
 
489
bool KDSingleApplicationGuard::Private::checkOperational( const char * function, const char * act ) const
 
490
{
 
491
    assert( function );
 
492
    assert( act );
 
493
    if ( !operational )
 
494
        qWarning( "KDSingleApplicationGuard::%s: need to be operational to %s", function, act );
 
495
    return operational;
 
496
}
 
497
 
 
498
bool KDSingleApplicationGuard::Private::checkOperationalPrimary( const char * function, const char * act ) const
 
499
{
 
500
    if ( !checkOperational( function, act ) )
 
501
        return false;
 
502
    if ( id != 0 )
 
503
        qWarning( "KDSingleApplicationGuard::%s: need to be primary to %s", function, act );
 
504
    return id == 0;
 
505
}
 
506
 
 
507
struct segmentheader
 
508
{
 
509
    size_t size : 16;
 
510
};
 
511
 
 
512
void KDSingleApplicationGuard::Private::sharedmem_free( char* pointer )
 
513
{
 
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;
 
519
 
 
520
    char* end = heap + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE;
 
521
 
 
522
    std::copy( heap_ptr + size, end, heap_ptr );
 
523
    std::fill( end - size, end, 0 );
 
524
 
 
525
    for( uint i = 0; i < reg->maxInstances; ++i )
 
526
    {
 
527
        if( reg->info[ i ].commandline > pointer )
 
528
            reg->info[ i ].commandline -= size + sizeof( segmentheader );
 
529
    }
 
530
}
 
531
 
 
532
char* KDSingleApplicationGuard::Private::sharedmem_malloc( size_t size )
 
533
{
 
534
    InstanceRegister* const reg = reinterpret_cast<InstanceRegister*>( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() );
 
535
    char* heap = reg->commandLines;
 
536
 
 
537
    while( heap + sizeof( segmentheader ) + size < reg->commandLines + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE )
 
538
    {
 
539
        segmentheader* const header = reinterpret_cast< segmentheader* >( heap );
 
540
        if( header->size == 0 )
 
541
        {
 
542
            header->size = size;
 
543
            return heap + sizeof( segmentheader ) - reinterpret_cast<qptrdiff>(reg->commandLines);
 
544
        }
 
545
        heap += sizeof( header ) + header->size;
 
546
    }
 
547
    return 0;
 
548
}
 
549
 
 
550
void KDSingleApplicationGuard::Private::shutdownInstance()
 
551
{
 
552
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem );
 
553
    instances->info[ q->d->id ].command |= ExitedInstance;
 
554
 
 
555
    if( q->isPrimaryInstance() )
 
556
    {
 
557
        // ohh... we need a new primary instance...
 
558
        for ( int i = 1, end = instances->maxInstances ; i  < end ; ++i )
 
559
        {
 
560
            if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 )
 
561
            {
 
562
                instances->info[ i ].command |= BecomePrimaryCommand;
 
563
                return;
 
564
            }
 
565
        }
 
566
        // none found? then my species is dead :-(
 
567
    }
 
568
}
 
569
 
 
570
KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0;
 
571
 
 
572
/*!
 
573
  Requests that the instance kills itself (by emitting exitRequested).
 
574
 
 
575
  If the instance has since exited, does nothing.
 
576
 
 
577
  \sa shutdown(), raise()
 
578
*/
 
579
void KDSingleApplicationGuard::Instance::kill()
 
580
{
 
581
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
 
582
    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
 
583
    {
 
584
        if( instances->info[ i ].pid != d->pid )
 
585
           continue;
 
586
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 
587
            instances->info[ i ].command = KillCommand;
 
588
    }
 
589
}
 
590
 
 
591
/*!
 
592
  Requests that the instance shuts itself down (by calling QCoreApplication::quit()).
 
593
 
 
594
  If the instance has since exited, does nothing.
 
595
 
 
596
  \sa kill(), raise()
 
597
*/
 
598
void KDSingleApplicationGuard::Instance::shutdown()
 
599
{
 
600
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
 
601
    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
 
602
    {
 
603
        if( instances->info[ i ].pid != d->pid )
 
604
           continue;
 
605
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 
606
            instances->info[ i ].command = ShutDownCommand;
 
607
    }
 
608
}
 
609
 
 
610
/*!
 
611
 
 
612
  Requests that the instance raises its main window.
 
613
 
 
614
  The effects are implementation-defined: the KDSingleApplicationGuard
 
615
  corresponding to the instance will emit its \link
 
616
  KDSingleApplicationGuard::raiseRequested() raiseRequested()\endlink
 
617
  signal.
 
618
 
 
619
  If the instance has since exited, does nothing.
 
620
 
 
621
  \sa kill(), shutdown()
 
622
*/
 
623
void KDSingleApplicationGuard::Instance::raise()
 
624
{
 
625
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem );
 
626
    for ( int i = 0, end = instances->maxInstances ; i < end ; ++i )
 
627
    {
 
628
        if( instances->info[ i ].pid != d->pid )
 
629
           continue;
 
630
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 
631
            instances->info[ i ].command = RaiseCommand;
 
632
    }
 
633
}
 
634
 
 
635
 
 
636
#ifndef Q_WS_WIN
 
637
// static
 
638
void KDSingleApplicationGuard::SIGINT_handler( int sig )
 
639
{
 
640
    if( sig == SIGINT && Private::primaryInstance != 0 )
 
641
        Private::primaryInstance->d->shutdownInstance();
 
642
    ::exit( 1 );
 
643
}
 
644
#endif
 
645
 
 
646
/*!
 
647
  \enum KDSingleApplicationGuard::Policy
 
648
 
 
649
  Defines the policy that a KDSingleApplicationGuard can enforce:
 
650
*/
 
651
 
 
652
/*!
 
653
  \var KDSingleApplicationGuard::NoPolicy
 
654
 
 
655
  instanceStarted() is emitted, and the new instance allowed to continue.
 
656
*/
 
657
 
 
658
/*!
 
659
  \var KDSingleApplicationGuard::AutoKillOtherInstances
 
660
 
 
661
  instanceStarted() is emitted, and the new instance is killed (Instance::kill()).
 
662
*/
 
663
 
 
664
/*!
 
665
  Creates a new KDSingleApplicationGuard with arguments
 
666
  QCoreApplication::arguments() and policy AutoKillOtherInstances,
 
667
  passing \a parent to the base class constructor, as usual.
 
668
*/
 
669
KDSingleApplicationGuard::KDSingleApplicationGuard( QObject * parent )
 
670
    : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
 
671
{
 
672
    d->create( QCoreApplication::arguments() );
 
673
}
 
674
 
 
675
/*!
 
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.
 
679
*/
 
680
KDSingleApplicationGuard::KDSingleApplicationGuard( Policy policy, QObject * parent )
 
681
    : QObject( parent ), d( new Private( policy, this ) )
 
682
{
 
683
    d->create( QCoreApplication::arguments() );
 
684
}
 
685
 
 
686
/*!
 
687
  Creates a new KDSingleApplicationGuard with arguments \a arguments
 
688
  and policy AutoKillOtherInstances, passing \a parent to the base
 
689
  class constructor, as usual.
 
690
*/
 
691
KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, QObject * parent )
 
692
    : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) )
 
693
{
 
694
    d->create( arguments );
 
695
}
 
696
 
 
697
/*!
 
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.
 
701
*/
 
702
KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent )
 
703
    : QObject( parent ), d( new Private( policy, this ) )
 
704
{
 
705
    d->create( arguments );
 
706
}
 
707
 
 
708
KDSingleApplicationGuard::Private::Private( Policy policy_, KDSingleApplicationGuard * qq )
 
709
    : q( qq ),
 
710
      id( -1 ),
 
711
      policy( policy_ ),
 
712
      operational( false ),
 
713
      exitRequested( false )
 
714
{
 
715
}
 
716
 
 
717
void KDSingleApplicationGuard::Private::create( const QStringList & arguments )
 
718
{
 
719
    if ( !QCoreApplication::instance() ) {
 
720
        qWarning( "KDSingleApplicationGuard: you need to construct a Q(Core)Application before you can construct a KDSingleApplicationGuard" );
 
721
        return;
 
722
    }
 
723
 
 
724
    const QString name = QCoreApplication::applicationName();
 
725
    if ( name.isEmpty() ) {
 
726
        qWarning( "KDSingleApplicationGuard: QCoreApplication::applicationName must not be emty" );
 
727
        return;
 
728
    }
 
729
 
 
730
    (void)registerInstanceType();
 
731
    if ( primaryInstance == 0 )
 
732
        primaryInstance = q;
 
733
 
 
734
    mem.setKey( name );
 
735
 
 
736
    // if another instance crashed, the shared memory segment is still there on Unix
 
737
    // the following lines trigger deletion in that case
 
738
#ifndef Q_WS_WIN
 
739
    mem.attach();
 
740
    mem.detach();
 
741
#endif
 
742
 
 
743
    const bool created = mem.create( sizeof( InstanceRegister ) );
 
744
    if( !created )
 
745
    {
 
746
        QString errorMsg;
 
747
        if( mem.error() != QSharedMemory::NoError && mem.error() != QSharedMemory::AlreadyExists )
 
748
            errorMsg += QString::fromLatin1( "QSharedMemomry::create() failed: %1" ).arg( mem.errorString() );
 
749
 
 
750
        if( !mem.attach() )
 
751
        {
 
752
            if( mem.error() != QSharedMemory::NoError )
 
753
                errorMsg += QString::fromLatin1( "QSharedMemomry::attach() failed: %1" ).arg( mem.errorString() );
 
754
 
 
755
            qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." );
 
756
            qWarning( "%s\n",  errorMsg.toLocal8Bit().constData() );
 
757
            return;
 
758
        }
 
759
 
 
760
        const int maxWaitMSecs = 1000 * 60; // stop waiting after 60 seconds
 
761
        QTime waitTimer;
 
762
        waitTimer.start();
 
763
 
 
764
        // lets wait till the other instance initialized the register
 
765
        bool initialized = false;
 
766
        while( !initialized && waitTimer.elapsed() < maxWaitMSecs )
 
767
        {
 
768
            const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
 
769
            initialized = instances->isValid();
 
770
#ifdef Q_WS_WIN
 
771
            ::Sleep(20);
 
772
#else
 
773
            usleep(20000);
 
774
#endif
 
775
        }
 
776
 
 
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 );
 
782
            return;
 
783
        }
 
784
 
 
785
    }
 
786
 
 
787
 
 
788
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
 
789
 
 
790
    if( !created )
 
791
    {
 
792
        assert( instances->isValid() );
 
793
 
 
794
        // we're _not_ the first instance
 
795
        // but the
 
796
        bool killOurSelf = false;
 
797
 
 
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 );
 
804
 
 
805
        // but the signal that we tried to start was sent to the primary application
 
806
        if( killOurSelf )
 
807
            exitRequested = true;
 
808
    }
 
809
    else
 
810
    {
 
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() );
 
816
    }
 
817
 
 
818
#ifndef Q_WS_WIN
 
819
    ::signal( SIGINT, SIGINT_handler );
 
820
#endif
 
821
 
 
822
    // now listen for commands
 
823
    timer.start( 750, q );
 
824
 
 
825
    operational = true;
 
826
}
 
827
 
 
828
/*!
 
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.
 
833
 */
 
834
KDSingleApplicationGuard::~KDSingleApplicationGuard()
 
835
{
 
836
    if( d->id == -1 )
 
837
        return;
 
838
 
 
839
    d->shutdownInstance();
 
840
}
 
841
 
 
842
/*!
 
843
  \property KDSingleApplicationGuard::operational
 
844
 
 
845
  Contains whether this KDSingleApplicationGuard is operational.
 
846
 
 
847
  A non-operational KDSingleApplicationGuard cannot be used in any meaningful way.
 
848
 
 
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
 
852
 
 
853
  Get this property's value using %isOperational().
 
854
*/
 
855
bool KDSingleApplicationGuard::isOperational() const
 
856
{
 
857
    return d->operational;
 
858
}
 
859
 
 
860
/*!
 
861
  \property KDSingleApplicationGuard::exitRequested
 
862
 
 
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
 
865
  this instance().
 
866
 
 
867
  Get this property's value using %isExitRequested().
 
868
*/
 
869
bool KDSingleApplicationGuard::isExitRequested() const
 
870
{
 
871
    return d->exitRequested;
 
872
};
 
873
 
 
874
/*!
 
875
  \property KDSingleApplicationGuard::primaryInstance
 
876
 
 
877
   Contains whether this instance is the primary instance.
 
878
 
 
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
 
881
   shut down.
 
882
 
 
883
   Get this property's value using %isPrimaryInstance(), and monitor changes to it
 
884
   using becamePrimaryInstance().
 
885
 */
 
886
bool KDSingleApplicationGuard::isPrimaryInstance() const
 
887
{
 
888
    return d->id == 0;
 
889
}
 
890
 
 
891
/*!
 
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.
 
895
 
 
896
 Get this property's value using %policy(), set it using %setPolicy(), and monitor changes
 
897
 to it using policyChanged().
 
898
 */
 
899
KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const
 
900
{
 
901
    return d->policy;
 
902
}
 
903
 
 
904
void KDSingleApplicationGuard::setPolicy( Policy policy )
 
905
{
 
906
    if ( !d->checkOperationalPrimary( "setPolicy", "change the policy" ) )
 
907
        return;
 
908
 
 
909
    if( d->policy == policy )
 
910
        return;
 
911
 
 
912
    d->policy = policy;
 
913
    emit policyChanged( policy );
 
914
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
 
915
    instances->policy = policy;
 
916
}
 
917
 
 
918
/*!
 
919
 Returns a list of all currently running instances.
 
920
 */
 
921
QVector<KDSingleApplicationGuard::Instance>
 
922
KDSingleApplicationGuard::instances() const
 
923
{
 
924
    if ( !d->checkOperational( "instances", "report on other instances" ) )
 
925
        return QVector<Instance>();
 
926
 
 
927
    if ( Private::primaryInstance == 0 ) {
 
928
        Private::primaryInstance = const_cast<KDSingleApplicationGuard*>( this );
 
929
    }
 
930
 
 
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 )
 
934
    {
 
935
        const ProcessInfo& info = instances->info[ i ];
 
936
        if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 )
 
937
        {
 
938
            bool truncated;
 
939
            const QStringList arguments = info.arguments( &truncated );
 
940
            result.push_back( Instance( arguments, truncated, info.pid ) );
 
941
        }
 
942
    }
 
943
    return result;
 
944
}
 
945
 
 
946
/*!
 
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().
 
950
 */
 
951
void KDSingleApplicationGuard::shutdownOtherInstances()
 
952
{
 
953
    if ( !d->checkOperationalPrimary( "shutdownOtherInstances", "shut other instances down" ) )
 
954
        return;
 
955
 
 
956
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
 
957
    for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
 
958
    {
 
959
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 
960
            instances->info[ i ].command = ShutDownCommand;
 
961
    }
 
962
}
 
963
 
 
964
/*!
 
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.
 
969
 */
 
970
void KDSingleApplicationGuard::killOtherInstances()
 
971
{
 
972
    if ( !d->checkOperationalPrimary( "killOtherInstances", "kill other instances" ) )
 
973
        return;
 
974
 
 
975
    KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
 
976
    for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
 
977
    {
 
978
        if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
 
979
            instances->info[ i ].command = KillCommand;
 
980
    }
 
981
}
 
982
 
 
983
bool KDSingleApplicationGuard::event( QEvent * event )
 
984
{
 
985
    if ( event->type() == QEvent::Timer ) {
 
986
        const QTimerEvent * const te = static_cast<QTimerEvent*>( event );
 
987
        if ( te->timerId() == d->timer.timerId() ) {
 
988
            d->poll();
 
989
            return true;
 
990
        }
 
991
    }
 
992
    return QObject::event( event );
 
993
}
 
994
 
 
995
void KDSingleApplicationGuard::Private::poll() {
 
996
 
 
997
    const quint32 now = QDateTime::currentDateTime().toTime_t();
 
998
 
 
999
    if ( primaryInstance == 0 ) {
 
1000
        primaryInstance = q;
 
1001
    }
 
1002
 
 
1003
    if ( q->isPrimaryInstance() )
 
1004
    {
 
1005
        // only the primary instance will get notified about new instances
 
1006
        QVector< Instance > exitedInstances;
 
1007
        QVector< Instance > startedInstances;
 
1008
 
 
1009
        {
 
1010
            KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
 
1011
 
 
1012
            if( instances->info[ id ].pid != QCoreApplication::applicationPid() )
 
1013
            {
 
1014
                for ( int i = 1, end = instances->maxInstances ; i < end && id == 0 ; ++i )
 
1015
                {
 
1016
                    if( instances->info[ i ].pid == QCoreApplication::applicationPid() )
 
1017
                        id = i;
 
1018
                }
 
1019
                emit q->becameSecondaryInstance();
 
1020
                return;
 
1021
            }
 
1022
 
 
1023
            instances->info[ id ].timestamp = now;
 
1024
 
 
1025
            for ( int i = 1, end = instances->maxInstances ; i < end ; ++i )
 
1026
            {
 
1027
                ProcessInfo& info = instances->info[ i ];
 
1028
                if( info.command & NewInstance )
 
1029
                {
 
1030
                    bool truncated;
 
1031
                    const QStringList arguments = info.arguments( &truncated );
 
1032
                    startedInstances.push_back( Instance( arguments, truncated, info.pid ) );
 
1033
                    info.command &= ~NewInstance;  // clear NewInstance flag
 
1034
                }
 
1035
                if( info.command & ExitedInstance )
 
1036
                {
 
1037
                    bool truncated;
 
1038
                    const QStringList arguments = info.arguments( &truncated );
 
1039
                    exitedInstances.push_back( Instance( arguments, truncated, info.pid ) );
 
1040
                    info.command = FreeInstance;   // set FreeInstance flag
 
1041
                }
 
1042
            }
 
1043
        }
 
1044
 
 
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 );
 
1050
    }
 
1051
    else
 
1052
    {
 
1053
        // do we have a command?
 
1054
        bool killOurSelf = false;
 
1055
        bool shutDownOurSelf = false;
 
1056
        bool policyDidChange = false;
 
1057
 
 
1058
        {
 
1059
            KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem );
 
1060
 
 
1061
            const Policy oldPolicy = policy;
 
1062
            policy = static_cast<Policy>( instances->policy );
 
1063
            policyDidChange = policy != oldPolicy;
 
1064
 
 
1065
            // check for the primary instance health status
 
1066
            if( now - instances->info[ 0 ].timestamp > KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS )
 
1067
            {
 
1068
                std::swap( instances->info[ 0 ], instances->info[ id ] );
 
1069
                id = 0;
 
1070
                instances->info[ id ].timestamp = now;
 
1071
                emit q->becamePrimaryInstance();
 
1072
                instances->info[ id ].command &= ~BecomePrimaryCommand;  // afterwards, reset the flag
 
1073
            }
 
1074
 
 
1075
            if( instances->info[ id ].command & BecomePrimaryCommand )
 
1076
            {
 
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
 
1080
                id = 0;
 
1081
                instances->info[ id ].timestamp = now;
 
1082
                emit q->becamePrimaryInstance();
 
1083
            }
 
1084
 
 
1085
            if( instances->info[ id ].command & RaiseCommand )
 
1086
            {
 
1087
               // raise ourself!
 
1088
               emit q->raiseRequested();
 
1089
               instances->info[ id ].command &= ~RaiseCommand;  // afterwards, reset the flag
 
1090
            }
 
1091
 
 
1092
 
 
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
 
1096
            if( killOurSelf )
 
1097
            {
 
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
 
1100
            }
 
1101
        }
 
1102
 
 
1103
        if( killOurSelf )  // kill our self takes precedence
 
1104
        {
 
1105
            exitRequested = true;
 
1106
            emit q->exitRequested();
 
1107
        }
 
1108
        else if( shutDownOurSelf )
 
1109
            qApp->quit();
 
1110
        else if( policyDidChange )
 
1111
            emit q->policyChanged( policy );
 
1112
    }
 
1113
}
 
1114
 
 
1115
#include "moc_kdsingleapplicationguard.cpp"
 
1116
 
 
1117
#ifdef KDTOOLSCORE_UNITTESTS
 
1118
 
 
1119
#include <kdunittest/test.h>
 
1120
 
 
1121
#include "kdautopointer.h"
 
1122
 
 
1123
#include <iostream>
 
1124
 
 
1125
#include <QtCore/QTime>
 
1126
#include <QtCore/QUuid>
 
1127
#include <QtTest/QSignalSpy>
 
1128
 
 
1129
static void wait( int msec, QSignalSpy * spy=0, int expectedCount=INT_MAX )
 
1130
{
 
1131
    QTime t;
 
1132
    t.start();
 
1133
    while ( ( !spy || spy->count() < expectedCount ) && t.elapsed() < msec )
 
1134
    {
 
1135
        qApp->processEvents( QEventLoop::WaitForMoreEvents, qMax( 10, msec - t.elapsed() ) );
 
1136
    }
 
1137
}
 
1138
 
 
1139
static std::ostream& operator<<( std::ostream& stream, const QStringList& list )
 
1140
{
 
1141
    stream << "QStringList(";
 
1142
    for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
 
1143
    {
 
1144
        stream << " " << it->toLocal8Bit().data();
 
1145
        if( it + 1 != list.end() )
 
1146
            stream << ",";
 
1147
    }
 
1148
    stream << " )";
 
1149
    return stream;
 
1150
}
 
1151
 
 
1152
namespace {
 
1153
    class ApplicationNameSaver {
 
1154
        Q_DISABLE_COPY( ApplicationNameSaver )
 
1155
        const QString oldname;
 
1156
    public:
 
1157
        explicit ApplicationNameSaver( const QString & name )
 
1158
            : oldname( QCoreApplication::applicationName() )
 
1159
        {
 
1160
            QCoreApplication::setApplicationName( name );
 
1161
        }
 
1162
        ~ApplicationNameSaver() {
 
1163
            QCoreApplication::setApplicationName( oldname );
 
1164
        }
 
1165
    };
 
1166
}
 
1167
 
 
1168
KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) {
 
1169
 
 
1170
    // set it to an unique name
 
1171
    const ApplicationNameSaver saver( QUuid::createUuid().toString() );
 
1172
 
 
1173
    KDAutoPointer<KDSingleApplicationGuard> guard3;
 
1174
    KDAutoPointer<QSignalSpy> spy3;
 
1175
    KDAutoPointer<QSignalSpy> spy4;
 
1176
 
 
1177
    {
 
1178
        KDSingleApplicationGuard guard1;
 
1179
        assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances );
 
1180
        assertEqual( guard1.instances().count(), 1 );
 
1181
        assertTrue( guard1.isPrimaryInstance() );
 
1182
 
 
1183
        guard1.setPolicy( KDSingleApplicationGuard::NoPolicy );
 
1184
        assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy );
 
1185
 
 
1186
        QSignalSpy spy1( &guard1, SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );
 
1187
 
 
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() );
 
1193
 
 
1194
        wait( 1000, &spy1, 1 );
 
1195
 
 
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() );
 
1201
    }
 
1202
 
 
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 );
 
1209
 
 
1210
    assertEqual( guard3->instances().first().arguments(), qApp->arguments() );
 
1211
 
 
1212
    QSignalSpy spyStarted( guard3.get(), SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) );
 
1213
    QSignalSpy spyExited(  guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance)) );
 
1214
 
 
1215
    {
 
1216
        KDSingleApplicationGuard guard1;
 
1217
        KDSingleApplicationGuard guard2;
 
1218
 
 
1219
        wait( 1000, &spyStarted, 2 );
 
1220
 
 
1221
        assertEqual( spyStarted.count(), 2 );
 
1222
    }
 
1223
 
 
1224
    wait( 1000, &spyExited, 2 );
 
1225
    assertEqual( spyExited.count(), 2 );
 
1226
 
 
1227
    spyStarted.clear();
 
1228
    spyExited.clear();
 
1229
 
 
1230
    {
 
1231
        // check arguments-too-long handling:
 
1232
        QStringList args;
 
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 );
 
1236
 
 
1237
        wait( 1000, &spyStarted, 1 );
 
1238
 
 
1239
        const QVector<KDSingleApplicationGuard::Instance> instances = guard3.instances();
 
1240
        assertEqual( instances.size(), 2 );
 
1241
 
 
1242
        assertTrue( instances[1].areArgumentsTruncated() );
 
1243
    }
 
1244
}
 
1245
 
 
1246
#endif // KDTOOLSCORE_UNITTESTS
 
1247
 
 
1248
#endif // QT_NO_SHAREDMEMORY
 
1249
#endif // QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN)