diff --git a/.pre-commit-ci-config.yaml b/.pre-commit-ci-config.yaml index 2e6c7a4..433541e 100644 --- a/.pre-commit-ci-config.yaml +++ b/.pre-commit-ci-config.yaml @@ -21,7 +21,7 @@ repos: types: [file, yaml] entry: yamllint --strict -f parsable - repo: https://github.com/streetsidesoftware/cspell-cli - rev: v9.4.0 + rev: v9.7.0 hooks: # Spell check changed files - id: cspell diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b7b709a..8823b92 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,7 +30,7 @@ repos: hooks: - id: hadolint-docker - repo: https://github.com/streetsidesoftware/cspell-cli - rev: v9.4.0 + rev: v9.7.0 hooks: # Spell check changed files - id: cspell diff --git a/.project-settings.env b/.project-settings.env index 9e6a104..f59b124 100644 --- a/.project-settings.env +++ b/.project-settings.env @@ -1,5 +1,5 @@ GOLANGCI_LINT_VERSION=v2.10.1 -BUF_VERSION=v1.65.0 +BUF_VERSION=v1.66.0 GO_VERSION=1.26.0 GCI_PREFIX=github.com/hyp3rd/hypercache -PROTO_ENABLED=true +PROTO_ENABLED=false diff --git a/Makefile b/Makefile index a6bb1e5..98d866c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ include .project-settings.env GOLANGCI_LINT_VERSION ?= v2.10.1 -BUF_VERSION ?= v1.65.0 +BUF_VERSION ?= v1.66.0 GO_VERSION ?= 1.26.0 GCI_PREFIX ?= github.com/hyp3rd/hypercache PROTO_ENABLED ?= true diff --git a/go.mod b/go.mod index b0734b7..2e5918e 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/valyala/fasthttp v1.69.0 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/crypto v0.48.0 // indirect - golang.org/x/net v0.50.0 // indirect + golang.org/x/net v0.51.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 2c89f2a..7e57c0d 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= diff --git a/hypercache.go b/hypercache.go index f1e6a8b..fbf7680 100644 --- a/hypercache.go +++ b/hypercache.go @@ -365,6 +365,15 @@ func (hyperCache *HyperCache[T]) startBackgroundJobs(ctx context.Context) { jobsCtx, cancel := context.WithCancel(ctx) hyperCache.bgCancel = cancel + // Ensure shutdown signaling always drives context cancellation, even when + // stop consumers race to read the stop channel. + go func(stop <-chan bool, done <-chan struct{}, cancel context.CancelFunc) { + select { + case <-stop: + cancel() + case <-done: + } + }(hyperCache.stop, jobsCtx.Done(), cancel) hyperCache.startExpirationRoutine(jobsCtx) hyperCache.startEvictionRoutine(jobsCtx) @@ -415,6 +424,13 @@ func (hyperCache *HyperCache[T]) handleExpirationSelect(ctx context.Context, tic case <-hyperCache.evictCh: // manual eviction trigger hyperCache.evictionLoop(ctx) + case <-ctx.Done(): + if tick != nil { + tick.Stop() + } + + return true + case <-hyperCache.stop: if tick != nil { tick.Stop() @@ -439,6 +455,11 @@ func (hyperCache *HyperCache[T]) startEvictionRoutine(ctx context.Context) { select { case <-tick.C: hyperCache.evictionLoop(ctx) + case <-ctx.Done(): + tick.Stop() + + return + case <-hyperCache.stop: tick.Stop() @@ -967,17 +988,16 @@ const ( // Stop function stops the expiration and eviction loops and closes the stop channel. func (hyperCache *HyperCache[T]) Stop(ctx context.Context) error { - // Stop the expiration and eviction loops - wg := sync.WaitGroup{} - - wg.Go(func() { - hyperCache.stop <- true - }) - - wg.Wait() + // Best-effort stop signal for listeners that still rely on stop channel. + select { + case hyperCache.stop <- true: + default: + } if hyperCache.bgCancel != nil { hyperCache.bgCancel() + + hyperCache.bgCancel = nil } hyperCache.once = sync.Once{} @@ -992,8 +1012,6 @@ func (hyperCache *HyperCache[T]) Stop(ctx context.Context) error { // Handle error return err } - - cancel() } return nil