Reputation: 5434
QSingleApplication
? QMutex
? QSharedMemory
? I'm looking for something that will work smoothly in Windows, OSX and Linux (Ubuntu). Using Qt 4.7.1
Upvotes: 51
Views: 36390
Reputation: 7426
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.
int main(int argc, char *argv[])
{
SingleApplication app( argc, argv );
return app.exec();
}
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();
}
Upvotes: 14
Reputation: 31
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;
}
Upvotes: 2
Reputation: 32635
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
}
Upvotes: 2
Reputation: 51
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);
Upvotes: 0
Reputation: 7600
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;
}
Upvotes: 0
Reputation: 179
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:
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.".
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."
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.
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
.
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;
}
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)
RunGuard
starts to run.(tag2)
and successfully creates the shared memory.release()
at (tag3)
.(tag1)
.(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."RunGuard::tryToRun()
in ProcCur will return false
, which is not as expected because ProcCur is the only existing process using RunGuard
.Upvotes: -1
Reputation: 8994
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();
}
Upvotes: 95