Skip to content

[v3] SingleInstance: os.Exit inside application.New skips the caller's defers (silent cleanup loss) #6052

Description

@midagedev

Wails version family

v3 (alpha/beta)

Exact Wails version

v3.0.0-beta.15 (sites verified identical at master 9d49e8e and at beta.9/beta.12)

Operating System

All (the os.Exit call is in shared code; darwin/linux have one extra exit site each)

Description

When SingleInstance detects an already-running instance, application.New calls os.Exit directly:

  • v3/pkg/application/application.go:206-218 — on alreadyRunningError: notifyFirstInstance(), then os.Exit(appOptions.SingleInstance.ExitCode)
  • plus single_instance_darwin.go:82 and single_instance_linux.go:99

os.Exit terminates without running deferred functions (Go spec), so any cleanup the caller scheduled before calling New() — flushing a write-ahead file, releasing a lock file, closing a log — is silently skipped in the second process. New() looks like an ordinary constructor, so having opened resources before it is natural; the constraint is invisible in the API.

In our production app (gadak) the second-instance exit was skipping a persistence flush scheduled with defer. We had to reorder initialization so nothing requiring cleanup exists before New(), and pin that ordering with a test — that works, but every SingleInstance adopter has to discover it the same way.

What this issue is not asking: SingleInstance.ExitCode already covers the exit value (#3723), and the second instance correctly must not continue into Run() and show a window — no change to that. The ask is a seam for cleanup, in either shape:

  1. New() returns a distinguished error (e.g. errors.Is(err, application.ErrAlreadyRunning)) after notifyFirstInstance(), and the caller exits — possibly opt-in to avoid breaking apps that don't check the error; or
  2. a hook, e.g. SingleInstance.OnSecondInstanceExit func(), invoked after notifyFirstInstance() and before os.Exit.

Related: #3301 ("in idiomatic Go we return an error"); #4066 already converted service-startup fatals into returned errors; #5170 recently touched this path. Happy to send a PR for whichever shape you prefer.

To Reproduce

  1. SingleInstance enabled; in main(), open any resource needing cleanup and schedule it with defer (e.g. defer f.Sync()), then call application.New(...).
  2. Start the app once, then start a second instance.
  3. The second process exits with ExitCode — the deferred cleanup never runs (observable with a defer that writes a sentinel file: the file is not written).

Expected behaviour

A way for the second instance to run its cleanup before terminating — via a returned error or a pre-exit hook — while wails keeps owning the "second instance does not open a window" behavior.

Attempted Fixes

Reordered our initialization so that nothing requiring cleanup exists before New(), pinned with a test. Works, but is a per-app rediscovery of an API-invisible constraint.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions