limiting: Make SemaphoreLimiter.Acquire() zero-alloc
Some checks failed
DCO action / DCO (pull_request) Failing after 24s
Tests and linters / Run gofumpt (pull_request) Successful in 29s
Tests and linters / Tests (pull_request) Successful in 42s
Vulncheck / Vulncheck (pull_request) Successful in 41s
Tests and linters / Tests with -race (pull_request) Failing after 45s
Tests and linters / Staticcheck (pull_request) Successful in 1m1s
Tests and linters / Lint (pull_request) Successful in 1m40s
Pre-commit hooks / Pre-commit (pull_request) Successful in 1m42s
Tests and linters / gopls check (pull_request) Successful in 1m56s

Previously, `Acquire` on exising key did 1 allocation because
`func() { sem.Release() }` was a closure capturing different variables.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
Evgenii Stratonikov 2025-02-26 13:18:40 +03:00
parent 356851eed3
commit 9721fcbac0
Signed by: fyrchik
SSH key fingerprint: SHA256:m/TTwCzjnRkXgnzEx9X92ccxy1CcVeinOgDb3NPWWmg
2 changed files with 22 additions and 1 deletions

View file

@ -69,7 +69,7 @@ func (lr *SemaphoreLimiter) Acquire(key string) (ReleaseFunc, bool) {
}
if ok := sem.Acquire(); ok {
return func() { sem.Release() }, true
return sem.Release, true
}
return nil, false
}

View file

@ -136,3 +136,24 @@ func resetFailCounts(testCases []*testCase) {
tc.failCount.Store(0)
}
}
func TestLimiterZeroAlloc(t *testing.T) {
const key = "key"
s, err := limiting.NewSemaphoreLimiter([]limiting.KeyLimit{{Keys: []string{key}, Limit: 1}})
require.NoError(t, err)
testAllocs := func(t *testing.T, key string) float64 {
return testing.AllocsPerRun(100, func() {
release, ok := s.Acquire(key)
require.True(t, ok)
release()
})
}
t.Run("existing key", func(t *testing.T) {
require.Zero(t, testAllocs(t, key))
})
t.Run("missing key", func(t *testing.T) {
require.Zero(t, testAllocs(t, "missing "+key))
})
}