aethersdr / aethersdr/AetherSDR
Floating-pan crash-loop guard can false-positive when two local instances share a settings store
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 221
- Forks
- 117
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 299
Description
Summary
The floating-panadapter crash-loop guard landed in #4863 (f6f56865) reads a persisted FloatingPanRestorePending marker at PanadapterStack construction. Finding it armed means "the previous process died mid-float", so the guard drops FloatingPanIds, comes up docked, and tells the operator:
N panadapter(s) restored docked — AetherSDR last closed unexpectedly while popping out. Pop out again to retry.
Two local instances sharing one settings store can make that sentence false.
Why two instances share a store
AppSettings::initGuiClientIdentity() (src/core/AppSettings.cpp) deliberately does not refuse a second local instance. It takes a QLockFile on the persistent GUI client ID and, on failure, falls back to a process-scoped ID with a warning rather than exiting. So a second instance is a supported configuration, and it reads and writes the same settings store.
The race
- Instance A pops out a panadapter.
armFloatingRestoreMarker()writesFloatingPanRestorePending=Trueandsave()s it before the reparent (this ordering is the whole point of #4863), thensaveFloatingState()commits the pan ID. - Within
kFloatingRestoreSettleMs(5 s), instance B starts. ItsPanadapterStackconstructor reads the store, sees the marker armed and IDs present, and evaluatesDropSavedIds. - B blanks
FloatingPanIds, retires the marker, and shows the operator a notice claiming the app last closed unexpectedly. Nothing crashed.
Severity
Low, and deliberately shipped as-is:
- The window is the 5 s settle interval, and only if a second instance starts inside it.
- This is not new state corruption —
FloatingPanIdswas already last-writer-wins across instances. - A's settle timer will clear the marker on schedule; A's own float is unaffected in-session.
What is new is the notice, which asserts something untrue about the user's last session. On a guard whose entire purpose is explaining an otherwise-silent behaviour change, a false explanation is the wrong failure.
Why the cheap fix is wrong
AppSettings::guiClientIdentityIsTransient() is the obvious gate — it is true precisely when another local process holds the lock. But it is also true whenever AETHER_AUTOMATION_* is set, because the automation identity path sets m_guiClientIdentityTransient = true unconditionally.
Gating the guard on it would therefore switch the guard off under automation — exactly where the pan float / pan dock verbs from #4864 would exercise this code path in CI. That trades a cosmetic bug for an untestable guard.
What a real fix looks like
Stamp the marker with the owning process identity instead of a bare True, and treat it as a previous-process crash only when that process is no longer alive. QLockFile already does same-machine PID-liveness (getLockInfo() / removeStaleLockFile()) and is already a dependency of the identity path, so a lock-file-shaped marker is likely cheaper and more portable than a hand-rolled kill(pid, 0) / OpenProcess split.
Worth weighing against simply narrowing the settle window, or dropping the notice's claim about why while keeping the state fix.
Pointers
src/gui/FloatingRestorePolicy.h—evaluateFloatingRestore(),floatingRestoreWrites(), the key and value constantssrc/gui/PanadapterStack.cpp— the constructor guard,armFloatingRestoreMarker(),clearFloatingRestoreMarker()src/gui/MainWindow_Session.cpp— the operator noticetests/floating_restore_policy_test.cpp— the pure-function test (note: in noctest -Rfilter inci.yml, so it does not gate merges)
Raised during review of #4863 and consciously deferred there rather than widening that PR. Original review thread: https://github.com/aethersdr/AetherSDR/pull/4863#discussion_r3744721904
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with evaluateFloatingRestore() and the marker constants in src/gui/FloatingRestorePolicy.h, then trace the constructor guard and marker helpers in src/gui/PanadapterStack.cpp. Review AppSettings::guiClientIdentityIsTransient() and QLockFile liveness behavior before choosing an approach. Update the pure-function coverage in tests/floating_restore_policy_test.cpp and verify the operator notice in src/gui/MainWindow_Session.cpp no longer makes a false crash claim.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100