Disallow multiple instances without a unique rig name

Because of confilicts using the  shared memory to communicate with jt9
only one instance of WSJT-X may run with each unique key (rig name).

Added a QLockFile for each unique  key in the temp directory and logic
to deal with stale locks and retries.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@4465 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2014-10-03 16:07:43 +00:00
parent 4980d0cdc9
commit 4075f5dbb7
5 changed files with 51 additions and 28 deletions

View File

@ -235,7 +235,7 @@ class Configuration::impl final
public: public:
using FrequencyDelta = Radio::FrequencyDelta; using FrequencyDelta = Radio::FrequencyDelta;
explicit impl (Configuration * self, QString const& instance_key, QSettings * settings, bool test_mode, QWidget * parent); explicit impl (Configuration * self, QSettings * settings, QWidget * parent);
~impl (); ~impl ();
bool have_rig (bool open_if_closed = true); bool have_rig (bool open_if_closed = true);
@ -428,8 +428,8 @@ private:
// delegate to implementation class // delegate to implementation class
Configuration::Configuration (QString const& instance_key, QSettings * settings, bool test_mode, QWidget * parent) Configuration::Configuration (QSettings * settings, QWidget * parent)
: m_ {this, instance_key, settings, test_mode, parent} : m_ {this, settings, parent}
{ {
} }
@ -549,7 +549,7 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s
} }
Configuration::impl::impl (Configuration * self, QString const& instance_key, QSettings * settings, bool test_mode, QWidget * parent) Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * parent)
: QDialog {parent} : QDialog {parent}
, self_ {self} , self_ {self}
, ui_ {new Ui::configuration_dialog} , ui_ {new Ui::configuration_dialog}
@ -593,8 +593,6 @@ Configuration::impl::impl (Configuration * self, QString const& instance_key, QS
, default_audio_input_device_selected_ {false} , default_audio_input_device_selected_ {false}
, default_audio_output_device_selected_ {false} , default_audio_output_device_selected_ {false}
{ {
(void)instance_key; // quell compiler warning
ui_->setupUi (this); ui_->setupUi (this);
@ -637,11 +635,7 @@ Configuration::impl::impl (Configuration * self, QString const& instance_key, QS
temp_path_.setPath (temp_location); temp_path_.setPath (temp_location);
} }
QString unique_directory {instance_key}; QString unique_directory {QApplication::applicationName ()};
if (test_mode)
{
unique_directory += " - test_mode";
}
if (!temp_path_.mkpath (unique_directory) || !temp_path_.cd (unique_directory)) if (!temp_path_.mkpath (unique_directory) || !temp_path_.cd (unique_directory))
{ {
QMessageBox::critical (this, "WSJT-X", tr ("Create temporary directory error: ") + temp_path_.absolutePath ()); QMessageBox::critical (this, "WSJT-X", tr ("Create temporary directory error: ") + temp_path_.absolutePath ());

View File

@ -61,7 +61,7 @@ public:
enum DataMode {data_mode_none, data_mode_USB, data_mode_data}; enum DataMode {data_mode_none, data_mode_USB, data_mode_data};
explicit Configuration (QString const& instance_key, QSettings * settings, bool test_mode, QWidget * parent = nullptr); explicit Configuration (QSettings * settings, QWidget * parent = nullptr);
~Configuration (); ~Configuration ();
int exec (); int exec ();

View File

@ -20,6 +20,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QStringList> #include <QStringList>
#include <QMessageBox> #include <QMessageBox>
#include <QLockFile>
#if QT_VERSION >= 0x050200 #if QT_VERSION >= 0x050200
#include <QCommandLineParser> #include <QCommandLineParser>
@ -32,12 +33,12 @@
#include "TraceFile.hpp" #include "TraceFile.hpp"
#include "mainwindow.h" #include "mainwindow.h"
// Multiple instances:
QSharedMemory mem_jt9;
QString my_key;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Multiple instances:
QSharedMemory mem_jt9;
QApplication a(argc, argv); QApplication a(argc, argv);
try try
{ {
@ -111,9 +112,42 @@ int main(int argc, char *argv[])
} }
a.setApplicationName (a.applicationName () + " - " + temp_name); a.setApplicationName (a.applicationName () + " - " + temp_name);
if (parser.isSet (test_option))
{
a.setApplicationName (a.applicationName () + " - test");
}
} }
multiple = true; multiple = true;
} }
// disallow multiple instances with same instance key
QLockFile instance_lock {QDir {QStandardPaths::writableLocation (QStandardPaths::TempLocation)}.absoluteFilePath (a.applicationName () + ".lock")};
instance_lock.setStaleLockTime (0);
auto ok = false;
while (!(ok = instance_lock.tryLock ()))
{
if (QLockFile::LockFailedError == instance_lock.error ())
{
auto button = QMessageBox::question (nullptr
, QApplication::applicationName ()
, QObject::tr ("Another instance may be running, try to remove stale lock file?")
, QMessageBox::Yes | QMessageBox::Retry | QMessageBox::No
, QMessageBox::Yes);
switch (button)
{
case QMessageBox::Yes:
instance_lock.removeStaleLockFile ();
break;
case QMessageBox::Retry:
break;
default:
throw std::runtime_error {"Multiple instances must have unique rig names"};
}
}
}
#endif #endif
#endif #endif
@ -124,7 +158,6 @@ int main(int argc, char *argv[])
throw std::runtime_error {"Cannot find a usable configuration path \"" + config_path.path ().toStdString () + '"'}; throw std::runtime_error {"Cannot find a usable configuration path \"" + config_path.path ().toStdString () + '"'};
} }
QSettings settings(config_path.absoluteFilePath (a.applicationName () + ".ini"), QSettings::IniFormat); QSettings settings(config_path.absoluteFilePath (a.applicationName () + ".ini"), QSettings::IniFormat);
#if WSJT_QDEBUG_TO_FILE #if WSJT_QDEBUG_TO_FILE
// // open a trace file // // open a trace file
TraceFile trace_file {QDir {QApplication::applicationDirPath ()}.absoluteFilePath ("wsjtx_trace.log")}; TraceFile trace_file {QDir {QApplication::applicationDirPath ()}.absoluteFilePath ("wsjtx_trace.log")};
@ -135,8 +168,7 @@ int main(int argc, char *argv[])
// Create and initialize shared memory segment // Create and initialize shared memory segment
// Multiple instances: use rig_name as shared memory key // Multiple instances: use rig_name as shared memory key
my_key = a.applicationName (); mem_jt9.setKey(a.applicationName ());
mem_jt9.setKey(my_key);
if(!mem_jt9.attach()) { if(!mem_jt9.attach()) {
if (!mem_jt9.create(sizeof(jt9com_))) { if (!mem_jt9.create(sizeof(jt9com_))) {
@ -167,7 +199,7 @@ int main(int argc, char *argv[])
).toBool () ? 1u : 4u; ).toBool () ? 1u : 4u;
} }
MainWindow w(multiple, &settings, &mem_jt9, my_key, downSampleFactor, parser.isSet (test_option)); MainWindow w(multiple, &settings, &mem_jt9, downSampleFactor);
w.show(); w.show();
QObject::connect (&a, SIGNAL (lastWindowClosed()), &a, SLOT (quit())); QObject::connect (&a, SIGNAL (lastWindowClosed()), &a, SLOT (quit()));

View File

@ -68,14 +68,14 @@ private:
}; };
//--------------------------------------------------- MainWindow constructor //--------------------------------------------------- MainWindow constructor
MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdmem, QString const& thekey, MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdmem,
unsigned downSampleFactor, bool test_mode, QWidget *parent) : unsigned downSampleFactor, QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
m_revision {revision ("$Rev$")}, m_revision {revision ("$Rev$")},
m_multiple {multiple}, m_multiple {multiple},
m_settings (settings), m_settings (settings),
ui(new Ui::MainWindow), ui(new Ui::MainWindow),
m_config (thekey, settings, test_mode, this), m_config (settings, this),
m_wideGraph (new WideGraph (settings)), m_wideGraph (new WideGraph (settings)),
m_logDlg (new LogQSO (program_title (), settings, &m_config, this)), m_logDlg (new LogQSO (program_title (), settings, &m_config, this)),
m_dialFreq {0}, m_dialFreq {0},
@ -85,7 +85,6 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
m_diskData {false}, m_diskData {false},
m_appDir {QApplication::applicationDirPath ()}, m_appDir {QApplication::applicationDirPath ()},
mem_jt9 {shdmem}, mem_jt9 {shdmem},
mykey_jt9 {thekey},
psk_Reporter (new PSK_Reporter (this)), psk_Reporter (new PSK_Reporter (this)),
m_msAudioOutputBuffered (0u), m_msAudioOutputBuffered (0u),
m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10), m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10),
@ -369,7 +368,7 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
lockFile.open(QIODevice::ReadWrite); lockFile.open(QIODevice::ReadWrite);
QStringList jt9_args { QStringList jt9_args {
"-s", mykey_jt9 "-s", QApplication::applicationName ()
, "-e", QDir::toNativeSeparators (m_appDir) , "-e", QDir::toNativeSeparators (m_appDir)
, "-a", QDir::toNativeSeparators (m_config.data_path ().absolutePath ()) , "-a", QDir::toNativeSeparators (m_config.data_path ().absolutePath ())
}; };

View File

@ -57,8 +57,8 @@ public:
using Frequency = Radio::Frequency; using Frequency = Radio::Frequency;
// Multiple instances: call MainWindow() with *thekey // Multiple instances: call MainWindow() with *thekey
explicit MainWindow(bool multiple, QSettings *, QSharedMemory *shdmem, QString const& thekey, explicit MainWindow(bool multiple, QSettings *, QSharedMemory *shdmem,
unsigned downSampleFactor, bool test_mode, QWidget *parent = 0); unsigned downSampleFactor, QWidget *parent = 0);
~MainWindow(); ~MainWindow();
public slots: public slots:
@ -349,8 +349,6 @@ private:
QRect m_astroGeom; QRect m_astroGeom;
QSharedMemory *mem_jt9; QSharedMemory *mem_jt9;
// Multiple instances:
QString mykey_jt9;
PSK_Reporter *psk_Reporter; PSK_Reporter *psk_Reporter;
SignalMeter *signalMeter; SignalMeter *signalMeter;
LogBook m_logBook; LogBook m_logBook;