Skip to content

On Linux, a second instance launches after the app's executable is updated on disk while it runs: MainModule path gains " (deleted)" and no longer matches #191

Description

@matt-edmondson

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)

  1. Start instance A of a small app that calls ShouldLaunch() and stays alive. It prints True, and the PID file stores MainModuleFileName: ".../upd/App".
  2. Run instance B: it prints False, which is correct.
  3. 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).
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions