You could try something along these lines:
#include <QThread>
#include <QSystemSemaphore>
#include <QSemaphore>
class ProcessCounter
{
class ProcessCounterThread : public QThread
{
public:
ProcessCounterThread(qint64 timeout, QSystemSemaphore &);
void release();
void cancel();
bool success() const;
protected:
void run() override;
private:
bool acquired;
qint64 maxWaitTime;
QSemaphore semaphore;
QSystemSemaphore & systemSemaphore;
};
public:
ProcessCounter(const QString & key, int count, qint64 timeout = 1000);
~ProcessCounter();
bool acquired() const;
private:
QSystemSemaphore semaphore;
ProcessCounterThread thread;
};
inline ProcessCounter::ProcessCounterThread::ProcessCounterThread(qint64 timeout, QSystemSemaphore & sem)
: acquired(false), maxWaitTime(timeout), systemSemaphore(sem)
{
}
inline bool ProcessCounter::ProcessCounterThread::success() const
{
return acquired;
}
inline void ProcessCounter::ProcessCounterThread::release()
{
semaphore.release();
}
inline void ProcessCounter::ProcessCounterThread::cancel()
{
semaphore.release();
wait();
acquired = false;
}
void ProcessCounter::ProcessCounterThread::run()
{
acquired = semaphore.tryAcquire(1, maxWaitTime);
if (!acquired)
systemSemaphore.release();
}
ProcessCounter::ProcessCounter(const QString & key, int count, qint64 timeout)
: semaphore(key, count), thread(timeout, semaphore)
{
thread.start();
if (!semaphore.acquire()) {
thread.cancel();
// ... Error handling
return;
}
thread.wait(); // Wait a bit hoping for the global resource to become available
}
ProcessCounter::~ProcessCounter()
{
if (thread.success())
semaphore.release(); // We had our global semaphore acquired, now it's time to release it
}
inline bool ProcessCounter::acquired() const
{
return thread.success();
}
Usage on the stack (as expected):
static const int maxProcesses = 20;
static const qint64 timeout = 1000;
static const QString key = QStringLiteral("KeyForTheGlobalSemaphore");
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
ProcessCounter counter(key, maxProcesses, timeout);
if (!counter.acquired())
return 0; // Not allowed, too many processes running already
return QApplication::exec();
}
Note you'd need to handle SIGSEGV and possibly other signals manually and you shouldn't call ::exit at all, because it will not run the ProcessCounter's destructor, meaning the global semaphore won't be released. Graceful shutdown is paramount here.