summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/ior.go8
-rw-r--r--internal/ior_bpfsetup.go13
-rw-r--r--internal/ior_parquet_sink.go8
3 files changed, 23 insertions, 6 deletions
diff --git a/internal/ior.go b/internal/ior.go
index 9a60869..02a5b30 100644
--- a/internal/ior.go
+++ b/internal/ior.go
@@ -499,10 +499,16 @@ func runTraceWithContext(parentCtx context.Context, cfg flags.Config, started ch
}()
defer releaseBindings()
- ch, err := setupEventChannel(bpfModule)
+ ch, rb, err := setupEventChannel(bpfModule)
if err != nil {
return err
}
+ // Stop the ring-buffer polling goroutine before the module is closed.
+ // rb.Stop() signals the background goroutine, drains the channel, and
+ // waits for the goroutine to exit; bpfModule.Close() (deferred above)
+ // then calls rb.Close() which frees the C ring_buffer struct. Both are
+ // idempotent so double-calling is safe.
+ defer rb.Stop()
ctx, cancel, stopSignals := setupTraceContext(parentCtx, cfg, logln)
defer cancel()
defer stopSignals()
diff --git a/internal/ior_bpfsetup.go b/internal/ior_bpfsetup.go
index 0d32d4c..61009c6 100644
--- a/internal/ior_bpfsetup.go
+++ b/internal/ior_bpfsetup.go
@@ -87,15 +87,20 @@ func setupBPFModule(parentCtx context.Context, cfg flags.Config) (*bpf.Module, *
return bpfModule, mgr, releaseBindings, nil
}
-// setupEventChannel initialises the BPF ring-buffer and returns the event channel.
-func setupEventChannel(bpfModule *bpf.Module) (chan []byte, error) {
+// setupEventChannel initialises the BPF ring-buffer and returns both the event
+// channel and the ring-buffer handle. The caller must call rb.Stop() when the
+// trace ends (before bpfModule.Close()) to promptly halt the background polling
+// goroutine and release the C ring_buffer struct. bpfModule.Close() also closes
+// all ring buffers it owns, but only calling Stop() first ensures the goroutine
+// exits without waiting for the module teardown path.
+func setupEventChannel(bpfModule *bpf.Module) (chan []byte, *bpf.RingBuffer, error) {
ch := make(chan []byte, appconfig.DefaultChannelBufferSize)
rb, err := bpfModule.InitRingBuf("event_map", ch)
if err != nil {
- return nil, err
+ return nil, nil, err
}
rb.Poll(300)
- return ch, nil
+ return ch, rb, nil
}
// --- compile-time interface satisfaction assertions ---
diff --git a/internal/ior_parquet_sink.go b/internal/ior_parquet_sink.go
index 3b87385..279c9be 100644
--- a/internal/ior_parquet_sink.go
+++ b/internal/ior_parquet_sink.go
@@ -111,10 +111,16 @@ func runHeadlessParquet(cfg flags.Config) error {
}()
defer releaseBindings()
- ch, err := setupEventChannel(bpfModule)
+ ch, rb, err := setupEventChannel(bpfModule)
if err != nil {
return err
}
+ // Stop the ring-buffer polling goroutine before the module is closed.
+ // rb.Stop() signals the background goroutine, drains the channel, and
+ // waits for the goroutine to exit; bpfModule.Close() (deferred above)
+ // then calls rb.Close() which frees the C ring_buffer struct. Both are
+ // idempotent so double-calling is safe.
+ defer rb.Stop()
ctx, cancel, stopSignals := setupTraceContext(context.Background(), cfg, logln)
defer cancel()
defer stopSignals()