From 59a16d498bfd3fbd7f7cfb0dfde7dee110d5b7fd Mon Sep 17 00:00:00 2001 From: "red-hat-konflux-kflux-prd-rh02[bot]" <190377777+red-hat-konflux-kflux-prd-rh02[bot]@users.noreply.github.com> Date: Sun, 3 May 2026 08:38:00 +0000 Subject: [PATCH] Update module github.com/puzpuzpuz/xsync/v4 to v4.5.0 Signed-off-by: red-hat-konflux-kflux-prd-rh02 <190377777+red-hat-konflux-kflux-prd-rh02[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 +- .../github.com/puzpuzpuz/xsync/v4/README.md | 5 +- .../github.com/puzpuzpuz/xsync/v4/counter.go | 2 +- vendor/github.com/puzpuzpuz/xsync/v4/map.go | 121 +++++++----------- .../puzpuzpuz/xsync/v4/mpmcqueue.go | 26 ++-- .../github.com/puzpuzpuz/xsync/v4/rbmutex.go | 2 +- vendor/github.com/puzpuzpuz/xsync/v4/util.go | 16 +-- vendor/modules.txt | 2 +- 9 files changed, 75 insertions(+), 105 deletions(-) diff --git a/go.mod b/go.mod index e3f660eb1..a2387de8e 100644 --- a/go.mod +++ b/go.mod @@ -155,7 +155,7 @@ require ( github.com/prometheus/otlptranslator v1.0.0 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/prometheus/sigv4 v0.4.1 // indirect - github.com/puzpuzpuz/xsync/v4 v4.4.0 // indirect + github.com/puzpuzpuz/xsync/v4 v4.5.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/tidwall/gjson v1.18.0 // indirect diff --git a/go.sum b/go.sum index 49f9dee7a..334fe575b 100644 --- a/go.sum +++ b/go.sum @@ -446,8 +446,8 @@ github.com/prometheus/prometheus v0.311.2 h1:6fBxp93y08GAZGNT1o3bIhgV/AMYvBFfU+l github.com/prometheus/prometheus v0.311.2/go.mod h1:gjsCxTKtHO1Q8T9333u1s+lUR1OjPyM7ruuGH8RvVyo= github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuXs= github.com/prometheus/sigv4 v0.4.1/go.mod h1:eu+ZbRvsc5TPiHwqh77OWuCnWK73IdkETYY46P4dXOU= -github.com/puzpuzpuz/xsync/v4 v4.4.0 h1:vlSN6/CkEY0pY8KaB0yqo/pCLZvp9nhdbBdjipT4gWo= -github.com/puzpuzpuz/xsync/v4 v4.4.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= +github.com/puzpuzpuz/xsync/v4 v4.5.0 h1:vOSWu6b57/emh+L/Cw0BeQfvxa/cogFywXHeGUxQxAg= +github.com/puzpuzpuz/xsync/v4 v4.5.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/vendor/github.com/puzpuzpuz/xsync/v4/README.md b/vendor/github.com/puzpuzpuz/xsync/v4/README.md index 7b482add1..98a262f59 100644 --- a/vendor/github.com/puzpuzpuz/xsync/v4/README.md +++ b/vendor/github.com/puzpuzpuz/xsync/v4/README.md @@ -8,7 +8,7 @@ Concurrent data structures for Go. Aims to provide more scalable alternatives fo Apart from direct library dependencies, `xsync` data structures can also be met in-code in other libraries like [Otter](https://github.com/maypok86/otter/blob/8c526307556486ea0337280a4211135720bc29cc/internal/hashmap/map.go) caching library. -Covered with tests following the approach described [here](https://puzpuzpuz.dev/testing-concurrent-code-for-fun-and-profit). +Covered with concurrent stress tests following the approach described [here](https://puzpuzpuz.dev/testing-concurrent-code-for-fun-and-profit). ## Benchmarks @@ -132,7 +132,8 @@ Make sure to implement proper back-off strategy to handle failed optimistic oper A `MPMCQueue` is a bounded multi-producer multi-consumer concurrent queue. ```go -q := xsync.NewMPMCQueue[string](1024) +// capacity is rounded up to the next power of 2 (1000 -> 1024) +q := xsync.NewMPMCQueue[string](1000) // producer optimistically inserts an item into the queue // optimistic insertion attempt; doesn't block inserted := q.TryEnqueue("bar") diff --git a/vendor/github.com/puzpuzpuz/xsync/v4/counter.go b/vendor/github.com/puzpuzpuz/xsync/v4/counter.go index be3d7a9ae..1858788da 100644 --- a/vendor/github.com/puzpuzpuz/xsync/v4/counter.go +++ b/vendor/github.com/puzpuzpuz/xsync/v4/counter.go @@ -39,7 +39,7 @@ type cstripe struct { // NewCounter creates a new Counter instance. func NewCounter() *Counter { - nstripes := nextPowOf2(parallelism()) + nstripes := uint32(nextPowOf2(uint64(parallelism()))) c := Counter{ stripes: make([]cstripe, nstripes), mask: nstripes - 1, diff --git a/vendor/github.com/puzpuzpuz/xsync/v4/map.go b/vendor/github.com/puzpuzpuz/xsync/v4/map.go index 03066f926..b69984b79 100644 --- a/vendor/github.com/puzpuzpuz/xsync/v4/map.go +++ b/vendor/github.com/puzpuzpuz/xsync/v4/map.go @@ -79,16 +79,6 @@ const ( loadAndDeleteOp ) -type hashKind int - -const ( - hashKindComparable hashKind = iota - hashKindInt - hashKindInt64 - hashKindUint64 - hashKindUintptr -) - // Deprecated: use [Map] type MapOf[K comparable, V any] = Map[K, V] @@ -131,7 +121,7 @@ type Map[K comparable, V any] struct { resizeIdx atomic.Int64 minTableLen int growOnly bool - hashKind hashKind + intKey bool } type mapTable[K comparable, V any] struct { @@ -224,12 +214,12 @@ func NewMap[K comparable, V any](options ...func(*MapConfig)) *Map[K, V] { m := &Map[K, V]{} m.resizeCond = *sync.NewCond(&m.resizeMu) - m.hashKind = detectHashKind[K]() + m.intKey = detectIntKey[K]() var table *mapTable[K, V] if c.sizeHint <= defaultMinMapTableLen*entriesPerMapBucket { table = newMapTable[K, V](defaultMinMapTableLen, maphash.MakeSeed()) } else { - tableLen := nextPowOf2(uint32((float64(c.sizeHint) / entriesPerMapBucket) / mapLoadFactor)) + tableLen := nextPowOf2(uint64((float64(c.sizeHint) / entriesPerMapBucket) / mapLoadFactor)) table = newMapTable[K, V](int(tableLen), maphash.MakeSeed()) } m.minTableLen = len(table.buckets) @@ -238,44 +228,40 @@ func NewMap[K comparable, V any](options ...func(*MapConfig)) *Map[K, V] { return m } -// detectHashKind returns the appropriate hash kind for the key type. -func detectHashKind[K comparable]() hashKind { +// detectIntKey returns true if the key type is an integer type. +func detectIntKey[K comparable]() bool { var zero K switch any(zero).(type) { - case int: - return hashKindInt - case int64: - return hashKindInt64 - case uint64: - return hashKindUint64 - case uintptr: - return hashKindUintptr + case int, uint, uintptr, int64, uint64, int32, uint32, int16, uint16, int8, uint8: + return true default: - return hashKindComparable + return false } } -// hashUint64 computes a hash for integer keys using a 128-bit -// multiply-xorshift mixer (wyhash-style). The constant is xxHash's -// PRIME64_1 which provides excellent avalanche. This is significantly -// faster than maphash.Comparable for integer types. +// hashUint64 computes a hash for integer keys using two rounds of +// multiply-xorshift mixing (wyhash-style). +// This is significantly faster than maphash.Comparable for integer types. func hashUint64(seed, v uint64) uint64 { - hi, lo := bits.Mul64(v^seed, 0x9E3779B185EBCA87) - return hi ^ lo + hi, lo := bits.Mul64(v^seed, 0x2d358dccaa6c78a5) + hi2, lo2 := bits.Mul64(hi^lo, 0x8bb84b93962eacc9) + return hi2 ^ lo2 } -func hashKey[K comparable](k K, hashKind hashKind, seed maphash.Seed, intSeed uint64) uint64 { - switch hashKind { - case hashKindInt: - return hashUint64(intSeed, uint64(any(k).(int))) - case hashKindInt64: - return hashUint64(intSeed, uint64(any(k).(int64))) - case hashKindUint64: - return hashUint64(intSeed, any(k).(uint64)) - case hashKindUintptr: - return hashUint64(intSeed, uint64(any(k).(uintptr))) +// toUint64 reinterprets integer-like keys as uint64 for hashUint64. +// The size switch is folded per instantiated K by the compiler. +func toUint64[K any](k K) uint64 { + switch unsafe.Sizeof(k) { + case 8: + return *(*uint64)(unsafe.Pointer(&k)) + case 4: + return uint64(*(*uint32)(unsafe.Pointer(&k))) + case 2: + return uint64(*(*uint16)(unsafe.Pointer(&k))) + case 1: + return uint64(*(*uint8)(unsafe.Pointer(&k))) default: - return maphash.Comparable(seed, k) + panic("unreachable") } } @@ -322,18 +308,10 @@ func ToPlainMap[K comparable, V any](m *Map[K, V]) map[K]V { // The ok result indicates whether value was found in the map. func (m *Map[K, V]) Load(key K) (value V, ok bool) { table := m.table.Load() - // This is hot path, hence hand-inlined hashKey(). var hash uint64 - switch m.hashKind { - case hashKindInt: - hash = hashUint64(table.intSeed, uint64(any(key).(int))) - case hashKindInt64: - hash = hashUint64(table.intSeed, uint64(any(key).(int64))) - case hashKindUint64: - hash = hashUint64(table.intSeed, any(key).(uint64)) - case hashKindUintptr: - hash = hashUint64(table.intSeed, uint64(any(key).(uintptr))) - default: + if m.intKey { + hash = hashUint64(table.intSeed, toUint64(key)) + } else { hash = maphash.Comparable(table.seed, key) } h1 := h1(hash) @@ -385,18 +363,10 @@ func (m *Map[K, V]) Store(key K, value V) { ) table := m.table.Load() tableLen := len(table.buckets) - // This is hot path, hence hand-inlined hashKey(). var hash uint64 - switch m.hashKind { - case hashKindInt: - hash = hashUint64(table.intSeed, uint64(any(key).(int))) - case hashKindInt64: - hash = hashUint64(table.intSeed, uint64(any(key).(int64))) - case hashKindUint64: - hash = hashUint64(table.intSeed, any(key).(uint64)) - case hashKindUintptr: - hash = hashUint64(table.intSeed, uint64(any(key).(uintptr))) - default: + if m.intKey { + hash = hashUint64(table.intSeed, toUint64(key)) + } else { hash = maphash.Comparable(table.seed, key) } h1 := h1(hash) @@ -613,18 +583,10 @@ func (m *Map[K, V]) doCompute( ) table := m.table.Load() tableLen := len(table.buckets) - // This is hot path, hence hand-inlined hashKey(). var hash uint64 - switch m.hashKind { - case hashKindInt: - hash = hashUint64(table.intSeed, uint64(any(key).(int))) - case hashKindInt64: - hash = hashUint64(table.intSeed, uint64(any(key).(int64))) - case hashKindUint64: - hash = hashUint64(table.intSeed, any(key).(uint64)) - case hashKindUintptr: - hash = hashUint64(table.intSeed, uint64(any(key).(uintptr))) - default: + if m.intKey { + hash = hashUint64(table.intSeed, toUint64(key)) + } else { hash = maphash.Comparable(table.seed, key) } h1 := h1(hash) @@ -956,7 +918,7 @@ func (m *Map[K, V]) transfer(table, newTable *mapTable[K, V]) { // Visit all source buckets that map to this destination bucket. // When growing, runs once. When shrinking, runs twice. for srcIdx := i; srcIdx < tableLen; srcIdx += baseLen { - total += transferBucketUnsafe(&table.buckets[srcIdx], newTable, m.hashKind) + total += transferBucketUnsafe(&table.buckets[srcIdx], newTable, m.intKey) } } // The exact counter stripe doesn't matter here, so pick up the one @@ -969,7 +931,7 @@ func (m *Map[K, V]) transfer(table, newTable *mapTable[K, V]) { func transferBucketUnsafe[K comparable, V any]( b *bucketPadded, destTable *mapTable[K, V], - hashKind hashKind, + intKey bool, ) (copied int) { rootb := b rootb.mu.Lock() @@ -977,7 +939,12 @@ func transferBucketUnsafe[K comparable, V any]( for i := range entriesPerMapBucket { if eptr := b.entries[i]; eptr != nil { e := (*entry[K, V])(eptr) - hash := hashKey(e.key, hashKind, destTable.seed, destTable.intSeed) + var hash uint64 + if intKey { + hash = hashUint64(destTable.intSeed, toUint64(e.key)) + } else { + hash = maphash.Comparable(destTable.seed, e.key) + } bidx := uint64(len(destTable.buckets)-1) & h1(hash) destb := &destTable.buckets[bidx] appendToBucket(h2(hash), e, destb) diff --git a/vendor/github.com/puzpuzpuz/xsync/v4/mpmcqueue.go b/vendor/github.com/puzpuzpuz/xsync/v4/mpmcqueue.go index 2c7d25038..d9d42f60f 100644 --- a/vendor/github.com/puzpuzpuz/xsync/v4/mpmcqueue.go +++ b/vendor/github.com/puzpuzpuz/xsync/v4/mpmcqueue.go @@ -1,10 +1,13 @@ package xsync import ( + "math/bits" "sync/atomic" "unsafe" ) +const mpmcQueueMaxRequestedCapacity uint64 = uint64(1) << (bits.UintSize - 2) + // Deprecated: use [MPMCQueue]. type MPMCQueueOf[I any] = MPMCQueue[I] @@ -17,9 +20,11 @@ type MPMCQueueOf[I any] = MPMCQueue[I] // Based on the data structure from the following C++ library: // https://github.com/rigtorp/MPMCQueue type MPMCQueue[I any] struct { - cap uint64 - head uint64 - // Padding to prevent false sharing. + capMask uint64 + capShift uint64 + // Padding to isolate read-only fields from the head/tail counters. + _ [cacheLineSize - 16]byte + head uint64 _ [cacheLineSize - 8]byte tail uint64 _ [cacheLineSize - 8]byte @@ -50,14 +55,19 @@ func NewMPMCQueueOf[I any](capacity int) *MPMCQueue[I] { } // NewMPMCQueue creates a new MPMCQueue instance with the given -// capacity. +// capacity. The capacity is rounded up to the next power of 2. func NewMPMCQueue[I any](capacity int) *MPMCQueue[I] { if capacity < 1 { panic("capacity must be positive number") } + if uint64(capacity) > mpmcQueueMaxRequestedCapacity { + panic("capacity is too large") + } + capPow2 := nextPowOf2(uint64(capacity)) return &MPMCQueue[I]{ - cap: uint64(capacity), - slots: make([]slotPadded[I], capacity), + capMask: capPow2 - 1, + capShift: uint64(bits.TrailingZeros64(capPow2)), + slots: make([]slotPadded[I], capPow2), } } @@ -99,9 +109,9 @@ func (q *MPMCQueue[I]) TryDequeue() (item I, ok bool) { } func (q *MPMCQueue[I]) idx(i uint64) uint64 { - return i % q.cap + return i & q.capMask } func (q *MPMCQueue[I]) turn(i uint64) uint64 { - return i / q.cap + return i >> q.capShift } diff --git a/vendor/github.com/puzpuzpuz/xsync/v4/rbmutex.go b/vendor/github.com/puzpuzpuz/xsync/v4/rbmutex.go index 4032b76ff..26744150c 100644 --- a/vendor/github.com/puzpuzpuz/xsync/v4/rbmutex.go +++ b/vendor/github.com/puzpuzpuz/xsync/v4/rbmutex.go @@ -55,7 +55,7 @@ type rslot struct { // NewRBMutex creates a new RBMutex instance. func NewRBMutex() *RBMutex { - nslots := nextPowOf2(parallelism()) + nslots := uint32(nextPowOf2(uint64(parallelism()))) mu := RBMutex{ rslots: make([]rslot, nslots), rmask: nslots - 1, diff --git a/vendor/github.com/puzpuzpuz/xsync/v4/util.go b/vendor/github.com/puzpuzpuz/xsync/v4/util.go index 46fc68706..b600dbbd9 100644 --- a/vendor/github.com/puzpuzpuz/xsync/v4/util.go +++ b/vendor/github.com/puzpuzpuz/xsync/v4/util.go @@ -17,20 +17,12 @@ const ( cacheLineSize = 64 ) -// nextPowOf2 computes the next highest power of 2 of 32-bit v. -// Source: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -func nextPowOf2(v uint32) uint32 { - if v == 0 { +// nextPowOf2 computes the next highest power of 2 of 64-bit v. +func nextPowOf2(v uint64) uint64 { + if v <= 1 { return 1 } - v-- - v |= v >> 1 - v |= v >> 2 - v |= v >> 4 - v |= v >> 8 - v |= v >> 16 - v++ - return v + return 1 << bits.Len64(v-1) } func parallelism() uint32 { diff --git a/vendor/modules.txt b/vendor/modules.txt index 5c32211cd..1080dba61 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -830,7 +830,7 @@ github.com/prometheus/prometheus/web/api/v1 # github.com/prometheus/sigv4 v0.4.1 ## explicit; go 1.24.0 github.com/prometheus/sigv4 -# github.com/puzpuzpuz/xsync/v4 v4.4.0 +# github.com/puzpuzpuz/xsync/v4 v4.5.0 ## explicit; go 1.24 github.com/puzpuzpuz/xsync/v4 # github.com/santhosh-tekuri/jsonschema/v6 v6.0.2