fix(restart): the self-relaunch waits for its parent's mutex

A database switch relaunches OpsLog, and the child regularly won the
race against the old instance's teardown — so following the app's own
instructions produced 'OpsLog is already running'. The relaunch now
passes --relaunch, which gets the same 20-second mutex patience the
post-update restart has always had. Opens the 0.27.6 block.
This commit is contained in:
2026-08-31 14:19:30 +02:00
parent 8790b98766
commit 68f0d68980
3 changed files with 23 additions and 7 deletions
+3 -1
View File
@@ -2851,7 +2851,9 @@ func (a *App) RestartApp() error {
if err != nil { if err != nil {
return fmt.Errorf("locate executable: %w", err) return fmt.Errorf("locate executable: %w", err)
} }
cmd := exec.Command(exe) // --relaunch: the child waits for OUR mutex instead of declaring us a
// duplicate — this instance is quitting, just not always fast enough.
cmd := exec.Command(exe, "--relaunch")
cmd.Dir = filepath.Dir(exe) cmd.Dir = filepath.Dir(exe)
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return fmt.Errorf("relaunch OpsLog: %w", err) return fmt.Errorf("relaunch OpsLog: %w", err)
+10
View File
@@ -1,4 +1,14 @@
[ [
{
"version": "0.27.6",
"date": "",
"en": [
"Switching the settings database no longer shows “OpsLog is already running”: the automatic relaunch now waits for the closing instance to release its lock instead of racing it."
],
"fr": [
"Changer de base de réglages naffiche plus « OpsLog is already running » : la relance automatique attend désormais que linstance qui se ferme libère son verrou au lieu de la prendre de vitesse."
]
},
{ {
"version": "0.27.5", "version": "0.27.5",
"date": "", "date": "",
+10 -6
View File
@@ -48,14 +48,14 @@ func hasFlag(args []string, flag string) bool {
} }
// acquireInstance grabs the single-instance mutex. On a normal launch it's a plain // acquireInstance grabs the single-instance mutex. On a normal launch it's a plain
// try (fail → another OpsLog is running, so exit). On a --post-update relaunch the // try (fail → another OpsLog is running, so exit). On a --post-update or
// previous instance may still be shutting down and holding the mutex, so retry for // --relaunch start the previous instance may still be shutting down and holding
// a few seconds until it frees. // the mutex, so retry for a few seconds until it frees.
func acquireInstance(postUpdate bool) bool { func acquireInstance(wait bool) bool {
if acquireSingleInstance() { if acquireSingleInstance() {
return true return true
} }
if !postUpdate { if !wait {
return false return false
} }
deadline := time.Now().Add(20 * time.Second) deadline := time.Now().Add(20 * time.Second)
@@ -90,7 +90,11 @@ func main() {
// to free instead of bailing out. Then clear the old exe it left behind. // to free instead of bailing out. Then clear the old exe it left behind.
bootLogLaunch() bootLogLaunch()
postUpdate := hasFlag(os.Args[1:], "--post-update") postUpdate := hasFlag(os.Args[1:], "--post-update")
if !acquireInstance(postUpdate) { // A self-relaunch (database switch) races its own parent: the new process
// regularly wins the start against the old one's teardown, and the operator
// got "OpsLog is already running" for following instructions. Same patience
// as post-update.
if !acquireInstance(postUpdate || hasFlag(os.Args[1:], "--relaunch")) {
// SAID, not merely done. This exit is correct — a second instance would // SAID, not merely done. This exit is correct — a second instance would
// fight the first over the rig — but it happened in total silence: no // fight the first over the rig — but it happened in total silence: no
// window, no data folder, no log, which is indistinguishable from a // window, no data folder, no log, which is indistinguishable from a