From a9b02bc67604fffd906146c12260d2bed90928d4 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 28 Oct 2026 15:40:39 +0100 Subject: [PATCH] node-tools: keep the stopping-runtime test under a second, and start no first bundle once stopping The test watched two seconds per row; a 10 ms restart pause and a 250 ms window prove the same. launch.Start itself now refuses while the runtime stops, so a reload's first start is held too (issue 1126). --- node-tools/internal/launch/error_code_test.go | 16 +++++++++++++--- node-tools/internal/launch/launch.go | 3 +++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/node-tools/internal/launch/error_code_test.go b/node-tools/internal/launch/error_code_test.go index 7002f46..3b3e1b2 100644 --- a/node-tools/internal/launch/error_code_test.go +++ b/node-tools/internal/launch/error_code_test.go @@ -53,7 +53,7 @@ done // itself is stopping, none is, by a restart or by a call. func TestABundleIsStartedAgainUnlessTheRuntimeIsStopping(t *testing.T) { saved := RestartFirst - RestartFirst = 50 * time.Millisecond + RestartFirst = 10 * time.Millisecond t.Cleanup(func() { RestartFirst = saved; shuttingDown.Store(false) }) for _, row := range []struct { name string @@ -86,20 +86,30 @@ func TestABundleIsStartedAgainUnlessTheRuntimeIsStopping(t *testing.T) { if err := syscall.Kill(pid, syscall.SIGTERM); err != nil { t.Fatal(err) } - deadline := time.Now().Add(2 * time.Second) + // The restart row ends at its second start; the stopping row watches a window many restart pauses long. + deadline := time.Now().Add(250 * time.Millisecond) for { said, _ = os.ReadFile(out) n := strings.Count(string(said), "started") if n > row.starts { t.Fatalf("started %d times, want %d", n, row.starts) } + if n == row.starts && !row.stopping { + break + } if time.Now().After(deadline) { if n != row.starts { t.Fatalf("started %d times, want %d", n, row.starts) } break } - time.Sleep(20 * time.Millisecond) + time.Sleep(5 * time.Millisecond) + } + if row.stopping { + // And a bundle first loaded now (a reload) is not started either. + if _, err := Start("late", entry, append(os.Environ(), "OUT="+out), &subscribing{}, t.Logf); err == nil { + t.Error("a bundle was started after the runtime began stopping") + } } }) } diff --git a/node-tools/internal/launch/launch.go b/node-tools/internal/launch/launch.go index 8167b2c..fecb1c2 100644 --- a/node-tools/internal/launch/launch.go +++ b/node-tools/internal/launch/launch.go @@ -194,6 +194,9 @@ var colour = regexp.MustCompile("\x1b\\[[0-9;]*m") // Start launches one bundle and learns its tools. It fails when the child cannot be started or does // not complete the handshake. A child that exits later is started again on its next call. func Start(module, entry string, env []string, mesh Bus, logf func(string, ...any)) (*Launched, error) { + if shuttingDown.Load() { + return nil, fmt.Errorf("%s's bundle is not started: the runtime is stopping", module) + } var mu sync.Mutex var current *child // The child that last subscribed is the one events are handed to: a restarted child subscribes