diff --git a/src/app/main.cpp b/src/app/main.cpp index 8169742c..25b5c3e3 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -274,7 +274,12 @@ int main(int argc, char *argv[]) using Zeal::Core::ApplicationSingleton; QScopedPointer appSingleton(new ApplicationSingleton()); - if (appSingleton->isSecondary()) { + if (appSingleton->state() == ApplicationSingleton::State::Failed) { + QTextStream(stderr) << "Failed to initialize application singleton." << '\n'; + return EXIT_FAILURE; + } + + if (appSingleton->state() == ApplicationSingleton::State::Secondary) { #ifdef Q_OS_WINDOWS ::AllowSetForegroundWindow(appSingleton->primaryPid()); #endif diff --git a/src/libs/core/applicationsingleton.cpp b/src/libs/core/applicationsingleton.cpp index a3c35238..7fa71162 100644 --- a/src/libs/core/applicationsingleton.cpp +++ b/src/libs/core/applicationsingleton.cpp @@ -45,8 +45,7 @@ ApplicationSingleton::ApplicationSingleton(QObject *parent) m_sharedMemory = new QSharedMemory(m_id, this); - m_isPrimary = m_sharedMemory->create(sizeof(SharedData)); - if (m_isPrimary) { + if (m_sharedMemory->create(sizeof(SharedData))) { setupPrimary(); return; } @@ -95,31 +94,34 @@ ApplicationSingleton::ApplicationSingleton(QObject *parent) } } - m_isPrimary = m_sharedMemory->create(sizeof(SharedData)); - if (m_isPrimary) { + if (m_sharedMemory->create(sizeof(SharedData))) { setupPrimary(); return; } } #endif - // Fall back to secondary if we couldn't reclaim the segment. + // If both create() and attach() failed, the shared memory segment is in + // a broken state (e.g., stale backing file after a crash). Clean up and + // try to become primary one last time. if (!m_sharedMemory->attach(QSharedMemory::ReadOnly)) { qCWarning(log) << "Cannot attach to the shared memory segment:" << m_sharedMemory->errorString(); + + if (m_sharedMemory->create(sizeof(SharedData))) { + setupPrimary(); + return; + } + + qCWarning(log) << "Cannot create shared memory segment:" << m_sharedMemory->errorString(); return; } setupSecondary(); } -bool ApplicationSingleton::isPrimary() const +ApplicationSingleton::State ApplicationSingleton::state() const { - return m_isPrimary; -} - -bool ApplicationSingleton::isSecondary() const -{ - return !m_isPrimary; + return m_state; } qint64 ApplicationSingleton::primaryPid() const @@ -130,7 +132,7 @@ qint64 ApplicationSingleton::primaryPid() const bool ApplicationSingleton::sendMessage(QByteArray &data, int timeout) { // No support for primary to secondary communication. - if (m_isPrimary) { + if (m_state != State::Secondary) { return false; } @@ -146,6 +148,7 @@ bool ApplicationSingleton::sendMessage(QByteArray &data, int timeout) void ApplicationSingleton::setupPrimary() { + m_state = State::Primary; m_primaryPid = QCoreApplication::applicationPid(); qCInfo(log, "Starting as a primary instance. (PID: %lld)", m_primaryPid); @@ -177,6 +180,8 @@ void ApplicationSingleton::setupPrimary() void ApplicationSingleton::setupSecondary() { + m_state = State::Secondary; + m_sharedMemory->lock(); auto sd = static_cast(m_sharedMemory->data()); m_primaryPid = sd->primaryPid; diff --git a/src/libs/core/applicationsingleton.h b/src/libs/core/applicationsingleton.h index 2cd1a39a..c3b339d3 100644 --- a/src/libs/core/applicationsingleton.h +++ b/src/libs/core/applicationsingleton.h @@ -16,10 +16,15 @@ class ApplicationSingleton final : public QObject Q_OBJECT Q_DISABLE_COPY_MOVE(ApplicationSingleton) public: + enum class State { + Primary, + Secondary, + Failed, + }; + explicit ApplicationSingleton(QObject *parent = nullptr); - bool isPrimary() const; - bool isSecondary() const; + State state() const; qint64 primaryPid() const; bool sendMessage(QByteArray &data, int timeout = 500); @@ -35,7 +40,7 @@ private: QString m_id; - bool m_isPrimary = false; + State m_state = State::Failed; qint64 m_primaryPid = 0; QSharedMemory *m_sharedMemory = nullptr;