Add uintptr, unsafe.Pointer atomics (#90)
Add support for atomic `uintptr` and `unsafe.Pointer` types. For `unsafe.Pointer`, name the atomic variant `atomic.UnsafePointer` to maintain the "unsafe" portion of the name in usage. Resolves #88
This commit is contained in:
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Uintptr` type for atomic operations on `uintptr` values.
|
||||||
|
- Add `atomic.UnsafePointer` type for atomic operations on `unsafe.Pointer` values.
|
||||||
|
|
||||||
## [1.7.0] - 2020-09-14
|
## [1.7.0] - 2020-09-14
|
||||||
### Added
|
### Added
|
||||||
- Support JSON serialization and deserialization of primitive atomic types.
|
- Support JSON serialization and deserialization of primitive atomic types.
|
||||||
|
|||||||
1
gen.go
1
gen.go
@@ -24,3 +24,4 @@ package atomic
|
|||||||
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
||||||
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
||||||
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Uintptr -wrapped=uintptr -unsigned -file=uintptr.go
|
||||||
|
|||||||
102
uintptr.go
Normal file
102
uintptr.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020-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 (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Uintptr is an atomic wrapper around uintptr.
|
||||||
|
type Uintptr struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUintptr creates a new Uintptr.
|
||||||
|
func NewUintptr(i uintptr) *Uintptr {
|
||||||
|
return &Uintptr{v: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(n uintptr) uintptr {
|
||||||
|
return atomic.AddUintptr(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uintptr and returns the new value.
|
||||||
|
func (i *Uintptr) Sub(n uintptr) uintptr {
|
||||||
|
return atomic.AddUintptr(&i.v, ^(n - 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Uintptr) CAS(old, new uintptr) bool {
|
||||||
|
return atomic.CompareAndSwapUintptr(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uintptr) Store(n uintptr) {
|
||||||
|
atomic.StoreUintptr(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uintptr and returns the old value.
|
||||||
|
func (i *Uintptr) Swap(n uintptr) uintptr {
|
||||||
|
return atomic.SwapUintptr(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped uintptr into JSON.
|
||||||
|
func (i *Uintptr) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped uintptr.
|
||||||
|
func (i *Uintptr) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uintptr
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Uintptr) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
||||||
76
uintptr_test.go
Normal file
76
uintptr_test.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// 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 (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUintptr(t *testing.T) {
|
||||||
|
atom := NewUintptr(42)
|
||||||
|
|
||||||
|
require.Equal(t, uintptr(42), atom.Load(), "Load didn't work.")
|
||||||
|
require.Equal(t, uintptr(46), atom.Add(4), "Add didn't work.")
|
||||||
|
require.Equal(t, uintptr(44), atom.Sub(2), "Sub didn't work.")
|
||||||
|
require.Equal(t, uintptr(45), atom.Inc(), "Inc didn't work.")
|
||||||
|
require.Equal(t, uintptr(44), atom.Dec(), "Dec didn't work.")
|
||||||
|
|
||||||
|
require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, uintptr(0), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
|
||||||
|
require.Equal(t, uintptr(0), atom.Swap(1), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, uintptr(1), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
|
||||||
|
atom.Store(42)
|
||||||
|
require.Equal(t, uintptr(42), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
|
||||||
|
t.Run("JSON/Marshal", func(t *testing.T) {
|
||||||
|
bytes, err := json.Marshal(atom)
|
||||||
|
require.NoError(t, err, "json.Marshal errored unexpectedly.")
|
||||||
|
require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte("40"), &atom)
|
||||||
|
require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
|
||||||
|
require.Equal(t, uintptr(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
|
||||||
|
err := json.Unmarshal([]byte(`"40"`), &atom)
|
||||||
|
require.Error(t, err, "json.Unmarshal didn't error as expected.")
|
||||||
|
assertErrorJSONUnmarshalType(t, err,
|
||||||
|
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("String", func(t *testing.T) {
|
||||||
|
// Use an integer with the signed bit set. If we're converting
|
||||||
|
// incorrectly, we'll get a negative value here.
|
||||||
|
atom := NewUintptr(uintptr(math.MaxUint64))
|
||||||
|
assert.Equal(t, "18446744073709551615", atom.String(),
|
||||||
|
"String() returned an unexpected value.")
|
||||||
|
})
|
||||||
|
}
|
||||||
58
unsafe_pointer.go
Normal file
58
unsafe_pointer.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// 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 (
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnsafePointer is an atomic wrapper around unsafe.Pointer.
|
||||||
|
type UnsafePointer struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnsafePointer creates a new UnsafePointer.
|
||||||
|
func NewUnsafePointer(p unsafe.Pointer) *UnsafePointer {
|
||||||
|
return &UnsafePointer{v: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(q unsafe.Pointer) {
|
||||||
|
atomic.StorePointer(&p.v, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped unsafe.Pointer and returns the old value.
|
||||||
|
func (p *UnsafePointer) Swap(q unsafe.Pointer) unsafe.Pointer {
|
||||||
|
return atomic.SwapPointer(&p.v, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (p *UnsafePointer) CAS(old, new unsafe.Pointer) bool {
|
||||||
|
return atomic.CompareAndSwapPointer(&p.v, old, new)
|
||||||
|
}
|
||||||
83
unsafe_pointer_test.go
Normal file
83
unsafe_pointer_test.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// 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 (
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnsafePointer(t *testing.T) {
|
||||||
|
i := int64(42)
|
||||||
|
j := int64(0)
|
||||||
|
k := int64(1)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
newAtomic func() *UnsafePointer
|
||||||
|
initial unsafe.Pointer
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "non-empty",
|
||||||
|
newAtomic: func() *UnsafePointer {
|
||||||
|
return NewUnsafePointer(unsafe.Pointer(&i))
|
||||||
|
},
|
||||||
|
initial: unsafe.Pointer(&i),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nil",
|
||||||
|
newAtomic: func() *UnsafePointer {
|
||||||
|
var p UnsafePointer
|
||||||
|
return &p
|
||||||
|
},
|
||||||
|
initial: unsafe.Pointer(nil),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
t.Run("Load", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
require.Equal(t, tt.initial, atom.Load(), "Load should report nil.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Swap", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
require.Equal(t, tt.initial, atom.Swap(unsafe.Pointer(&k)), "Swap didn't return the old value.")
|
||||||
|
require.Equal(t, unsafe.Pointer(&k), atom.Load(), "Swap didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CAS", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
require.True(t, atom.CAS(tt.initial, unsafe.Pointer(&j)), "CAS didn't report a swap.")
|
||||||
|
require.Equal(t, unsafe.Pointer(&j), atom.Load(), "CAS didn't set the correct value.")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Store", func(t *testing.T) {
|
||||||
|
atom := tt.newAtomic()
|
||||||
|
atom.Store(unsafe.Pointer(&i))
|
||||||
|
require.Equal(t, unsafe.Pointer(&i), atom.Load(), "Store didn't set the correct value.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user