summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-24 20:32:17 +0300
committerPaul Buetow <paul@buetow.org>2026-04-24 20:32:17 +0300
commitfadbf135d0b251387fd785083df79e27d1025cac (patch)
treeb97ae34ed88957a355022c8beedf39d4193a3f47
parent4595fa01693497682beb1b84641233ca4d214072 (diff)
fix task 38: plug profiling fd leak on setup failure paths
runTraceWithContext and runHeadlessParquet opened cpu/mem/exec-trace profile files via setupProfiling, then relied on the shutdown watcher goroutine (registered later) to close them. If any intervening step (newEventLoop, recorder.Start) returned an error, the watcher was never registered and the profile fds leaked until process exit. Add a defer profiling.stop(logln) immediately after setupProfiling so the fds are released on every return path. profiling.stop is idempotent via sync.Once so the watcher and defer can both run harmlessly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-rw-r--r--internal/ior.go5
-rw-r--r--internal/ior_parquet_sink.go5
2 files changed, 10 insertions, 0 deletions
diff --git a/internal/ior.go b/internal/ior.go
index ab52299..e6e7233 100644
--- a/internal/ior.go
+++ b/internal/ior.go
@@ -400,6 +400,11 @@ func runTraceWithContext(parentCtx context.Context, cfg flags.Config, started ch
if err != nil {
return err
}
+ // Guarantee the profiling file descriptors (cpu/mem/exec-trace profiles) are
+ // closed even if a later setup step fails before the shutdown watcher is
+ // registered. profiling.stop is idempotent via sync.Once, so double-calling
+ // it from the watcher goroutine and from this defer is safe.
+ defer profiling.stop(logln)
signalTraceStarted(started)
diff --git a/internal/ior_parquet_sink.go b/internal/ior_parquet_sink.go
index eb187e8..83ca769 100644
--- a/internal/ior_parquet_sink.go
+++ b/internal/ior_parquet_sink.go
@@ -119,6 +119,11 @@ func runHeadlessParquet(cfg flags.Config) error {
if err != nil {
return err
}
+ // Guarantee the profiling file descriptors (cpu/mem/exec-trace profiles) are
+ // closed even if a later setup step fails before the shutdown watcher is
+ // registered. profiling.stop is idempotent via sync.Once, so double-calling
+ // it from the watcher goroutine and from this defer is safe.
+ defer profiling.stop(logln)
el, err := newEventLoop(newEventLoopConfig(cfg))
if err != nil {