News
15th October 2015 | By:

How to run a single app instance in QT

Sometimes we don’t want to allow users to run multiple instances of the app at once. We might want to do it, to prevent memory leak, files corruption or we decided there is no practical need to run two instances of our app at once. As there is many ways of completing this task, there is many tricky use cases as well where the single instance lock may fall.
I will show couple of cases how to implement this kind of secure lock and will describe advantages and disadvantages of each method.

Please note each occurrence of `<unique identifier>’ should be replaced by some kind of unique identifier, for example UUID.

1. Using `QLockFile` to create a lock file

This solution is good if we want to check for multiple instances running for each user. The lock file is created in a temporary directory that is specific to a logged in user. This means that each user on the system will be able to run one instance of the app for it’s own.
There is no danger when the app crashes, because the lock will be destroyed with the app instance.
[code language=”cpp”]
#include <QString>
#include <QLockFile>
#include <QDir>
#include <QMessageBox>
QString tmpDir = QDir::tempPath();
QLockFile lockFile(tmpDir + "/<unique identifier>.lock");
if(!lockFile.tryLock(100)){
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Warning);
msgBox.setText("You already have this app running."
"\r\nOnly one instance is allowed.");
msgBox.exec();
return 1;
}
[/code]

2. Using `QSystemSemaphore` and `QSharedMemory` to create shared memory cluster

As the previous solution is nice and clean, sometimes we want to limit to one instance for the whole machine. It’s impossible to do with the shared lock file as there might be some permission issues. To be sure that our app will run as single instance machine wise we need to use shared memory that is common for all users. This is a bit more complicated solution, as the shared memory is common for all system users we need to be sure only one user can access it at the same time. To ensure that system semaphores are coming with help. By using system semaphores we can ensure the given part of code and shared memory will be used by single instance at the same time.
[code language=”cpp”]
QSystemSemaphore sema("<unique identifier>", 1);
sema.acquire();
#ifndef Q_OS_WIN32
// on linux/unix shared memory is not freed upon crash
// so if there is any trash from previous instance, clean it
QSharedMemory nix_fix_shmem("<unique identifier 2>");
if(nix_fix_shmem.attach()){
nix_fix_shmem.detach();
}
#endif
QSharedMemory shmem("<unique identifier 2>");
bool is_running;
if (shmem.attach()){
is_running = true;
}else{
shmem.create(1);
is_running = false;
}
sema.release();
if(is_running){
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Warning);
msgBox.setText("You already have this app running."
"\r\nOnly one instance is allowed.");
msgBox.exec();
return 1;
}
[/code]
There are many more ways of ensuring single instance run on the machine, however I find these the most agile, simple and universal.

Tags: , , ,

5 Comments

  1. Martin
    6th December 2016 @ 13:23

    I was thinking abandon using the method #2 as the QSharedMemory is not clear uppong a crash. But your solution was really interesting. I did some test and to get it work properly you have to add this line
    // Prevent multiple application instances
    QSystemSemaphore sema(“Sonar-Key”, 1);
    sema.acquire();
    #if !defined(Q_OS_WIN32)
    // On linux/unix shared memory is not freed upon crash
    // so if there is any trash from previous instance, clean it
    QSharedMemory nix_fix_shmem(“Sonar-Key2”);
    bool bDetach = false;
    if (nix_fix_shmem.attach() == false)
    {

    bDetach = nix_fix_shmem.detach();
    }

    nix_fix_shmem.detach();
    #endif
    QSharedMemory shmem(“Sonar-Key2”);
    bool is_running;
    if (shmem.attach() == true)
    {

    is_running = true;
    }

    else
    {

    shmem.create(1);
    is_running = false;
    }

    sema.release();

    Reply
    • Martin
      6th December 2016 @ 13:25

      sory, i did not mention the line :
      #if !defined(Q_OS_WIN32)
      // On linux/unix shared memory is not freed upon crash
      // so if there is any trash from previous instance, clean it
      QSharedMemory nix_fix_shmem(“Sonar-Key2”);
      bool bDetach = false;
      if (nix_fix_shmem.attach() == false)
      {
      bDetach = nix_fix_shmem.detach();
      }
      nix_fix_shmem.detach(); // <——– This one
      #endif

      Reply
      • Lucas
        1st September 2017 @ 6:28

        why you created bDetach? you saved the detach result but never ask for the result.
        then you added a detach after the “if”, so, why to do the “if” if you will detach anyways?
        With the standard code, our yours, when I close che app with Ctrl+C or external kill, the next time it says running, but the next it one starts.
        I have to call it twice, I am using a QProcess calling “ps” and parsing the output to see if there are 2 applications running or this one is the ony one running…

        Reply
  2. Hojjat
    25th January 2017 @ 11:48

    Hi,
    I don’t know what version of Qt you used but QSharedMemory::lock() is a semaphore itself and the QSystemSemaphore is not required.
    Thanks.

    Reply
  3. Ed
    6th March 2018 @ 3:06

    This is simple and worked great for me, Qt4.8.5 Fedora 19. Probably not the best code for cleaning up after itself but it works fine. If you want to end the process that is already running, build another command ‘kill -9 pid’ and system that off.

    void my_class::dupe_check()
    {
    std::stringstream command;
    int isRunning = 0;
    //get this process id
    pid_t pid = getpid();
    command << "ps -eo pid,comm | grep 'my_app_name' | grep -v " << pid;
    isRunning = system(command.str().c_str());
    if(isRunning == 0)
    {
    QMessageBox::critical(this, "my_app_name", "my_app_name is already running. EXITING");
    std::cout << "my_app is already running. EXITING" << std::endl;
    exit(0);
    }
    return;
    }

    Reply

Leave a reply

Your email address will not be published. Not now, not ever. Required fields are marked *

Comments


You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Name
Email
Website