on main help sign in
refactor: use csync.Map for regex caches (#2187)
7f3d7c7Carlos Alexandro Becker
internal/agent/tools/grep.go+13−36
@@ -15,64 +15,41 @@ import (
15 "regexp"
15 "regexp"
16 "sort"
16 "sort"
17 "strings"
17 "strings"
18− "sync"
19 "time"
18 "time"
20
19
21 "charm.land/fantasy"
20 "charm.land/fantasy"
21+ "github.com/charmbracelet/crush/internal/csync"
22 "github.com/charmbracelet/crush/internal/fsext"
22 "github.com/charmbracelet/crush/internal/fsext"
23)
23)
24
24
25// regexCache provides thread-safe caching of compiled regex patterns
25// regexCache provides thread-safe caching of compiled regex patterns
26type regexCache struct {
26type regexCache struct {
27− cache map[string]*regexp.Regexp
27+ *csync.Map[string, *regexp.Regexp]
28− mu sync.RWMutex
29}
28}
30
29
31// newRegexCache creates a new regex cache
30// newRegexCache creates a new regex cache
32func newRegexCache() *regexCache {
31func newRegexCache() *regexCache {
33 return &regexCache{
32 return &regexCache{
34− cache: make(map[string]*regexp.Regexp),
33+ Map: csync.NewMap[string, *regexp.Regexp](),
35 }
34 }
36}
35}
37
36
38// get retrieves a compiled regex from cache or compiles and caches it
37// get retrieves a compiled regex from cache or compiles and caches it
39func (rc *regexCache) get(pattern string) (*regexp.Regexp, error) {
38func (rc *regexCache) get(pattern string) (*regexp.Regexp, error) {
40− // Try to get from cache first (read lock)
39+ var rerr error
41− rc.mu.RLock()
40+ return rc.GetOrSet(pattern, func() *regexp.Regexp {
42− if regex, exists := rc.cache[pattern]; exists {
43− rc.mu.RUnlock()
44− return regex, nil
45− }
46− rc.mu.RUnlock()
47−
48− // Compile the regex (write lock)
49− rc.mu.Lock()
50− defer rc.mu.Unlock()
51−
52− // Double-check in case another goroutine compiled it while we waited
53− if regex, exists := rc.cache[pattern]; exists {
54− return regex, nil
55− }
56−
57− // Compile and cache the regex
58− regex, err := regexp.Compile(pattern)
41+ regex, err := regexp.Compile(pattern)
59− if err != nil {
42+ if err != nil {
60− return nil, err
43+ rerr = err
61− }
44+ }
62−
63− rc.cache[pattern] = regex
64− return regex, nil
45+ return regex
46+ }), rerr
65}
47}
66
48
67// ResetCache clears compiled regex caches to prevent unbounded growth across sessions.
49// ResetCache clears compiled regex caches to prevent unbounded growth across sessions.
68func ResetCache() {
50func ResetCache() {
69− searchRegexCache.mu.Lock()
51+ searchRegexCache.Reset(map[string]*regexp.Regexp{})
70− clear(searchRegexCache.cache)
71− searchRegexCache.mu.Unlock()
72−
73− globRegexCache.mu.Lock()
52+ globRegexCache.Reset(map[string]*regexp.Regexp{})
74− clear(globRegexCache.cache)
75− globRegexCache.mu.Unlock()
76}
53}
77
54
78// Global regex cache instances
55// Global regex cache instances