Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-24 - Cache static maps at struct level to avoid allocation in performance-sensitive areas
**Learning:** In performance-sensitive areas (like color mapping for the status line where multiple sections run in parallel), avoid repeated map allocations per plugin execution. Generating the color map per execution causes unnecessary GC pressure. Global variables carry risk of shared mutable state and require expensive deep copying.
**Action:** Cache static data maps at the instance level (e.g., in the `StatusLine` struct) using `sync.Once` during initialization (e.g. within an accessor method) to ensure thread-safety, prevent nil map panics, minimize allocations, and preserve backward compatibility with struct literals.
12 changes: 11 additions & 1 deletion internal/statusline/statusline.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ type StatusLine struct {
isIdle bool
bashPlugins []plugin.Plugin // Cached discovered bash plugins
bashPluginsOnce sync.Once
colorsMap map[string]string
colorsOnce sync.Once
Comment on lines +38 to +39
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Caching the color map at the StatusLine instance level still results in one allocation per instance. Since the color map is derived from static ANSI constants, it is a prime candidate for a package-level singleton in internal/colors. Moving the cache there (using a package-level sync.Once) would reduce allocations to exactly once per process lifetime, which is more efficient than the current per-instance approach and keeps the StatusLine struct cleaner.

}

// getColors returns the color map, initializing it once if needed
func (sl *StatusLine) getColors() map[string]string {
sl.colorsOnce.Do(func() {
sl.colorsMap = colors.ColorMap()
})
return sl.colorsMap
}
Comment on lines +43 to 48
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Sharing the same map instance across parallel plugin executions introduces a potential race condition. Since renderLine executes sections in parallel goroutines, multiple plugins will concurrently access the same sl.colorsMap reference. If a native plugin implementation modifies this map, it can cause a concurrent map read/write panic during JSON marshaling in other goroutines. While this change successfully reduces allocations, it sacrifices the isolation previously provided by colors.ColorMap(). Consider documenting that the Colors map must be treated as read-only by all plugins, or use a more robust approach to ensure immutability.


// New creates a new StatusLine renderer
Expand Down Expand Up @@ -634,7 +644,7 @@ func (sl *StatusLine) runPlugin(name string) string {
ContextWindowSize: sl.input.Context.ContextWindow,
},
Config: sl.getPluginConfig(name),
Colors: colors.ColorMap(),
Colors: sl.getColors(),
}

// Try native plugin first (much faster - no subprocess)
Expand Down
Loading