diff --git a/lib/bytesutil/fast_string_matcher.go b/lib/bytesutil/fast_string_matcher.go index d2933563bf..619bb325d7 100644 --- a/lib/bytesutil/fast_string_matcher.go +++ b/lib/bytesutil/fast_string_matcher.go @@ -62,10 +62,8 @@ func (fsm *FastStringMatcher) Match(s string) bool { s = strings.Clone(s) fsm.m.Store(s, e) - if atomic.LoadUint64(&fsm.lastCleanupTime)+61 < ct { - // Perform a global cleanup for fsm.m by removing items, which weren't accessed - // during the last 5 minutes. - atomic.StoreUint64(&fsm.lastCleanupTime, ct) + if needCleanup(&fsm.lastCleanupTime, ct) { + // Perform a global cleanup for fsm.m by removing items, which weren't accessed during the last 5 minutes. m := &fsm.m m.Range(func(k, v interface{}) bool { e := v.(*fsmEntry) @@ -78,3 +76,14 @@ func (fsm *FastStringMatcher) Match(s string) bool { return b } + +func needCleanup(lastCleanupTime *uint64, currentTime uint64) bool { + lct := atomic.LoadUint64(lastCleanupTime) + if lct+61 >= currentTime { + return false + } + // Atomically compare and swap the current time with the lastCleanupTime + // in order to guarantee that only a single goroutine out of multiple + // concurrently executing goroutines gets true from the call. + return atomic.CompareAndSwapUint64(lastCleanupTime, lct, currentTime) +} diff --git a/lib/bytesutil/fast_string_matcher_test.go b/lib/bytesutil/fast_string_matcher_test.go index 7ea7925ce1..f9c7e29f0d 100644 --- a/lib/bytesutil/fast_string_matcher_test.go +++ b/lib/bytesutil/fast_string_matcher_test.go @@ -23,3 +23,27 @@ func TestFastStringMatcher(t *testing.T) { f("a_b-C", false) f("foobar", true) } + +func TestNeedCleanup(t *testing.T) { + f := func(lastCleanupTime, currentTime uint64, resultExpected bool) { + t.Helper() + lct := lastCleanupTime + result := needCleanup(&lct, currentTime) + if result != resultExpected { + t.Fatalf("unexpected result for needCleanup(%d, %d); got %v; want %v", lastCleanupTime, currentTime, result, resultExpected) + } + if result { + if lct != currentTime { + t.Fatalf("unexpected value for lct; got %d; want currentTime=%d", lct, currentTime) + } + } else { + if lct != lastCleanupTime { + t.Fatalf("unexpected value for lct; got %d; want lastCleanupTime=%d", lct, lastCleanupTime) + } + } + } + f(0, 0, false) + f(0, 61, false) + f(0, 62, true) + f(10, 100, true) +} diff --git a/lib/bytesutil/fast_string_transformer.go b/lib/bytesutil/fast_string_transformer.go index c131d15e85..670f2c3f81 100644 --- a/lib/bytesutil/fast_string_transformer.go +++ b/lib/bytesutil/fast_string_transformer.go @@ -67,10 +67,8 @@ func (fst *FastStringTransformer) Transform(s string) string { } fst.m.Store(s, e) - if atomic.LoadUint64(&fst.lastCleanupTime)+61 < ct { - // Perform a global cleanup for fst.m by removing items, which weren't accessed - // during the last 5 minutes. - atomic.StoreUint64(&fst.lastCleanupTime, ct) + if needCleanup(&fst.lastCleanupTime, ct) { + // Perform a global cleanup for fst.m by removing items, which weren't accessed during the last 5 minutes. m := &fst.m m.Range(func(k, v interface{}) bool { e := v.(*fstEntry) diff --git a/lib/bytesutil/internstring.go b/lib/bytesutil/internstring.go index 45c9a63285..138b007951 100644 --- a/lib/bytesutil/internstring.go +++ b/lib/bytesutil/internstring.go @@ -30,10 +30,8 @@ func InternString(s string) string { } internStringsMap.Store(sCopy, e) - if atomic.LoadUint64(&internStringsMapLastCleanupTime)+61 < ct { - // Perform a global cleanup for internStringsMap by removing items, which weren't accessed - // during the last 5 minutes. - atomic.StoreUint64(&internStringsMapLastCleanupTime, ct) + if needCleanup(&internStringsMapLastCleanupTime, ct) { + // Perform a global cleanup for internStringsMap by removing items, which weren't accessed during the last 5 minutes. m := &internStringsMap m.Range(func(k, v interface{}) bool { e := v.(*ismEntry)