What's wrong
IsStoredProcessRunning requires the running process's MainModule.FileName to equal the stored MainModuleFileName exactly (SingleAppInstance.cs:278-279). On Linux, once a running executable's file is unlinked or replaced, the kernel reports its path (/proc/<pid>/exe, and so MainModule.FileName) with a (deleted) suffix. The comparison then fails, and the live first instance is treated as a recycled PID.
Repro (Linux, net10.0)
- Start instance A of a small app that calls
ShouldLaunch() and stays alive. It prints True, and the PID file stores MainModuleFileName: ".../upd/App".
- Run instance B: it prints
False, which is correct.
- Replace the binary the way apt/rpm and self-updaters do:
cp App app.new && mv -f app.new App. A's MainModule.FileName now reads .../upd/App (deleted).
- Run B again. Actual:
ShouldLaunch() returns True while A is still running, and B overwrites A's PID file. Expected: False.
The README promises to "ensure only one instance" and to verify "PID, process name, start time, executable path". The IsAlreadyRunning XML doc says it "verifies it's the same application".
Why it matters
"An update was installed while the app was open, then the user launches it again" is the standard flow for package-manager and in-app updates. It is also exactly when a duplicate instance does the most harm, because the old and new versions share settings and data files. After step 4, a third launch is checked against B, so A becomes invisible for good.
Suggested fix / acceptance criteria
- Before comparing on Linux, strip a trailing
" (deleted)" from the running process's module path. Alternatively, treat a path that differs only by that suffix as a match, and rely on the name and start-time checks.
- Add a Linux-only test: start a helper, replace its executable file with
File.Move(..., overwrite: true), and assert ShouldLaunch() still returns false.
Related but different: #188, which covers start-time drift after a wall-clock step.
What's wrong
IsStoredProcessRunningrequires the running process'sMainModule.FileNameto equal the storedMainModuleFileNameexactly (SingleAppInstance.cs:278-279). On Linux, once a running executable's file is unlinked or replaced, the kernel reports its path (/proc/<pid>/exe, and soMainModule.FileName) with a(deleted)suffix. The comparison then fails, and the live first instance is treated as a recycled PID.Repro (Linux, net10.0)
ShouldLaunch()and stays alive. It printsTrue, and the PID file storesMainModuleFileName: ".../upd/App".False, which is correct.cp App app.new && mv -f app.new App. A'sMainModule.FileNamenow reads.../upd/App (deleted).ShouldLaunch()returnsTruewhile A is still running, and B overwrites A's PID file. Expected:False.The README promises to "ensure only one instance" and to verify "PID, process name, start time, executable path". The
IsAlreadyRunningXML doc says it "verifies it's the same application".Why it matters
"An update was installed while the app was open, then the user launches it again" is the standard flow for package-manager and in-app updates. It is also exactly when a duplicate instance does the most harm, because the old and new versions share settings and data files. After step 4, a third launch is checked against B, so A becomes invisible for good.
Suggested fix / acceptance criteria
" (deleted)"from the running process's module path. Alternatively, treat a path that differs only by that suffix as a match, and rely on the name and start-time checks.File.Move(..., overwrite: true), and assertShouldLaunch()still returnsfalse.Related but different: #188, which covers start-time drift after a wall-clock step.