diff --git a/go.mod b/go.mod index c3d0b4a4a..6bdd5981c 100644 --- a/go.mod +++ b/go.mod @@ -16,12 +16,13 @@ require ( github.com/msaf1980/go-expirecache v0.0.2 github.com/msaf1980/go-metrics v0.0.14 github.com/msaf1980/go-stringutils v0.1.4 + github.com/msaf1980/go-syncutils v0.0.3 github.com/msaf1980/go-timeutils v0.0.3 github.com/pelletier/go-toml v1.9.5 github.com/prometheus/client_golang v1.13.1 github.com/prometheus/client_model v0.3.0 github.com/prometheus/prometheus v0.40.2 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 go.uber.org/zap v1.21.0 ) diff --git a/go.sum b/go.sum index 7ee1bdbdd..675376882 100644 --- a/go.sum +++ b/go.sum @@ -585,6 +585,10 @@ github.com/msaf1980/go-metrics v0.0.14/go.mod h1:8VcR8MdyvIJpcVLOVFKbhb27+60tXy0 github.com/msaf1980/go-stringutils v0.1.2/go.mod h1:AxmV/6JuQUAtZJg5XmYATB5ZwCWgtpruVHY03dswRf8= github.com/msaf1980/go-stringutils v0.1.4 h1:UwsIT0hplHVucqbknk3CoNqKkmIuSHhsbBldXxyld5U= github.com/msaf1980/go-stringutils v0.1.4/go.mod h1:AxmV/6JuQUAtZJg5XmYATB5ZwCWgtpruVHY03dswRf8= +github.com/msaf1980/go-syncutils v0.0.2 h1:F7lTtojuZUHFH9Cs6yRz4SRnvmttSV2qD6nEvseCFVg= +github.com/msaf1980/go-syncutils v0.0.2/go.mod h1:zoZwQNkDATcfKq5lQPK6dmJT7Z01COxw/vd8bcJyC9w= +github.com/msaf1980/go-syncutils v0.0.3 h1:bd6+yTSB8/CmpG7M6j1gq5sJMyPqecjJcBf19s2Y6u4= +github.com/msaf1980/go-syncutils v0.0.3/go.mod h1:zoZwQNkDATcfKq5lQPK6dmJT7Z01COxw/vd8bcJyC9w= github.com/msaf1980/go-timeutils v0.0.3 h1:c0NIpJBcU6KoMeMCPdnbGFcaP4sm7VCwoW1cdgsmUkU= github.com/msaf1980/go-timeutils v0.0.3/go.mod h1:r252j2O/ZLuwNMp/rlSYhbQdxg6glZ3MzgvskE/ItGY= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -713,8 +717,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg= diff --git a/vendor/github.com/msaf1980/go-syncutils/LICENSE b/vendor/github.com/msaf1980/go-syncutils/LICENSE new file mode 100644 index 000000000..4822803ac --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Michail Safronov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/bool.go b/vendor/github.com/msaf1980/go-syncutils/atomic/bool.go new file mode 100644 index 000000000..49f70cc1e --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/bool.go @@ -0,0 +1,53 @@ +package atomic + +import ( + "strconv" + "sync/atomic" +) + +// Bool is an atomic type-safe wrapper for bool values. +type Bool struct { + _ noCopy + + v uint32 +} + +// NewBool creates a new Bool. +func NewBool(val bool) *Bool { + return &Bool{v: boolToUint32(val)} +} + +// Load atomically loads the wrapped bool. +func (x *Bool) Load() bool { + return truthy(atomic.LoadUint32(&x.v)) +} + +// Store atomically stores the passed bool. +func (x *Bool) Store(val bool) { + atomic.StoreUint32(&x.v, boolToUint32(val)) +} + +// CompareAndSwap is an atomic compare-and-swap for bool values. +func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) { + return atomic.CompareAndSwapUint32(&x.v, boolToUint32(old), boolToUint32(new)) +} + +// Swap atomically stores the given bool and returns the old value. +func (x *Bool) Swap(val bool) (old bool) { + return truthy(atomic.SwapUint32(&x.v, boolToUint32(val))) +} + +// Toggle atomically negates the Boolean and returns the previous value. +func (b *Bool) Toggle() (old bool) { + for { + old := b.Load() + if b.CompareAndSwap(old, !old) { + return old + } + } +} + +// String encodes the wrapped value as a string. +func (b *Bool) String() string { + return strconv.FormatBool(b.Load()) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/bool_ext.go b/vendor/github.com/msaf1980/go-syncutils/atomic/bool_ext.go new file mode 100644 index 000000000..cfc485186 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/bool_ext.go @@ -0,0 +1,14 @@ +package atomic + +//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go + +func truthy(n uint32) bool { + return n == 1 +} + +func boolToUint32(b bool) uint32 { + if b { + return 1 + } + return 0 +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/duration.go b/vendor/github.com/msaf1980/go-syncutils/atomic/duration.go new file mode 100644 index 000000000..78000f0db --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/duration.go @@ -0,0 +1,75 @@ +// @generated Code generated by gen-atomicwrapper. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "sync/atomic" + "time" +) + +// Duration is an atomic type-safe wrapper for time.Duration values. +type Duration struct { + _ noCopy + + v int64 +} + +// NewDuration creates a new Duration. +func NewDuration(val time.Duration) *Duration { + return &Duration{v: val.Nanoseconds()} +} + +// Load atomically loads the wrapped time.Duration. +func (x *Duration) Load() time.Duration { + return time.Duration(atomic.LoadInt64(&x.v)) +} + +// Store atomically stores the passed time.Duration. +func (x *Duration) Store(val time.Duration) { + atomic.StoreInt64(&x.v, val.Nanoseconds()) +} + +// CompareAndSwap is an atomic compare-and-swap for time.Duration values. +func (x *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) { + return atomic.CompareAndSwapInt64(&x.v, old.Nanoseconds(), new.Nanoseconds()) +} + +// Swap atomically stores the given time.Duration and returns the old value. +func (x *Duration) Swap(val time.Duration) (old time.Duration) { + return time.Duration(atomic.SwapInt64(&x.v, val.Nanoseconds())) +} + +// Add atomically adds to the wrapped time.Duration and returns the new value. +func (x *Duration) Add(delta time.Duration) time.Duration { + return time.Duration(atomic.AddInt64(&x.v, delta.Nanoseconds())) +} + +// Sub atomically subtracts from the wrapped time.Duration and returns the new value. +func (x *Duration) Sub(delta time.Duration) time.Duration { + return time.Duration(atomic.AddInt64(&x.v, -delta.Nanoseconds())) +} + +// String encodes the wrapped value as a string. +func (x *Duration) String() string { + return x.Load().String() +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/error.go b/vendor/github.com/msaf1980/go-syncutils/atomic/error.go new file mode 100644 index 000000000..4a3ef3329 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/error.go @@ -0,0 +1,77 @@ +// @generated Code generated by gen-atomicwrapper. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +// Error is an atomic type-safe wrapper for error values. +type Error struct { + _ noCopy + + v Value +} + +var _zeroError error + +// NewError creates a new Error. +func NewError(val error) *Error { + x := &Error{} + if val != _zeroError { + x.Store(val) + } + return x +} + +// Load atomically loads the wrapped error. +func (x *Error) Load() error { + return unpackError(x.v.Load()) +} + +// Store atomically stores the passed error. +func (x *Error) Store(val error) { + x.v.Store(packError(val)) +} + +// StoreIfNil atomically stores the passed error (if no previous error). +func (x *Error) StoreIfNil(val error) (swapped bool) { + return x.CompareAndSwap(nil, val) +} + +// CompareAndSwap is an atomic compare-and-swap for error values. +func (x *Error) CompareAndSwap(old, new error) (swapped bool) { + if x.v.CompareAndSwap(packError(old), packError(new)) { + return true + } + + if old == _zeroError { + // If the old value is the empty value, then it's possible the + // underlying Value hasn't been set and is nil, so retry with nil. + return x.v.CompareAndSwap(nil, packError(new)) + } + + return false +} + +// Swap atomically stores the given error and returns the old +// value. +func (x *Error) Swap(val error) (old error) { + return unpackError(x.v.Swap(packError(val))) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/error_ext.go b/vendor/github.com/msaf1980/go-syncutils/atomic/error_ext.go new file mode 100644 index 000000000..a46f98656 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/error_ext.go @@ -0,0 +1,37 @@ +// Copyright (c) 2020-2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +// atomic.Value panics on nil inputs, or if the underlying type changes. +// Stabilize by always storing a custom struct that we control. + +type packedError struct{ Value error } + +func packError(v error) interface{} { + return packedError{v} +} + +func unpackError(v interface{}) error { + if err, ok := v.(packedError); ok { + return err.Value + } + return nil +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/float32.go b/vendor/github.com/msaf1980/go-syncutils/atomic/float32.go new file mode 100644 index 000000000..9f49ae902 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/float32.go @@ -0,0 +1,103 @@ +// @generated Code generated by gen-atomicwrapper. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "math" + "strconv" + "sync/atomic" +) + +// Float32 is an atomic type-safe wrapper for float32 values. +type Float32 struct { + _ noCopy + + v uint32 +} + +var _zeroFloat32 float32 + +// NewFloat32 creates a new Float32. +func NewFloat32(val float32) *Float32 { + if val == _zeroFloat32 { + return &Float32{} + } else { + return &Float32{v: math.Float32bits(val)} + } +} + +// Load atomically loads the wrapped float32. +func (x *Float32) Load() float32 { + return math.Float32frombits(atomic.LoadUint32(&x.v)) +} + +// Store atomically stores the passed float32. +func (x *Float32) Store(val float32) { + atomic.StoreUint32(&x.v, math.Float32bits(val)) +} + +// Swap atomically stores the given float32 and returns the old value. +func (x *Float32) Swap(val float32) (old float32) { + return math.Float32frombits(atomic.SwapUint32(&x.v, math.Float32bits(val))) +} + +// Add atomically adds to the wrapped float32 and returns the new value. +func (x *Float32) Add(delta float32) float32 { + for { + old := x.Load() + new := old + delta + if x.CompareAndSwap(old, new) { + return new + } + } +} + +// Sub atomically subtracts from the wrapped float32 and returns the new value. +func (x *Float32) Sub(delta float32) float32 { + return x.Add(-delta) +} + +// CompareAndSwap is an atomic compare-and-swap for float32 values. +// +// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators +// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN. +// This avoids typical CompareAndSwap loops from blocking forever, e.g., +// +// for { +// old := atom.Load() +// new = f(old) +// if atom.CompareAndSwap(old, new) { +// break +// } +// } +// +// If CompareAndSwap did not match NaN to match, then the above would loop forever. +func (x *Float32) CompareAndSwap(old, new float32) (swapped bool) { + return atomic.CompareAndSwapUint32(&x.v, math.Float32bits(old), math.Float32bits(new)) +} + +// String encodes the wrapped value as a string. +func (f *Float32) String() string { + // 'g' is the behavior for floats with %v. + return strconv.FormatFloat(float64(f.Load()), 'g', -1, 32) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/float64.go b/vendor/github.com/msaf1980/go-syncutils/atomic/float64.go new file mode 100644 index 000000000..23fe853e0 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/float64.go @@ -0,0 +1,103 @@ +// @generated Code generated by gen-atomicwrapper. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "math" + "strconv" + "sync/atomic" +) + +// Float64 is an atomic type-safe wrapper for float64 values. +type Float64 struct { + _ noCopy + + v uint64 +} + +var _zeroFloat64 float64 + +// NewFloat64 creates a new Float64. +func NewFloat64(val float64) *Float64 { + if val == _zeroFloat64 { + return &Float64{} + } else { + return &Float64{v: math.Float64bits(val)} + } +} + +// Load atomically loads the wrapped float64. +func (x *Float64) Load() float64 { + return math.Float64frombits(atomic.LoadUint64(&x.v)) +} + +// Store atomically stores the passed float64. +func (x *Float64) Store(val float64) { + atomic.StoreUint64(&x.v, math.Float64bits(val)) +} + +// Swap atomically stores the given float64 and returns the old value. +func (x *Float64) Swap(val float64) (old float64) { + return math.Float64frombits(atomic.SwapUint64(&x.v, math.Float64bits(val))) +} + +// Add atomically adds to the wrapped float64 and returns the new value. +func (x *Float64) Add(delta float64) float64 { + for { + old := x.Load() + new := old + delta + if x.CompareAndSwap(old, new) { + return new + } + } +} + +// Sub atomically subtracts from the wrapped float64 and returns the new value. +func (x *Float64) Sub(delta float64) float64 { + return x.Add(-delta) +} + +// CompareAndSwap is an atomic compare-and-swap for float64 values. +// +// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators +// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN. +// This avoids typical CompareAndSwap loops from blocking forever, e.g., +// +// for { +// old := atom.Load() +// new = f(old) +// if atom.CompareAndSwap(old, new) { +// break +// } +// } +// +// If CompareAndSwap did not match NaN to match, then the above would loop forever. +func (x *Float64) CompareAndSwap(old, new float64) (swapped bool) { + return atomic.CompareAndSwapUint64(&x.v, math.Float64bits(old), math.Float64bits(new)) +} + +// String encodes the wrapped value as a string. +func (f *Float64) String() string { + // 'g' is the behavior for floats with %v. + return strconv.FormatFloat(float64(f.Load()), 'g', -1, 32) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/int32.go b/vendor/github.com/msaf1980/go-syncutils/atomic/int32.go new file mode 100644 index 000000000..375798d2e --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/int32.go @@ -0,0 +1,86 @@ +// @generated Code generated by gen-atomicint. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "strconv" + "sync/atomic" +) + +// Int32 is an atomic wrapper around int32. +type Int32 struct { + _ noCopy + + v int32 +} + +// NewInt32 creates a new Int32. +func NewInt32(val int32) *Int32 { + return &Int32{v: val} +} + +// Load atomically loads the wrapped value. +func (i *Int32) Load() int32 { + return atomic.LoadInt32(&i.v) +} + +// Add atomically adds to the wrapped int32 and returns the new value. +func (i *Int32) Add(delta int32) int32 { + return atomic.AddInt32(&i.v, delta) +} + +// Sub atomically subtracts from the wrapped int32 and returns the new value. +func (i *Int32) Sub(delta int32) int32 { + return atomic.AddInt32(&i.v, -delta) +} + +// Inc atomically increments the wrapped int32 and returns the new value. +func (i *Int32) Inc() int32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Int32) Dec() int32 { + return i.Sub(1) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) { + return atomic.CompareAndSwapInt32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int32) Store(val int32) { + atomic.StoreInt32(&i.v, val) +} + +// Swap atomically swaps the wrapped int32 and returns the old value. +func (i *Int32) Swap(val int32) (old int32) { + return atomic.SwapInt32(&i.v, val) +} + +// String encodes the wrapped value as a string. +func (i *Int32) String() string { + v := i.Load() + return strconv.FormatInt(int64(v), 10) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/int64.go b/vendor/github.com/msaf1980/go-syncutils/atomic/int64.go new file mode 100644 index 000000000..c9954bb5d --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/int64.go @@ -0,0 +1,86 @@ +// @generated Code generated by gen-atomicint. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "strconv" + "sync/atomic" +) + +// Int64 is an atomic wrapper around int64. +type Int64 struct { + _ noCopy + + v int64 +} + +// NewInt64 creates a new Int64. +func NewInt64(val int64) *Int64 { + return &Int64{v: val} +} + +// Load atomically loads the wrapped value. +func (i *Int64) Load() int64 { + return atomic.LoadInt64(&i.v) +} + +// Add atomically adds to the wrapped int64 and returns the new value. +func (i *Int64) Add(delta int64) int64 { + return atomic.AddInt64(&i.v, delta) +} + +// Sub atomically subtracts from the wrapped int64 and returns the new value. +func (i *Int64) Sub(delta int64) int64 { + return atomic.AddInt64(&i.v, -delta) +} + +// Inc atomically increments the wrapped int64 and returns the new value. +func (i *Int64) Inc() int64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int64 and returns the new value. +func (i *Int64) Dec() int64 { + return i.Sub(1) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) { + return atomic.CompareAndSwapInt64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int64) Store(val int64) { + atomic.StoreInt64(&i.v, val) +} + +// Swap atomically swaps the wrapped int64 and returns the old value. +func (i *Int64) Swap(val int64) (old int64) { + return atomic.SwapInt64(&i.v, val) +} + +// String encodes the wrapped value as a string. +func (i *Int64) String() string { + v := i.Load() + return strconv.FormatInt(int64(v), 10) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/nocopy.go b/vendor/github.com/msaf1980/go-syncutils/atomic/nocopy.go new file mode 100644 index 000000000..f1814ce90 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/nocopy.go @@ -0,0 +1,14 @@ +package atomic + +// noCopy may be added to structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go118.go b/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go118.go new file mode 100644 index 000000000..c5e606e91 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go118.go @@ -0,0 +1,11 @@ +//go:build go1.18 +// +build go1.18 + +package atomic + +import "fmt" + +// String returns a human readable representation of a Pointer's underlying value. +func (p *Pointer[T]) String() string { + return fmt.Sprint(p.Load()) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go118_pre119.go b/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go118_pre119.go new file mode 100644 index 000000000..09d212ef7 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go118_pre119.go @@ -0,0 +1,46 @@ +//go:build go1.18 && !go1.19 +// +build go1.18,!go1.19 + +package atomic + +import "unsafe" + +type Pointer[T any] struct { + // Mention *T in a field to disallow conversion between Pointer types. + // See go.dev/issue/56603 for more details. + // Use *T, not T, to avoid spurious recursive type definition errors. + _ [0]*T + + _ noCopy + + p UnsafePointer +} + +// NewPointer creates a new Pointer. +func NewPointer[T any](v *T) *Pointer[T] { + var p Pointer[T] + if v != nil { + p.p.Store(unsafe.Pointer(v)) + } + return &p +} + +// Load atomically loads the wrapped value. +func (p *Pointer[T]) Load() *T { + return (*T)(p.p.Load()) +} + +// Store atomically stores the passed value. +func (p *Pointer[T]) Store(val *T) { + p.p.Store(unsafe.Pointer(val)) +} + +// Swap atomically swaps the wrapped pointer and returns the old value. +func (p *Pointer[T]) Swap(val *T) (old *T) { + return (*T)(p.p.Swap(unsafe.Pointer(val))) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (p *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) { + return p.p.CompareAndSwap(unsafe.Pointer(old), unsafe.Pointer(new)) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go119.go b/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go119.go new file mode 100644 index 000000000..5de00bea1 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/pointer_go119.go @@ -0,0 +1,40 @@ +// Copyright (c) 2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.19 +// +build go1.19 + +package atomic + +import "sync/atomic" + +// Pointer is an atomic pointer of type *T. +type Pointer[T any] struct { + atomic.Pointer[T] +} + +// NewPointer creates a new Pointer. +func NewPointer[T any](v *T) *Pointer[T] { + var p Pointer[T] + if v != nil { + p.Store(v) + } + return &p +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/string.go b/vendor/github.com/msaf1980/go-syncutils/atomic/string.go new file mode 100644 index 000000000..02b2aaa8f --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/string.go @@ -0,0 +1,72 @@ +// @generated Code generated by gen-atomicwrapper. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +// String is an atomic type-safe wrapper for string values. +type String struct { + _ noCopy + + v Value +} + +var _zeroString string + +// NewString creates a new String. +func NewString(val string) *String { + x := &String{} + if val != _zeroString { + x.Store(val) + } + return x +} + +// Load atomically loads the wrapped string. +func (x *String) Load() string { + return unpackString(x.v.Load()) +} + +// Store atomically stores the passed string. +func (x *String) Store(val string) { + x.v.Store(packString(val)) +} + +// CompareAndSwap is an atomic compare-and-swap for string values. +func (x *String) CompareAndSwap(old, new string) (swapped bool) { + if x.v.CompareAndSwap(packString(old), packString(new)) { + return true + } + + if old == _zeroString { + // If the old value is the empty value, then it's possible the + // underlying Value hasn't been set and is nil, so retry with nil. + return x.v.CompareAndSwap(nil, packString(new)) + } + + return false +} + +// Swap atomically stores the given string and returns the old +// value. +func (x *String) Swap(val string) (old string) { + return unpackString(x.v.Swap(packString(val))) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/string_ext.go b/vendor/github.com/msaf1980/go-syncutils/atomic/string_ext.go new file mode 100644 index 000000000..019109c86 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/string_ext.go @@ -0,0 +1,54 @@ +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped Value -pack packString -unpack unpackString -compareandswap -swap -file=string.go + +func packString(s string) interface{} { + return s +} + +func unpackString(v interface{}) string { + if s, ok := v.(string); ok { + return s + } + return "" +} + +// String returns the wrapped value. +func (s *String) String() string { + return s.Load() +} + +// MarshalText encodes the wrapped string into a textual form. +// +// This makes it encodable as JSON, YAML, XML, and more. +func (s *String) MarshalText() ([]byte, error) { + return []byte(s.Load()), nil +} + +// UnmarshalText decodes text and replaces the wrapped string with it. +// +// This makes it decodable from JSON, YAML, XML, and more. +func (s *String) UnmarshalText(b []byte) error { + s.Store(string(b)) + return nil +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/time.go b/vendor/github.com/msaf1980/go-syncutils/atomic/time.go new file mode 100644 index 000000000..b65364d93 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/time.go @@ -0,0 +1,68 @@ +// @generated Code generated by gen-atomicwrapper. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "time" +) + +// Time is an atomic type-safe wrapper for time.Time values. +type Time struct { + _ noCopy + + v Value +} + +var _zeroTime time.Time + +// NewTime creates a new Time. +func NewTime(val time.Time) *Time { + x := &Time{} + if val != _zeroTime { + x.Store(val) + } + return x +} + +// Load atomically loads the wrapped time.Time. +func (x *Time) Load() time.Time { + return unpackTime(x.v.Load()) +} + +// Store atomically stores the passed time.Time. +func (x *Time) Store(val time.Time) { + x.v.Store(packTime(val)) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (x *Time) CompareAndSwap(old, new time.Time) (swapped bool) { + if old == _zeroTime { + return x.v.CompareAndSwap(nil, new) + } + return x.v.CompareAndSwap(old, new) +} + +// Swap atomically swaps the wrapped time.Time and returns the old value. +func (x *Time) Swap(val time.Time) (old time.Time) { + return unpackTime(x.v.Swap(val)) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/time_ext.go b/vendor/github.com/msaf1980/go-syncutils/atomic/time_ext.go new file mode 100644 index 000000000..1e3dc978a --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/time_ext.go @@ -0,0 +1,36 @@ +// Copyright (c) 2021 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import "time" + +//go:generate bin/gen-atomicwrapper -name=Time -type=time.Time -wrapped=Value -pack=packTime -unpack=unpackTime -imports time -file=time.go + +func packTime(t time.Time) interface{} { + return t +} + +func unpackTime(v interface{}) time.Time { + if t, ok := v.(time.Time); ok { + return t + } + return time.Time{} +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/uint32.go b/vendor/github.com/msaf1980/go-syncutils/atomic/uint32.go new file mode 100644 index 000000000..12f9363ef --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/uint32.go @@ -0,0 +1,86 @@ +// @generated Code generated by gen-atomicint. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "strconv" + "sync/atomic" +) + +// Uint32 is an atomic wrapper around uint32. +type Uint32 struct { + _ noCopy + + v uint32 +} + +// NewUint32 creates a new Uint32. +func NewUint32(val uint32) *Uint32 { + return &Uint32{v: val} +} + +// Load atomically loads the wrapped value. +func (i *Uint32) Load() uint32 { + return atomic.LoadUint32(&i.v) +} + +// Add atomically adds to the wrapped uint32 and returns the new value. +func (i *Uint32) Add(delta uint32) uint32 { + return atomic.AddUint32(&i.v, delta) +} + +// Sub atomically subtracts from the wrapped uint32 and returns the new value. +func (i *Uint32) Sub(delta uint32) uint32 { + return atomic.AddUint32(&i.v, ^(delta - 1)) +} + +// Inc atomically increments the wrapped uint32 and returns the new value. +func (i *Uint32) Inc() uint32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped uint32 and returns the new value. +func (i *Uint32) Dec() uint32 { + return i.Sub(1) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Uint32) CompareAndSwap(old, new uint32) (swapped bool) { + return atomic.CompareAndSwapUint32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint32) Store(val uint32) { + atomic.StoreUint32(&i.v, val) +} + +// Swap atomically swaps the wrapped uint32 and returns the old value. +func (i *Uint32) Swap(val uint32) (old uint32) { + return atomic.SwapUint32(&i.v, val) +} + +// String encodes the wrapped value as a string. +func (i *Uint32) String() string { + v := i.Load() + return strconv.FormatUint(uint64(v), 10) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/uint64.go b/vendor/github.com/msaf1980/go-syncutils/atomic/uint64.go new file mode 100644 index 000000000..83d4a6061 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/uint64.go @@ -0,0 +1,86 @@ +// @generated Code generated by gen-atomicint. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "strconv" + "sync/atomic" +) + +// Uint64 is an atomic wrapper around uint64. +type Uint64 struct { + _ noCopy + + v uint64 +} + +// NewUint64 creates a new Uint64. +func NewUint64(val uint64) *Uint64 { + return &Uint64{v: val} +} + +// Load atomically loads the wrapped value. +func (i *Uint64) Load() uint64 { + return atomic.LoadUint64(&i.v) +} + +// Add atomically adds to the wrapped uint64 and returns the new value. +func (i *Uint64) Add(delta uint64) uint64 { + return atomic.AddUint64(&i.v, delta) +} + +// Sub atomically subtracts from the wrapped uint64 and returns the new value. +func (i *Uint64) Sub(delta uint64) uint64 { + return atomic.AddUint64(&i.v, ^(delta - 1)) +} + +// Inc atomically increments the wrapped uint64 and returns the new value. +func (i *Uint64) Inc() uint64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped uint64 and returns the new value. +func (i *Uint64) Dec() uint64 { + return i.Sub(1) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { + return atomic.CompareAndSwapUint64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint64) Store(val uint64) { + atomic.StoreUint64(&i.v, val) +} + +// Swap atomically swaps the wrapped uint64 and returns the old value. +func (i *Uint64) Swap(val uint64) (old uint64) { + return atomic.SwapUint64(&i.v, val) +} + +// String encodes the wrapped value as a string. +func (i *Uint64) String() string { + v := i.Load() + return strconv.FormatUint(uint64(v), 10) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/uintptr.go b/vendor/github.com/msaf1980/go-syncutils/atomic/uintptr.go new file mode 100644 index 000000000..3a35b21b0 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/uintptr.go @@ -0,0 +1,86 @@ +// @generated Code generated by gen-atomicint. + +// Copyright (c) 2020-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "strconv" + "sync/atomic" +) + +// Uintptr is an atomic wrapper around uintptr. +type Uintptr struct { + _ noCopy + + v uintptr +} + +// NewUintptr creates a new Uintptr. +func NewUintptr(val uintptr) *Uintptr { + return &Uintptr{v: val} +} + +// Load atomically loads the wrapped value. +func (i *Uintptr) Load() uintptr { + return atomic.LoadUintptr(&i.v) +} + +// Add atomically adds to the wrapped uintptr and returns the new value. +func (i *Uintptr) Add(delta uintptr) uintptr { + return atomic.AddUintptr(&i.v, delta) +} + +// Sub atomically subtracts from the wrapped uintptr and returns the new value. +func (i *Uintptr) Sub(delta uintptr) uintptr { + return atomic.AddUintptr(&i.v, ^(delta - 1)) +} + +// Inc atomically increments the wrapped uintptr and returns the new value. +func (i *Uintptr) Inc() uintptr { + return i.Add(1) +} + +// Dec atomically decrements the wrapped uintptr and returns the new value. +func (i *Uintptr) Dec() uintptr { + return i.Sub(1) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (i *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) { + return atomic.CompareAndSwapUintptr(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uintptr) Store(val uintptr) { + atomic.StoreUintptr(&i.v, val) +} + +// Swap atomically swaps the wrapped uintptr and returns the old value. +func (i *Uintptr) Swap(val uintptr) (old uintptr) { + return atomic.SwapUintptr(&i.v, val) +} + +// String encodes the wrapped value as a string. +func (i *Uintptr) String() string { + v := i.Load() + return strconv.FormatUint(uint64(v), 10) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/unsafe_pointer.go b/vendor/github.com/msaf1980/go-syncutils/atomic/unsafe_pointer.go new file mode 100644 index 000000000..06b19cae9 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/unsafe_pointer.go @@ -0,0 +1,58 @@ +// Copyright (c) 2021-2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "sync/atomic" + "unsafe" +) + +// UnsafePointer is an atomic wrapper around unsafe.Pointer. +type UnsafePointer struct { + _ noCopy + + v unsafe.Pointer +} + +// NewUnsafePointer creates a new UnsafePointer. +func NewUnsafePointer(val unsafe.Pointer) *UnsafePointer { + return &UnsafePointer{v: val} +} + +// Load atomically loads the wrapped value. +func (p *UnsafePointer) Load() unsafe.Pointer { + return atomic.LoadPointer(&p.v) +} + +// Store atomically stores the passed value. +func (p *UnsafePointer) Store(val unsafe.Pointer) { + atomic.StorePointer(&p.v, val) +} + +// Swap atomically swaps the wrapped unsafe.Pointer and returns the old value. +func (p *UnsafePointer) Swap(val unsafe.Pointer) (old unsafe.Pointer) { + return atomic.SwapPointer(&p.v, val) +} + +// CompareAndSwap is an atomic compare-and-swap. +func (p *UnsafePointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) { + return atomic.CompareAndSwapPointer(&p.v, old, new) +} diff --git a/vendor/github.com/msaf1980/go-syncutils/atomic/value.go b/vendor/github.com/msaf1980/go-syncutils/atomic/value.go new file mode 100644 index 000000000..88fb4d778 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/atomic/value.go @@ -0,0 +1,29 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import "sync/atomic" + +// Value shadows the type of the same name from sync/atomic +// https://godoc.org/sync/atomic#Value +type Value struct { + atomic.Value +} diff --git a/vendor/github.com/msaf1980/go-syncutils/lock/chanmutex.go b/vendor/github.com/msaf1980/go-syncutils/lock/chanmutex.go new file mode 100644 index 000000000..70758052f --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/lock/chanmutex.go @@ -0,0 +1,67 @@ +package lock + +import ( + "context" + "time" +) + +// ChanMutex is the struct implementing Mutex by channel. +type ChanMutex struct { + lockChan chan struct{} +} + +// NewChanMutex returns ChanMutex. +func NewChanMutex() *ChanMutex { + return &ChanMutex{ + lockChan: make(chan struct{}, 1), + } +} + +// Lock acquires the lock. +// If it is currently held by others, Lock will wait until it has a chance to acquire it. +func (m *ChanMutex) Lock() { + m.lockChan <- struct{}{} +} + +// Unlock releases the lock. +func (m *ChanMutex) Unlock() { + <-m.lockChan +} + +// TryLock attempts to acquire the lock without blocking. +// Return false if someone is holding it now. +func (m *ChanMutex) TryLock() bool { + select { + case m.lockChan <- struct{}{}: + return true + default: + return false + } +} + +// LockWithContext attempts to acquire the lock, blocking until resources +// are available or ctx is done (timeout or cancellation). +func (m *ChanMutex) LockWithContext(ctx context.Context) bool { + select { + case m.lockChan <- struct{}{}: + return true + case <-ctx.Done(): + // timeout or cancellation + return false + } +} + +// LockWithTimeout attempts to acquire the lock within a period of time. +// Return false if spending time is more than duration and no chance to acquire it. +func (m *ChanMutex) LockWithTimeout(duration time.Duration) bool { + + t := time.After(duration) + + select { + case m.lockChan <- struct{}{}: + return true + case <-t: + // timeout + return false + } +} diff --git a/vendor/github.com/msaf1980/go-syncutils/lock/condchan.go b/vendor/github.com/msaf1980/go-syncutils/lock/condchan.go new file mode 100644 index 000000000..a2d972c2c --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/lock/condchan.go @@ -0,0 +1,165 @@ +package lock + +import ( + "context" + "sync" + "time" +) + +// CondChan implements a condition variable, a rendezvous point for goroutines waiting for or announcing the occurrence +// of an event. +// +// A Cond must not be copied after first use. +type CondChan struct { + _ noCopy + + ch chan struct{} + L sync.Mutex +} + +func (cc *CondChan) waitCh() <-chan struct{} { + + if cc.ch == nil { + cc.ch = make(chan struct{}) + } + ch := cc.ch + + return ch + +} + +// Wait atomically unlocks cc.Lockand suspends execution of the calling goroutine. +// It is required for the caller to hold cc.Lock during the call. +func (cc *CondChan) Wait() { + + ch := cc.waitCh() + + cc.L.Unlock() + + <-ch + + cc.L.Lock() +} + +// WaitU atomically unlocks cc.Lockand suspends execution of the calling goroutine. +// It is required for the caller to hold cc.Lock during the call. +// After execution, cc.Lock is unlocked +func (cc *CondChan) WaitU() { + + ch := cc.waitCh() + + cc.L.Unlock() + + <-ch + +} + +// WaitWithContext attempts to wait with context. +// It is required for the caller to hold cc.Lock during the call. +func (cc *CondChan) WaitWithContext(ctx context.Context) (ok bool) { + + ch := cc.waitCh() + + cc.L.Unlock() + + select { + case <-ch: + cc.L.Lock() + ok = true + case <-ctx.Done(): + // timeout or cancellation + } + + return + +} + +// WaitUWithContext attempts to wait with context. +// It is required for the caller to hold cc.Lock during the call. +// After execution, cc.Lock is unlocked +func (cc *CondChan) WaitUWithContext(ctx context.Context) (ok bool) { + + ch := cc.waitCh() + + cc.L.Unlock() + + select { + case <-ch: + ok = true + case <-ctx.Done(): + // timeout or cancellation + } + + return + +} + +// WaitWithTimeout attempts to wait with timeout. +// After later resuming execution, Wait locks cc.Lock before returning. +func (cc *CondChan) WaitWithTimeout(duration time.Duration) (ok bool) { + + t := time.After(duration) + + ch := cc.waitCh() + + cc.L.Unlock() + + select { + case <-ch: + cc.L.Lock() + ok = true + case <-t: + // timeout + } + + return + +} + +// WaitUWithTimeout attempts to wait with timeout. +// After later resuming execution, Wait locks cc.Lock before returning. +// After execution, cc.Lock is unlocked +func (cc *CondChan) WaitUWithTimeout(duration time.Duration) (ok bool) { + + t := time.After(duration) + + ch := cc.waitCh() + + cc.L.Unlock() + + select { + case <-ch: + ok = true + case <-t: + // timeout + } + + return + +} + +// Signal wakes one goroutine waiting on cc, if there is any. +// It is required for the caller to hold cc.Lock during the call. +func (cc *CondChan) Signal() { + + if cc.ch == nil { + return + } + select { + case cc.ch <- struct{}{}: + default: + } + +} + +// Broadcast wakes all goroutines waiting on cc. +// It is required for the caller to hold cc.Lock during the call. +func (cc *CondChan) Broadcast() { + + if cc.ch == nil { + return + } + close(cc.ch) + cc.ch = make(chan struct{}) + +} diff --git a/vendor/github.com/msaf1980/go-syncutils/lock/mutex.go b/vendor/github.com/msaf1980/go-syncutils/lock/mutex.go new file mode 100644 index 000000000..0e257f758 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/lock/mutex.go @@ -0,0 +1,165 @@ +package lock + +import ( + "context" + "sync" + "sync/atomic" + "time" +) + +const tmLocked int32 = 1 // lock + +// Mutex - Try Mutex +type Mutex struct { + state int32 + mx sync.Mutex + ch chan struct{} +} + +func (m *Mutex) chGet() chan struct{} { + + m.mx.Lock() + if m.ch == nil { + m.ch = make(chan struct{}, 1) + } + r := m.ch + m.mx.Unlock() + return r + +} + +func (m *Mutex) tryChGet() (chan struct{}, bool) { + + if !m.mx.TryLock() { + return nil, false + } + if m.ch == nil { + m.ch = make(chan struct{}, 1) + } + r := m.ch + m.mx.Unlock() + return r, true + +} + +func (m *Mutex) chClose() { + // it's need only when exists parallel + // to make faster need add counter to add drop listners of chan + + var o chan struct{} + m.mx.Lock() + if m.ch != nil { + o = m.ch + m.ch = nil + } + m.mx.Unlock() + + if o != nil { + close(o) + } + +} + +// Lock - locks mutex +func (m *Mutex) Lock() { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return + } + + // Slow way + m.lockS() +} + +// TryLock - try locks mutex +func (m *Mutex) TryLock() bool { + return atomic.CompareAndSwapInt32(&m.state, 0, -1) +} + +// Unlock - unlocks mutex +func (m *Mutex) Unlock() { + if atomic.CompareAndSwapInt32(&m.state, -1, 0) { + m.chClose() + return + } + + panic("Mutex: Unlock fail") +} + +// LockWithContext - try locks mutex with context +func (m *Mutex) LockWithContext(ctx context.Context) bool { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + return true + } + + // Slow way + return m.lockST(ctx) +} + +// LockD - try locks mutex with time duration +func (m *Mutex) LockWithTimeout(d time.Duration) bool { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + return true + } + + // Slow way + return m.lockSD(d) +} + +func (m *Mutex) lockS() { + ch := m.chGet() + for { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return + } + + select { + case <-ch: + ch = m.chGet() + } + } + +} + +func (m *Mutex) lockST(ctx context.Context) bool { + ch := m.chGet() + for { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return true + } + + if ctx == nil { + return false + } + + select { + case <-ch: + ch = m.chGet() + case <-ctx.Done(): + return false + } + + } +} + +func (m *Mutex) lockSD(d time.Duration) bool { + // may be use context.WithTimeout(context.Background(), d) however NO it's not fun + t := time.After(d) + ch := m.chGet() + for { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return true + } + + select { + case <-ch: + ch = m.chGet() + case <-t: + return false + } + + } +} diff --git a/vendor/github.com/msaf1980/go-syncutils/lock/nocopy.go b/vendor/github.com/msaf1980/go-syncutils/lock/nocopy.go new file mode 100644 index 000000000..88f6fc3f2 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/lock/nocopy.go @@ -0,0 +1,14 @@ +package lock + +// noCopy may be added to structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} diff --git a/vendor/github.com/msaf1980/go-syncutils/lock/pmutex.go b/vendor/github.com/msaf1980/go-syncutils/lock/pmutex.go new file mode 100644 index 000000000..5e424b12f --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/lock/pmutex.go @@ -0,0 +1,491 @@ +package lock + +import ( + "context" + "fmt" + "sync" + "time" +) + +// PMutex - Read Write Try Mutex with change priority (Promote and Reduce) +// F methods (like LockF and TryLockF) Locks mutex if mutex already locked then this methods will be first in lock queue +// Promote - lock mutex from RLock to Lock +// Reduce - lock mutex from Lock to RLock +type PMutex struct { + state int32 + mx sync.Mutex + ch chan struct{} +} + +func (m *PMutex) chGet() chan struct{} { + + m.mx.Lock() + if m.ch == nil { + m.ch = make(chan struct{}, 1) + } + r := m.ch + m.mx.Unlock() + return r + +} + +// chClose - unlocks other routines needs mx.Lock +func (m *PMutex) chClose() { + // it's need only when exists parallel + // to make faster need add counter to add drop listners of chan + + var o chan struct{} + + if m.ch != nil { + o = m.ch + m.ch = nil + } + if o != nil { + close(o) + } + +} + +// Lock - locks mutex +func (m *PMutex) Lock() { + + m.mx.Lock() + + if m.state == 0 { + m.state = -1 + m.mx.Unlock() + return + } + m.mx.Unlock() + // Slow way + m.lockS() +} + +// TryLock - try locks mutex +func (m *PMutex) TryLock() (ok bool) { + + m.mx.Lock() + + if m.state == 0 { + m.state = -1 + ok = true + } + + m.mx.Unlock() + + return +} + +// Unlock - unlocks mutex +func (m *PMutex) Unlock() { + + m.mx.Lock() + + if m.state == -1 { + m.state = 0 + m.chClose() + } else { + panic(fmt.Sprintf("PMutex: Unlock fail (%v)", m.state)) + } + m.mx.Unlock() +} + +// Reduce - lock mutex from Lock to RLock +func (m *PMutex) Reduce() { + + m.mx.Lock() + + if m.state == -1 { + m.state = 1 + m.chClose() + } else { + panic(fmt.Sprintf("PMutex: Reduce fail (%v)", m.state)) + } + m.mx.Unlock() +} + +// LockWithContext - try locks mutex with context +func (m *PMutex) LockWithContext(ctx context.Context) bool { + + m.mx.Lock() + + if m.state == 0 { + m.state = -1 + m.mx.Unlock() + return true + } + m.mx.Unlock() + + // Slow way + return m.lockST(ctx) +} + +// LockWithTimeout - try locks mutex with time duration +func (m *PMutex) LockWithTimeout(d time.Duration) bool { + m.mx.Lock() + + if m.state == 0 { + m.state = -1 + m.mx.Unlock() + return true + } + m.mx.Unlock() + + // Slow way + return m.lockSD(d) +} + +// RLock - read locks mutex +func (m *PMutex) RLock() { + m.mx.Lock() + + if m.state >= 0 { + m.state++ + m.mx.Unlock() + return + } + m.mx.Unlock() + + // Slow way + m.rlockS() +} + +// TryRLock - read locks mutex +func (m *PMutex) TryRLock() (ok bool) { + m.mx.Lock() + + if m.state >= 0 { + m.state++ + ok = true + } + m.mx.Unlock() + + return +} + +// RUnlock - unlocks mutex +func (m *PMutex) RUnlock() { + + m.mx.Lock() + + if m.state > 0 { + m.state-- + if m.state <= 1 { + m.chClose() + } + } else { + panic(fmt.Sprintf("PMutex: RUnlock fail (%v)", m.state)) + } + + m.mx.Unlock() +} + +// RLockWithContext - try read locks mutex with context +func (m *PMutex) RLockWithContext(ctx context.Context) bool { + m.mx.Lock() + + if m.state >= 0 { + m.state++ + m.mx.Unlock() + return true + } + m.mx.Unlock() + + // Slow way + return m.rlockST(ctx) +} + +// RLockWithTimeout - try read locks mutex with time duration +func (m *PMutex) RLockWithTimeout(d time.Duration) bool { + m.mx.Lock() + + if m.state >= 0 { + m.state++ + m.mx.Unlock() + return true + } + m.mx.Unlock() + + // Slow way + return m.rlockSD(d) +} + +func (m *PMutex) lockS() { + + ch := m.chGet() + for { + + m.mx.Lock() + if m.state == 0 { + m.state = -1 + m.mx.Unlock() + return + } + m.mx.Unlock() + + select { + case <-ch: + ch = m.chGet() + } + } +} + +func (m *PMutex) lockST(ctx context.Context) bool { + + ch := m.chGet() + for { + + m.mx.Lock() + if m.state == 0 { + m.state = -1 + m.mx.Unlock() + return true + } + m.mx.Unlock() + + if ctx == nil { + return false + } + + select { + case <-ch: + ch = m.chGet() + case <-ctx.Done(): + return false + } + } +} + +func (m *PMutex) lockSD(d time.Duration) bool { + // may be use context.WithTimeout(context.Background(), d) however NO it's not fun + t := time.After(d) + + ch := m.chGet() + for { + + m.mx.Lock() + if m.state == 0 { + m.state = -1 + m.mx.Unlock() + return true + } + m.mx.Unlock() + + select { + case <-ch: + ch = m.chGet() + case <-t: + return false + } + + } +} + +func (m *PMutex) rlockS() { + + ch := m.chGet() + for { + + m.mx.Lock() + if m.state >= 0 { + m.state++ + m.mx.Unlock() + return + } + m.mx.Unlock() + + select { + case <-ch: + ch = m.chGet() + } + + } +} + +func (m *PMutex) rlockST(ctx context.Context) bool { + + ch := m.chGet() + for { + + m.mx.Lock() + if m.state >= 0 { + m.state++ + m.mx.Unlock() + return true + } + m.mx.Unlock() + + if ctx == nil { + return false + } + + select { + case <-ch: + ch = m.chGet() + case <-ctx.Done(): + return false + } + + } +} + +func (m *PMutex) rlockSD(d time.Duration) bool { + + t := time.After(d) + + ch := m.chGet() + for { + m.mx.Lock() + if m.state >= 0 { + m.state++ + m.mx.Unlock() + return true + } + m.mx.Unlock() + + select { + case <-ch: + ch = m.chGet() + case <-t: + return false + } + + } +} + +// Promote - lock mutex from RLock to Lock +// !!! use carefully - can produce deadlock, if promote from two grouroutines +func (m *PMutex) Promote() { + m.mx.Lock() + + if m.state == 1 { + m.state = -1 + m.mx.Unlock() + return + } + m.mx.Unlock() + + // Slow way + m.promoteS() +} + +// TryPromote - lock mutex from RLock to Lock +func (m *PMutex) TryPromote() (ok bool) { + m.mx.Lock() + + if m.state == 1 { + m.state = -1 + ok = true + } + m.mx.Unlock() + + return +} + +// PromoteWithContext - try locks mutex from RLock to Lock with context +// !!! If returns false then mutex is UNLOCKED if true mutex is locked as Lock +func (m *PMutex) PromoteWithContext(ctx context.Context) bool { + m.mx.Lock() + + if m.state == 1 { + m.state = -1 + m.mx.Unlock() + return true + } + m.mx.Unlock() + + // Slow way + return m.promoteST(ctx) +} + +// PromoteWithTimeout - try locks mutex from RLock to Lock with time duration +// !!! If returns false then mutex is UNLOCKED if true mutex is locked as Lock +func (m *PMutex) PromoteWithTimeout(d time.Duration) bool { + m.mx.Lock() + + if m.state == 1 { + m.state = -1 + m.mx.Unlock() + return true + } + m.mx.Unlock() + + // Slow way + return m.promoteSD(d) +} + +func (m *PMutex) promoteS() { + + ch := m.chGet() + for { + m.mx.Lock() + if m.state == 1 { + m.state = -1 + m.mx.Unlock() + return + } + m.mx.Unlock() + + select { + case <-ch: + ch = m.chGet() + } + } + +} + +func (m *PMutex) promoteST(ctx context.Context) bool { + + ch := m.chGet() + for { + + m.mx.Lock() + if m.state == 1 { + m.state = -1 + m.mx.Unlock() + return true + } + m.mx.Unlock() + + if ctx == nil { + return false + } + + select { + case <-ch: + ch = m.chGet() + case <-ctx.Done(): + m.RUnlock() + return false + } + + } + +} + +func (m *PMutex) promoteSD(d time.Duration) bool { + + t := time.After(d) + + ch := m.chGet() + for { + + m.mx.Lock() + if m.state == 1 { + m.state = -1 + m.mx.Unlock() + return true + + } + m.mx.Unlock() + + select { + case <-ch: + ch = m.chGet() + case <-t: + m.RUnlock() + return false + } + + } +} diff --git a/vendor/github.com/msaf1980/go-syncutils/lock/rwmutex.go b/vendor/github.com/msaf1980/go-syncutils/lock/rwmutex.go new file mode 100644 index 000000000..7f0c89ed1 --- /dev/null +++ b/vendor/github.com/msaf1980/go-syncutils/lock/rwmutex.go @@ -0,0 +1,293 @@ +package lock + +import ( + "context" + "sync" + "sync/atomic" + "time" +) + +// RWMutex - Read Write and Try Mutex with change priority (Promote and Reduce) +type RWMutex struct { + state int32 + mx sync.Mutex + ch chan struct{} +} + +func (m *RWMutex) chGet() chan struct{} { + m.mx.Lock() + if m.ch == nil { + m.ch = make(chan struct{}, 1) + } + r := m.ch + m.mx.Unlock() + return r +} + +func (m *RWMutex) tryChGet() (chan struct{}, bool) { + + if !m.mx.TryLock() { + return nil, false + } + if m.ch == nil { + m.ch = make(chan struct{}, 1) + } + r := m.ch + m.mx.Unlock() + + return r, true + +} + +func (m *RWMutex) chClose() { + // it's need only when exists parallel + // to make faster need add counter to add drop listners of chan + + var o chan struct{} + m.mx.Lock() + if m.ch != nil { + o = m.ch + m.ch = nil + } + m.mx.Unlock() + + if o != nil { + close(o) + } + +} + +// Lock - locks mutex +func (m *RWMutex) Lock() { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return + } + + // Slow way + m.lockS() +} + +// TryLock - try locks mutex +func (m *RWMutex) TryLock() bool { + return atomic.CompareAndSwapInt32(&m.state, 0, -1) +} + +// Unlock - unlocks mutex +func (m *RWMutex) Unlock() { + if atomic.CompareAndSwapInt32(&m.state, -1, 0) { + m.chClose() + return + } + + panic("RWMutex: Unlock fail") +} + +// LockWithContext - try locks mutex with context +func (m *RWMutex) LockWithContext(ctx context.Context) bool { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + return true + } + + // Slow way + return m.lockST(ctx) +} + +// LockD - try locks mutex with time duration +func (m *RWMutex) LockWithTimeout(d time.Duration) bool { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + return true + } + + // Slow way + return m.lockSD(d) +} + +// RLock - read locks mutex +func (m *RWMutex) RLock() { + k := atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + return + } + + // Slow way + m.rlockS() +} + +// TryRLock - try read locks mutex +func (m *RWMutex) TryRLock() bool { + k := atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + return true + } else if k == -1 { + return false + } + + // Slow way + if m.mx.TryLock() { + k := atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + m.mx.Unlock() + return true + } else if k == -1 { + m.mx.Unlock() + return false + } + } + + return false +} + +// RUnlock - unlocks mutex +func (m *RWMutex) RUnlock() { + i := atomic.AddInt32(&m.state, -1) + if i > 0 { + return + } else if i == 0 { + m.chClose() + return + } + + panic("RWMutex: RUnlock fail") +} + +// RLockWithContext - try read locks mutex with context +func (m *RWMutex) RLockWithContext(ctx context.Context) bool { + k := atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + return true + } + + // Slow way + return m.rlockST(ctx) +} + +// RLockWithDuration - try read locks mutex with time duration +func (m *RWMutex) RLockWithTimeout(d time.Duration) bool { + k := atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + return true + } + + // Slow way + return m.rlockSD(d) +} + +func (m *RWMutex) lockS() { + ch := m.chGet() + for { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return + } + + select { + case <-ch: + ch = m.chGet() + } + } + +} + +func (m *RWMutex) lockST(ctx context.Context) bool { + ch := m.chGet() + for { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return true + } + + if ctx == nil { + return false + } + + select { + case <-ch: + ch = m.chGet() + case <-ctx.Done(): + return false + } + + } +} + +func (m *RWMutex) lockSD(d time.Duration) bool { + // may be use context.WithTimeout(context.Background(), d) however NO it's not fun + t := time.After(d) + ch := m.chGet() + for { + if atomic.CompareAndSwapInt32(&m.state, 0, -1) { + + return true + } + + select { + case <-ch: + ch = m.chGet() + case <-t: + return false + } + + } +} + +func (m *RWMutex) rlockS() { + + ch := m.chGet() + var k int32 + for { + k = atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + return + } + + select { + case <-ch: + ch = m.chGet() + } + + } + +} + +func (m *RWMutex) rlockST(ctx context.Context) bool { + ch := m.chGet() + var k int32 + for { + k = atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + return true + } + + if ctx == nil { + return false + } + + select { + case <-ch: + ch = m.chGet() + case <-ctx.Done(): + return false + } + + } +} + +func (m *RWMutex) rlockSD(d time.Duration) bool { + ch := m.chGet() + t := time.After(d) + var k int32 + for { + k = atomic.LoadInt32(&m.state) + if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) { + return true + } + + select { + case <-ch: + ch = m.chGet() + case <-t: + return false + } + } +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index fa1245b18..2924cf3a1 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -8,7 +8,6 @@ import ( "fmt" "math" "os" - "path/filepath" "reflect" "regexp" "runtime" @@ -141,12 +140,11 @@ func CallerInfo() []string { } parts := strings.Split(file, "/") - file = parts[len(parts)-1] if len(parts) > 1 { + filename := parts[len(parts)-1] dir := parts[len(parts)-2] - if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { - path, _ := filepath.Abs(file) - callers = append(callers, fmt.Sprintf("%s:%d", path, line)) + if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) } } @@ -530,7 +528,7 @@ func isNil(object interface{}) bool { []reflect.Kind{ reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice}, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer}, kind) if isNilableKind && value.IsNil() { @@ -818,49 +816,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true // we consider nil to be equal to the nil set } - defer func() { - if e := recover(); e != nil { - ok = false - } - }() - listKind := reflect.TypeOf(list).Kind() - subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } + subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) if subsetKind == reflect.Map && listKind == reflect.Map { - listValue := reflect.ValueOf(list) - subsetKeys := subsetValue.MapKeys() + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) - for i := 0; i < len(subsetKeys); i++ { - subsetKey := subsetKeys[i] - subsetElement := subsetValue.MapIndex(subsetKey).Interface() - listElement := listValue.MapIndex(subsetKey).Interface() + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) - if !ObjectsAreEqual(subsetElement, listElement) { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...) + if !av.IsValid() { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) } } return true } - for i := 0; i < subsetValue.Len(); i++ { - element := subsetValue.Index(i).Interface() + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) } } @@ -879,34 +872,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } - defer func() { - if e := recover(); e != nil { - ok = false - } - }() - listKind := reflect.TypeOf(list).Kind() - subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } + subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) if subsetKind == reflect.Map && listKind == reflect.Map { - listValue := reflect.ValueOf(list) - subsetKeys := subsetValue.MapKeys() + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) - for i := 0; i < len(subsetKeys); i++ { - subsetKey := subsetKeys[i] - subsetElement := subsetValue.MapIndex(subsetKey).Interface() - listElement := listValue.MapIndex(subsetKey).Interface() + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) - if !ObjectsAreEqual(subsetElement, listElement) { + if !av.IsValid() { + return true + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { return true } } @@ -914,8 +901,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) } - for i := 0; i < subsetValue.Len(); i++ { - element := subsetValue.Index(i).Interface() + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index f0af8246c..e6ff8dfeb 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -218,16 +218,22 @@ func (c *Call) Unset() *Call { foundMatchingCall := false - for i, call := range c.Parent.ExpectedCalls { + // in-place filter slice for calls to be removed - iterate from 0'th to last skipping unnecessary ones + var index int // write index + for _, call := range c.Parent.ExpectedCalls { if call.Method == c.Method { _, diffCount := call.Arguments.Diff(c.Arguments) if diffCount == 0 { foundMatchingCall = true - // Remove from ExpectedCalls - c.Parent.ExpectedCalls = append(c.Parent.ExpectedCalls[:i], c.Parent.ExpectedCalls[i+1:]...) + // Remove from ExpectedCalls - just skip it + continue } } + c.Parent.ExpectedCalls[index] = call + index++ } + // trim slice up to last copied index + c.Parent.ExpectedCalls = c.Parent.ExpectedCalls[:index] if !foundMatchingCall { unlockOnce.Do(c.unlock) diff --git a/vendor/modules.txt b/vendor/modules.txt index 9c4d3f360..42d534a0a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -215,6 +215,10 @@ github.com/msaf1980/go-metrics/graphite # github.com/msaf1980/go-stringutils v0.1.4 ## explicit; go 1.16 github.com/msaf1980/go-stringutils +# github.com/msaf1980/go-syncutils v0.0.3 +## explicit; go 1.18 +github.com/msaf1980/go-syncutils/atomic +github.com/msaf1980/go-syncutils/lock # github.com/msaf1980/go-timeutils v0.0.3 ## explicit; go 1.19 github.com/msaf1980/go-timeutils/duration @@ -326,7 +330,7 @@ github.com/shurcooL/httpfs/union # github.com/stretchr/objx v0.5.0 ## explicit; go 1.12 github.com/stretchr/objx -# github.com/stretchr/testify v1.8.1 +# github.com/stretchr/testify v1.8.2 ## explicit; go 1.13 github.com/stretchr/testify/assert github.com/stretchr/testify/mock