summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-12 22:36:11 +0300
committerPaul Buetow <paul@buetow.org>2026-05-12 22:36:11 +0300
commita2c067cc49b96968da81031275de9c44c4ba2ee9 (patch)
tree84d0507274599cf3cad0daa9b1be613549b74304 /internal
parent235eb7541d6396e860b23aad63ed44c734cdf767 (diff)
introduce RuntimeBuilder to separate construction from orchestration in ior.go
RuntimeBuilder encapsulates allocation of per-trace-session components (statsengine.Engine, streamrow.RingBuffer, Sequencer, flamegraph.LiveTrie); buildTUIRuntime, buildTestFlamesRuntime, and buildTestLiveFlamesRuntime now delegate construction to RuntimeBuilder.Build() and focus only on wiring. wireRuntimeBindings is extracted to isolate the context-binding wiring step. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal')
-rw-r--r--internal/ior.go85
-rw-r--r--internal/runtime_builder.go49
2 files changed, 98 insertions, 36 deletions
diff --git a/internal/ior.go b/internal/ior.go
index b6070dc..aff1355 100644
--- a/internal/ior.go
+++ b/internal/ior.go
@@ -163,29 +163,28 @@ func tuiTestLiveFlamesStarter(cfg flags.Config) runtime.TraceStarter {
}
// buildTestFlamesRuntime allocates a stats engine, stream buffer, and seeded
-// live trie for static test-flames mode.
+// live trie for static test-flames mode. Component allocation is delegated to
+// RuntimeBuilder so this function focuses on the seed step only.
func buildTestFlamesRuntime(cfg flags.Config) (*statsengine.Engine, *streamrow.RingBuffer, *flamegraph.LiveTrie) {
- engine := statsengine.NewEngine(64)
- streamBuf := streamrow.NewRingBuffer()
- liveTrie := flamegraph.NewLiveTrie(cfg.CollapsedFields, cfg.CountField)
- flamegraph.SeedTestFlameData(liveTrie)
- return engine, streamBuf, liveTrie
+ components := newRuntimeBuilder(cfg).Build()
+ flamegraph.SeedTestFlameData(components.liveTrie)
+ return components.engine, components.streamBuf, components.liveTrie
}
// buildTestLiveFlamesRuntime allocates a stats engine, stream buffer, and live
// trie for live test-flames mode, then launches a goroutine to update the trie.
+// Component allocation is delegated to RuntimeBuilder; this function handles
+// only the seed step and the background updater goroutine.
func buildTestLiveFlamesRuntime(ctx context.Context, cfg flags.Config) (*statsengine.Engine, *streamrow.RingBuffer, *flamegraph.LiveTrie) {
- engine := statsengine.NewEngine(64)
- streamBuf := streamrow.NewRingBuffer()
- liveTrie := flamegraph.NewLiveTrie(cfg.CollapsedFields, cfg.CountField)
- flamegraph.SeedTestLiveFlameData(liveTrie, 0)
+ components := newRuntimeBuilder(cfg).Build()
+ flamegraph.SeedTestLiveFlameData(components.liveTrie, 0)
interval := cfg.LiveInterval
if interval <= 0 {
interval = 200 * time.Millisecond
}
- go runSyntheticLiveFlames(ctx, liveTrie, interval)
- return engine, streamBuf, liveTrie
+ go runSyntheticLiveFlames(ctx, components.liveTrie, interval)
+ return components.engine, components.streamBuf, components.liveTrie
}
func runSyntheticLiveFlames(ctx context.Context, liveTrie *flamegraph.LiveTrie, interval time.Duration) {
@@ -225,40 +224,54 @@ type tuiRuntime struct {
filterEpoch uint64
}
-// buildTUIRuntime allocates fresh trace-session state and, when persistent
-// runtime bindings exist in ctx, wires them in (reusing the existing stream
-// buffer / sequencer and reading the parquet recorder and filter epoch).
+// buildTUIRuntime constructs fresh trace-session components via RuntimeBuilder
+// and then wires them into any persistent runtime bindings found in ctx.
+// Construction (allocating engine, buffer, sequencer, trie) is handled by
+// RuntimeBuilder; this function focuses on the wiring: reusing the persistent
+// stream buffer and sequencer from the TUI, reading the recorder and filter
+// epoch, and publishing the new components back to the runtime bindings.
func buildTUIRuntime(ctx context.Context, cfg flags.Config) (*tuiRuntime, error) {
+ components := newRuntimeBuilder(cfg).Build()
rt := &tuiRuntime{
- engine: statsengine.NewEngine(64),
- streamSeq: streamrow.NewSequencer(0),
- liveTrie: flamegraph.NewLiveTrie(cfg.CollapsedFields, cfg.CountField),
+ engine: components.engine,
+ streamBuf: components.streamBuf,
+ streamSrc: components.streamBuf,
+ streamSeq: components.streamSeq,
+ liveTrie: components.liveTrie,
}
- buf := streamrow.NewRingBuffer()
- rt.streamBuf = buf
- rt.streamSrc = buf
if bindings, ok := runtime.RuntimeBindingsFromContext(ctx); ok {
- if persistent := bindings.StreamBuffer(); persistent != nil {
- rt.streamSrc = persistent
- sink, ok := persistent.(streamEventSink)
- if !ok {
- return nil, fmt.Errorf("runtime stream source does not support event pushes")
- }
- rt.streamBuf = sink
- }
- if persistentSeq := bindings.StreamSequencer(); persistentSeq != nil {
- rt.streamSeq = persistentSeq
+ if err := wireRuntimeBindings(rt, bindings); err != nil {
+ return nil, err
}
- rt.recorder = bindings.Recorder()
- rt.filterEpoch = bindings.FilterEpoch()
- bindings.SetDashboardSnapshotSource(rt.engine)
- bindings.SetEventStreamSource(rt.streamSrc)
- bindings.SetLiveTrie(rt.liveTrie)
}
return rt, nil
}
+// wireRuntimeBindings reuses persistent TUI-owned state (stream buffer,
+// sequencer, recorder, filter epoch) from bindings and publishes the freshly
+// built components back to the TUI so the new trace session is visible.
+// It is called only when a TraceRuntimeBindings is present in the context.
+func wireRuntimeBindings(rt *tuiRuntime, bindings runtime.TraceRuntimeBindings) error {
+ if persistent := bindings.StreamBuffer(); persistent != nil {
+ rt.streamSrc = persistent
+ sink, ok := persistent.(streamEventSink)
+ if !ok {
+ return fmt.Errorf("runtime stream source does not support event pushes")
+ }
+ rt.streamBuf = sink
+ }
+ if persistentSeq := bindings.StreamSequencer(); persistentSeq != nil {
+ rt.streamSeq = persistentSeq
+ }
+ rt.recorder = bindings.Recorder()
+ rt.filterEpoch = bindings.FilterEpoch()
+ bindings.SetDashboardSnapshotSource(rt.engine)
+ bindings.SetEventStreamSource(rt.streamSrc)
+ bindings.SetLiveTrie(rt.liveTrie)
+ return nil
+}
+
// makeTUIEventLoopConfigurer returns the func(*eventLoop) callback that wires
// the event loop into the TUI runtime: it sets the initial filter, installs
// the print callback that fans out to engine/stream/trie, and registers the
diff --git a/internal/runtime_builder.go b/internal/runtime_builder.go
new file mode 100644
index 0000000..bc1c228
--- /dev/null
+++ b/internal/runtime_builder.go
@@ -0,0 +1,49 @@
+package internal
+
+import (
+ "ior/internal/flags"
+ "ior/internal/flamegraph"
+ "ior/internal/statsengine"
+ "ior/internal/streamrow"
+)
+
+const defaultEngineCapacity = 64
+
+// runtimeComponents holds the freshly allocated trace-session components
+// produced by RuntimeBuilder.Build. All fields are non-nil after a successful
+// build. The caller is responsible for wiring these into the runtime bindings
+// and event-loop callbacks.
+type runtimeComponents struct {
+ engine *statsengine.Engine
+ streamBuf *streamrow.RingBuffer
+ streamSeq *streamrow.Sequencer
+ liveTrie *flamegraph.LiveTrie
+}
+
+// RuntimeBuilder encapsulates the allocation of per-trace-session runtime
+// components. Construct one with newRuntimeBuilder, then call Build to
+// produce a fresh runtimeComponents struct ready for wiring.
+type RuntimeBuilder struct {
+ // cfg holds the configuration used to size and configure the components.
+ cfg flags.Config
+}
+
+// newRuntimeBuilder creates a RuntimeBuilder configured from cfg.
+// The builder captures cfg at construction time; later cfg changes do not affect
+// a builder already created.
+func newRuntimeBuilder(cfg flags.Config) RuntimeBuilder {
+ return RuntimeBuilder{cfg: cfg}
+}
+
+// Build allocates a fresh set of runtime components for one trace session.
+// It creates a new stats engine, stream ring buffer, sequencer, and live trie
+// each time it is called; callers must not share the returned components across
+// concurrent trace sessions.
+func (b RuntimeBuilder) Build() runtimeComponents {
+ return runtimeComponents{
+ engine: statsengine.NewEngine(defaultEngineCapacity),
+ streamBuf: streamrow.NewRingBuffer(),
+ streamSeq: streamrow.NewSequencer(0),
+ liveTrie: flamegraph.NewLiveTrie(b.cfg.CollapsedFields, b.cfg.CountField),
+ }
+}