49

QSingleApplication? QMutex? QSharedMemory? I'm looking for something that will work smoothly in Windows, OSX and Linux (Ubuntu). Using Qt 4.7.1

Nejat
  • 31,784
  • 12
  • 106
  • 138
JasonGenX
  • 4,952
  • 27
  • 106
  • 198
  • http://stackoverflow.com/questions/783212/find-qwidget-of-single-instance-qt-application – Martin Beckett Feb 15 '11 at 16:50
  • the links there go to some "mobility" API with camera, roaming etc functions. Are you sure this is where you obtain QtSingleApplication? – JasonGenX Feb 15 '11 at 17:21
  • @The link in the answer is correct http://doc.trolltech.com/solutions/4/qtsingleapplication/qtsingleapplication.html, the QtSingleApplication link in the question is wrong. – Martin Beckett Feb 15 '11 at 17:37

7 Answers7

91

Simple solution, that does what you want. Without network dependency (as QtSingleApplication) and without any overhead.

Usage:

int main()
{
    RunGuard guard( "some_random_key" );
    if ( !guard.tryToRun() )
        return 0;

    QAppplication a(/*...*/);
    // ...
}

RunGuard.h

#ifndef RUNGUARD_H
#define RUNGUARD_H

#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>


class RunGuard
{

public:
    RunGuard( const QString& key );
    ~RunGuard();

    bool isAnotherRunning();
    bool tryToRun();
    void release();

private:
    const QString key;
    const QString memLockKey;
    const QString sharedmemKey;

    QSharedMemory sharedMem;
    QSystemSemaphore memLock;

    Q_DISABLE_COPY( RunGuard )
};


#endif // RUNGUARD_H

RunGuard.cpp

#include "RunGuard.h"

#include <QCryptographicHash>


namespace
{

QString generateKeyHash( const QString& key, const QString& salt )
{
    QByteArray data;

    data.append( key.toUtf8() );
    data.append( salt.toUtf8() );
    data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();

    return data;
}

}


RunGuard::RunGuard( const QString& key )
    : key( key )
    , memLockKey( generateKeyHash( key, "_memLockKey" ) )
    , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
    , sharedMem( sharedmemKey )
    , memLock( memLockKey, 1 )
{
    memLock.acquire();
    {
        QSharedMemory fix( sharedmemKey );    // Fix for *nix: http://habrahabr.ru/post/173281/
        fix.attach();
    }
    memLock.release();
}

RunGuard::~RunGuard()
{
    release();
}

bool RunGuard::isAnotherRunning()
{
    if ( sharedMem.isAttached() )
        return false;

    memLock.acquire();
    const bool isRunning = sharedMem.attach();
    if ( isRunning )
        sharedMem.detach();
    memLock.release();

    return isRunning;
}

bool RunGuard::tryToRun()
{
    if ( isAnotherRunning() )   // Extra check
        return false;

    memLock.acquire();
    const bool result = sharedMem.create( sizeof( quint64 ) );
    memLock.release();
    if ( !result )
    {
        release();
        return false;
    }

    return true;
}

void RunGuard::release()
{
    memLock.acquire();
    if ( sharedMem.isAttached() )
        sharedMem.detach();
    memLock.release();
}
Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61
  • 3
    This works for me, if the application crashes, it will allow to restart(that was my problem), thanks @SaZ – Haris Feb 18 '15 at 11:41
  • 3
    @SaZ thanks for posting this code - I just tried it in my app - it works first time :) – Michael Vincent May 13 '15 at 09:20
  • 2
    Wow. Worked perfectly! Thanks so much :) Just had to include RunGuard.h in main. – mrg95 Sep 01 '15 at 06:39
  • Is it also possible to communicate something to the other instance running? For example when the user tries to open a file but another instance of the application is already running, then I want that file to be opened by the existing instance. And what's that `fix` for in the constructor? – bweber Oct 24 '15 at 08:00
  • 3
    Another question: couldn't you just use `QSharedMemory::lock()` instead of a separate `QSystemSemaphore`? – bweber Oct 24 '15 at 08:36
  • 1
    @user1488118 1) you are talking about IPC, it is not related to current question. `QtSingleApplication` can communicate. Or you can create your own mechanism, based on `QLocalSocket`. 2) No, `QSharedMemory::lock()` is not enough, but it requeres a long topic, to describe, why. – Dmitry Sazonov Oct 26 '15 at 09:01
  • Awesome, what license does your snippet? Am I free to use it in my GPLv2 thingy with this header: /* Author: Dmitry Sazonov * StackOverflow: http://stackoverflow.com/a/28172162/882600 * Used with permission from original author. * / ?? – Tomas Pruzina Aug 15 '16 at 00:54
  • 8
    @AoeAoe it's a code sample, there are no restrictions. Feel free to use it even without any remarks. – Dmitry Sazonov Aug 15 '16 at 07:05
  • There is `LNK4042: object specified more than once; extras ignored` warning in `runguard.obj`. Any idea why and how to remove it? – Mustafa Chelik Sep 07 '16 at 19:01
  • @MustafaChelik it's a linker warning. It couldn't be related to .obj file. Use verbose linker output for deep diagnostic. – Dmitry Sazonov Sep 07 '16 at 22:29
  • You are right Dmitry. After `run qmake` and rebuild the warning went. Thank you. – Mustafa Chelik Sep 08 '16 at 06:24
  • Why exactly are you using a hash for the keys? – xxmicloxx Jan 01 '17 at 21:39
  • @xxmicloxx it doesn't matter what to use. I use it for preventing possible glitches with other global kernel objects – Dmitry Sazonov Jan 01 '17 at 22:22
  • 1
    Some have asked (and I wondered) why not just use QSharedMemory::lock() and not bother with the system semaphore. I think it has to do with handling the fact that there could be multiple users on the system. The shared memory is available to all users so we need the system semaphore to ensure only one user accesses it at a time. This article explains it fairly well: http://blog.aeguana.com/2015/10/15/how-to-run-a-single-app-instance-in-qt/ – sidewinderguy Jun 07 '17 at 00:56
  • not working for me with Windows 10 and Qt 5.5. it blocks multiple instances with the same Windows login, but not with multiple Windows logins. – Patrick Parker Apr 15 '19 at 20:49
  • @PatrickParker I think that it is not possible to answer your question with a cross-platform solutions or with Qt only code. I can recommend you to use platform-specific things. There is a link to an article (russian) that will solve your question: [RSDN](https://rsdn.org/article/baseserv/avins.xml). You can take code samples from here or use a translator (article is very simple). Or you may ask your question with "WinAPI" tag. – Dmitry Sazonov Apr 15 '19 at 21:39
  • @DmitrySazonov thanks for the link. I considered using a Global Windows Mutex, in the end I went with a CreateFile lock similar to the one here: https://stackoverflow.com/a/18184226/7098259 since I only care about preventing instances which would write to the same resource folder. – Patrick Parker Apr 16 '19 at 23:14
  • @PatrickParker [FYI](https://doc.qt.io/qt-5/qlockfile.html). But be careful with situations when application may crash and lock file may stay alive. – Dmitry Sazonov Apr 17 '19 at 08:27
  • Too bad QT doesn't have a reliable implementation for the QLockFile: it's somewhat useless if you need to hold it indefinitely, without staleness checks; because it easily gets orphaned if the program exits abnormally. Handles from CreateFile at least are reliably closed by the OS. – Patrick Parker Apr 17 '19 at 14:33
  • @PatrickParker I think that is it not an issue of Qt. It is very difficult to implement such stuff for all platforms that are supported by Qt. So in your case it's better to use platform-specific code. – Dmitry Sazonov Apr 18 '19 at 09:42
  • Is it required to hash the keys? For me it works even without hashing them or am I missing something? – Mahesh Jagtap Apr 14 '20 at 10:57
  • @MaheshJagtap no, it doesn't. You can generate any string. But the best way is to have unique string. If another application will use kernel object with same name - you can't start your app. It will think that it's already started. Anyway, hashing is done only once, it should not affect performance at all. Resources to start a process is much higher. – Dmitry Sazonov Apr 14 '20 at 17:45
  • 2
    This is an awesome implementation! Thanks so much, just what I needed! – Lorenzo_g Aug 02 '20 at 21:16
  • Not worked on Win10 + Qt 5.15.2 – – Vincent Sit Nov 08 '21 at 01:43
  • @VincentSit could you provide some additional debug information? – Dmitry Sazonov Nov 08 '21 at 15:38
  • 1
    @Dmitry Sazonov It's working. It's my problem. I passed in a different key each time. – Vincent Sit Nov 09 '21 at 03:06
9

As QtSingleApplication is relatively obsolete and unmaintained anymore, I wrote a replacement, called SingleApplication.

It is based on QSharedMemory and uses a QLocalServer to notify the parent process of the new instance being spawn. It works on all platforms and is compatible supports Qt 5 and Qt 6.

The full code and documentation are available here.

Basic Example:

int main(int argc, char *argv[])
{
    SingleApplication app( argc, argv );

    return app.exec();
}

Advanced Example

Among other things it supports sending messages between the newly spawned instance and the primary instance, for example:

int main(int argc, char *argv[])
{
    SingleApplication app( argc, argv, true );

    if( app.isSecondary() ) {
        app.sendMessage( app.arguments().join(' ')).toUtf8() );
        app.exit( 0 );
    }

    return app.exec();
}
Itay Grudev
  • 7,055
  • 4
  • 54
  • 86
  • Thanks for posting this. Would you be open to a pull request that adds a "command" to be sent with the connection? I would like to optionally execute a command like "open file". – Sohail Aug 03 '16 at 00:04
  • `signal( SIGILL, SingleApplicationPrivate::terminate ); // 4` LOL, did you even try this? I suppose no. This thing doesn't work. – LtWorf Aug 09 '16 at 06:13
  • @LtWorf What exactly is the problem here? – Itay Grudev Aug 09 '16 at 10:46
  • Ah sorry I misread it as SIGKILL. Anyway it's broken because in case of sigkill it doesn't do any cleanup. – LtWorf Aug 09 '16 at 12:23
  • 1
    I know that I cannot handle `SIGKILL`, but I used another hack to handle that case. By initializing and then explicitly deleting the `QSharedMemory` instance the kernel clears the block if there aren't any active processes attached to it. – Itay Grudev Aug 09 '16 at 15:45
  • https://github.com/itay-grudev/SingleApplication/blob/master/singleapplication.cpp#L215 – Itay Grudev Aug 09 '16 at 15:47
2

You can use QSharedMemory with a specific key and check if the shared memory with that key could be created or not. If it is nor able to create it, then an instance is already run:

QSharedMemory sharedMemory;
sharedMemory.setKey("MyApplicationKey");

if (!sharedMemory.create(1))
{
    QMessageBox::warning(this, tr("Warning!"), tr("An instance of this application is running!") );

    exit(0); // Exit already a process running
}
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Nejat
  • 31,784
  • 12
  • 106
  • 138
1

for Windows:

HANDLE g_app_mutex = NULL;

bool check_one_app_instance()
{
    g_app_mutex = ::CreateMutex(NULL, FALSE, L"8BD290769B404A7816985M9E505CF9AD64"); // this any different key as string
    if(GetLastError() == ERROR_ALREADY_EXISTS)
    {
        CloseHandle(g_app_mutex);
        return false;
    }

    return true;
}
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
andreyDev
  • 21
  • 1
0

I am using this solution for now.

However it has the drawback that the program can only be run once by the user, even if they login from multiple locations at the same time.

singleinstance.h

#ifndef SINGLEINSTANCE_H
#define SINGLEINSTANCE_H

typedef enum {
    SYSTEM,
    SESSION,
} scope_t;

class SingleInstance
{
public:
    static bool unique(QString key, scope_t scope);
};

#endif // SINGLEINSTANCE_H

singleinstance.cpp

#include <QLockFile>
#include <QProcessEnvironment>

#include "singleinstance.h"

/**
 * @brief filename
 * @param key
 * @param scope
 * @return a fully qualified filename
 *
 * Generates an appropriate filename for the lock
 */
static QString filename(QString key, scope_t scope) {

    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    QString tmp = env.value("TEMP", "/tmp") + "/";
    QString user = env.value("USER", "alfio");


    QString r;                                                                                                                                                                         
    switch (scope) {                                                                                                                                                                   
        case SYSTEM:                                                                                                                                                                   
            r = tmp;                                                                                                                                                                   
            break;
        case SESSION:
            //FIXME this will prevent trabucco to run in multiple X11 sessions
            r = env.value("XDG_RUNTIME_DIR", tmp + user) + "/";
            break;
    }
    return r + key + ".lock";
}

/**
 * @brief SingleInstance::unique
 * @param key the unique name of the program
 * @param scope wether it needs to be system-wide or session-wide
 * @return true if this is the only instance
 *
 * Make sure that this instance is unique.
 */
bool SingleInstance::unique(QString key, scope_t scope) {
    QLockFile* lock = new QLockFile(filename(key, scope));
    bool r = lock->tryLock();
    if (!r)
        delete lock;
    return r;
}
LtWorf
  • 7,286
  • 6
  • 31
  • 45
  • What you will do if you have no write access to temp folder? Also it is worse for performance. – Dmitry Sazonov Aug 24 '16 at 07:36
  • In a machine that works properly, you do have access to that. If you have no /tmp, you can't even do a graphical login. Performances hardly matter since it's a check done once when starting the program. – LtWorf Aug 24 '16 at 07:50
  • It depends on your platform. I created an applications for Windows XP embedded with very restricted access. There where only one writable folder for application needs and path to this folder was provided in runtime. So checking for locked files was very expensive operation. – Dmitry Sazonov Aug 24 '16 at 08:42
  • 1
    who is the owner of QLockFile* lock? it appears to be a dangling resource – Patrick Parker Apr 16 '19 at 15:56
0

for linux:

//----------------------------------

QProcess *m_prSystemCall;
m_prSystemCall = new QProcess();

QString Commnd = "pgrep  " + qApp->applicationDisplayName();
m_prSystemCall->start(Commnd);
m_prSystemCall->waitForFinished(8000);
QString output(m_prSystemCall->readAllStandardOutput());
QStringList AppList = output.split("\n", QString::SkipEmptyParts);
qDebug() <<"pgrep out:"<<AppList;
for(int i=0;i<AppList.size()-1;i++)
{
    Commnd = "kill " + AppList.at(i);
    m_prSystemCall->start(Commnd);
    m_prSystemCall->waitForFinished(8000);
}

//-------------------------------------------------------

and for Windows:

#include <tlhelp32.h>
#include <comdef.h>

QString pName = qApp->applicationDisplayName();
pName += ".exe";
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

if (Process32First(snapshot, &entry) == TRUE)
{
    DWORD myPID =  GetCurrentProcessId();
    while (Process32Next(snapshot, &entry) == TRUE)
    {
        const WCHAR* wc = entry.szExeFile ;
        _bstr_t b(wc);
        const char* c = b;

        if (stricmp(c, pName.toStdString().c_str()) == 0)
        {
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);

            qDebug() <<"myPID: "<< myPID << "entry.th32ProcessID" << entry.th32ProcessID;
            if(myPID != entry.th32ProcessID)
                TerminateProcess(hProcess,0);
            QThread::msleep(10);
            CloseHandle(hProcess);
        }
    }

}

CloseHandle(snapshot);
aminM
  • 51
  • 7
-1

According to Qt's doc, an acquired QSystemSemaphore won't be automatically released if the process crashes without calling its destructor under Unix-like OSes. Which could be the cause of a deadlock in another process trying to acquire the same semaphore. If you want to be 100% sure that your program properly handles crashes and if you don't insist on using Qt, you may want to use the other locking mechanisms which the operating systems do automatically release when the process dies - for example, lockf() and the O_EXLOCK flag passed to open() which are mentioned in How do I recover a semaphore when the process that decremented it to zero crashes? or flock(). In fact, creating of shared memory is no longer needed if flock() is used. Simply using flock() is enough to make single instance app protection.

If recovering semaphore from crashes in Unix doesn't matter, I think that RunGuard from Dmitry Sazonov's answer could still be somewhat simplified:

  1. The destructor ~RunGuard() and RunGuard::release() may be taken off since QSharedMemory will automatically detach from the shared memory segment upon its destruction, as in Qt's doc for QSharedMemory::~QSharedMemory(): "The destructor clears the key, which forces the shared memory object to detach from its underlying shared memory segment.".

  2. RunGuard::isAnotherRunning() may also be taken off, too. The goal is exclusive execution. As @Nejat has mentioned, we can merely take advantage of the fact there could be at most one shared memory segment being created for a given key at any time, as in Qt's doc for QSharedMemory::create(): "If a shared memory segment identified by the key already exists, the attach operation is not performed and false is returned."

  3. If I understand correctly, the purpose of "fix" QSharedMemory object in the constructor is to destroy the shared memory segment which survives due to the previous process crash, as in Qt's doc: "Unix: ... When the last thread or process that has an instance of QSharedMemory attached to a particular shared memory segment detaches from the segment by destroying its instance of QSharedMemory, the Unix kernel release the shared memory segment. But if that last thread or process crashes without running the QSharedMemory destructor, the shared memory segment survives the crash.". When "fix" gets destructed, an implicit detach() should be called by its destructor and the surviving shared memory segment, if any, will be released.

  4. Not sure if QSharedMemory is thread-safe/process-safe or not. Otherwise, the code related to memLock may be further removed if thread-safety is handled internally by QSharedMemory. On the other hand, fix should also be protected by memLock if the safety is an issue:

    RunGuard::RunGuard( const QString& key )
        : key( key )
        , memLockKey( generateKeyHash( key, "_memLockKey" ) )
        , sharedMemKey( generateKeyHash( key, "_sharedMemKey" ) )
        , sharedMem( sharedMemKey )
        , memLock( memLockKey, 1 )
    {
        memLock.acquire();
        {
            QSharedMemory fix( sharedMemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
            fix.attach();
        }
        memLock.release();
    }
    

    because an explicit attach() and an implicit detach() are called around fix.

  5. The simplified version of RunGuard is as follows:

    Usage:

    int main()
    {
        RunGuard guard( "some_random_key" );
        if ( !guard.tryToRun() )
            return 0;
    
        QAppplication a(/*...*/);
        // ...
    }
    

    runGuard.h:

    #ifndef RUNGUARD_H
    #define RUNGUARD_H
    
    #include <QObject>
    #include <QSharedMemory>
    #include <QSystemSemaphore>
    
    class RunGuard
    {
    
    public:
        RunGuard( const QString& key );
        bool tryToRun();
    
    private:
        const QString key;
        const QString memLockKey;
        const QString sharedMemKey;
    
        QSharedMemory sharedMem;
        QSystemSemaphore memLock;
    
        Q_DISABLE_COPY( RunGuard )
    };
    
    
    #endif // RUNGUARD_H
    

    runGuard.cpp:

    #include "runGuard.h"
    #include <QCryptographicHash>
    
    namespace
    {
    
        QString generateKeyHash( const QString& key, const QString& salt )
        {
            QByteArray data;
            data.append( key.toUtf8() );
            data.append( salt.toUtf8() );
            data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
            return data;
    }
    
    }
    
    RunGuard::RunGuard( const QString& key )
        : key( key )
        , memLockKey( generateKeyHash( key, "_memLockKey" ) )
        , sharedMemKey( generateKeyHash( key, "_sharedMemKey" ) )
        , sharedMem( sharedMemKey )
        , memLock( memLockKey, 1 )
    {
        QSharedMemory fix( sharedMemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
        fix.attach();
    }
    
    bool RunGuard::tryToRun()
    {
        memLock.acquire();
        const bool result = sharedMem.create( sizeof( quint64 ) );
        memLock.release();
        if ( !result )
            return false;
    
        return true;
    }
    
  6. There is a possible race condition here:

    bool RunGuard::tryToRun()
    {
        if ( isAnotherRunning() )   // Extra check
            return false;
                                                                   // (tag1)
        memLock.acquire();
        const bool result = sharedMem.create( sizeof( quint64 ) ); // (tag2)
        memLock.release();
        if ( !result )
        {
            release();                                             // (tag3)
            return false;
        }
    
        return true;
    }
    

    Consider the scenario:

    When the current process ProcCur runs to (tag1) the following happens: (note that (tag1) is outside of lock protection)

    1. Another process ProcOther using RunGuard starts to run.
    2. ProcOther runs to (tag2) and successfully creates the shared memory.
    3. ProcOther crashes before it calls release() at (tag3).
    4. ProcCur continues running from (tag1).
    5. ProcCur runs to (tag2) and attempts to create shared memory. However, sharedMem.create() will return false because ProcOther have left a created one. As we can see in the doc of QSharedMemory::create(): "If a shared memory segment identified by the key already exists, the attach operation is not performed and false is returned."
    6. Finally, RunGuard::tryToRun() in ProcCur will return false, which is not as expected because ProcCur is the only existing process using RunGuard.
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Justin
  • 169
  • 5
  • 1. It is nesessary in case, when you want to do some extra logic and when you need to release RunGuard directly. 2. This check may be used for other instances, if IPC is required. 3. Yes, this is a fix for *nix for your case. 4. `QSystemSemaphore` is used as cross-process "mutex". It is necessary to protect from some race conditions. It is not a "waiter". RunGuard is not thread-safe, because thread safety is not necessary here. But RunGuard must be proccess safe. – Dmitry Sazonov Oct 28 '15 at 12:24
  • It is still unknown if QSharedMemory is process-safe/thread-safe or not. So protecting the member function calls of QSharedMemory by QSystemSemaphore could be trivial. There should be documents somewhere mentioned that QSharedMemory is not process-safe/thread-safe, but I cannot find out any of such documents. Hence, I said "memLock ***MAY*** be further removed". – Justin Oct 28 '15 at 15:02
  • Agree with protection of "fix". But I think that you have some misunderstanding what is process-safety and thread-safety. It is not necessary to care about thread safety in RunGuard at all. `memLock` is used to prevend some races. – Dmitry Sazonov Oct 28 '15 at 15:26
  • By the way, "fix" should have been protected by "memLock" too if QSharedMemory had not been process-safe/thread-safe. As I have modified 4. above. – Justin Oct 28 '15 at 15:32
  • According to [this](http://doc.qt.io/qt-4.8/threads-reentrancy.html): "By extension, a class is said to be _reentrant_ if its member functions can be called safely from multiple threads, as long as each thread uses a _different_ instance of the class. The class is _thread-safe_ if its member functions can be called safely from multiple threads, even if all the threads use the _same_ instance of the class." If RunGuard is intended to be used only once in each app, the QSharedMemory object of it is the only instance which is different from that of all the other RunGuard's in the other apps. – Justin Mar 13 '16 at 13:13
  • 1
    you wrong. You are saying, that there are no difference between thread-unsafe and reentrant class. In Qt, `reentrant` means that you may use one instance in different threads, but you should guard access to object with some guards. You need to improve your skills in understanding multithreading and in understanding difference between thread and process. – Dmitry Sazonov Mar 13 '16 at 14:17
  • 1
    Btw, you need to read documentation carefully. Read in once again: "A reentrant function can also be called simultaneously from multiple threads, but only if each invocation uses its own data." There are nothing told about instances. Only about invokation. – Dmitry Sazonov Mar 13 '16 at 14:20
  • 1
    There is a possible race in the code. See 6. above. – Justin Mar 13 '16 at 15:42
  • @Justin 6 seems more like the same platform-specific exception safety issue rather than a "race" condition – Patrick Parker Apr 18 '19 at 14:33