fix(core): improve singleton crash recovery (#1799)

This commit is contained in:
Oleg Shparber
2026-04-04 12:31:42 +03:00
committed by GitHub
parent d0fc3824b6
commit 0199d4aeb9
3 changed files with 32 additions and 17 deletions
+6 -1
View File
@@ -274,7 +274,12 @@ int main(int argc, char *argv[])
using Zeal::Core::ApplicationSingleton;
QScopedPointer<ApplicationSingleton> 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
+18 -13
View File
@@ -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<SharedData *>(m_sharedMemory->data());
m_primaryPid = sd->primaryPid;
+8 -3
View File
@@ -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;