Skip to content

Commit

Permalink
Add CompareAndSwap, Deprecate CAS
Browse files Browse the repository at this point in the history
This aligns with sync/atomic from go1.19
  • Loading branch information
eNV25 committed Aug 5, 2022
1 parent 511277d commit bcb50e9
Show file tree
Hide file tree
Showing 17 changed files with 152 additions and 24 deletions.
9 changes: 8 additions & 1 deletion bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,15 @@ func (x *Bool) Store(val bool) {
}

// CAS is an atomic compare-and-swap for bool values.
//
// Deprecated: Use CompareAndSwap
func (x *Bool) CAS(old, new bool) (swapped bool) {
return x.v.CAS(boolToInt(old), boolToInt(new))
return x.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap for bool values.
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
return x.v.CompareAndSwap(boolToInt(old), boolToInt(new))
}

// Swap atomically stores the given bool and returns the old
Expand Down
9 changes: 8 additions & 1 deletion duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,15 @@ func (x *Duration) Store(val time.Duration) {
}

// CAS is an atomic compare-and-swap for time.Duration values.
//
// Deprecated: Use CompareAndSwap
func (x *Duration) CAS(old, new time.Duration) (swapped bool) {
return x.v.CAS(int64(old), int64(new))
return x.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap for time.Duration values.
func (x *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) {
return x.v.CompareAndSwap(int64(old), int64(new))
}

// Swap atomically stores the given time.Duration and returns the old
Expand Down
9 changes: 8 additions & 1 deletion error.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,15 @@ func (x *Error) Store(val error) {
}

// CAS is an atomic compare-and-swap for error values.
//
// Deprecated: Use CompareAndSwap
func (x *Error) CAS(old, new error) (swapped bool) {
return x.v.CAS(packError(old), packError(new))
return x.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap for error values.
func (x *Error) CompareAndSwap(old, new error) (swapped bool) {
return x.v.CompareAndSwap(packError(old), packError(new))
}

// Swap atomically stores the given error and returns the old
Expand Down
16 changes: 16 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ func TestErrorSwap(t *testing.T) {
require.Equal(t, err1, old, "Expected old to be initial value")
}

func TestErrorCompareAndSwap(t *testing.T) {
err1 := errors.New("hello1")
err2 := errors.New("hello2")

atom := NewError(err1)
require.Equal(t, err1, atom.Load(), "Expected Load to return initialized value")

swapped := atom.CompareAndSwap(err2, err2)
require.Equal(t, swapped, false, "Expected swapped to be false")
require.Equal(t, err1, atom.Load(), "Expected Load to return initial value")

swapped = atom.CompareAndSwap(err1, err2)
require.Equal(t, swapped, true, "Expected swapped to be true")
require.Equal(t, err2, atom.Load(), "Expected Load to return overridden value")
}

func TestErrorCAS(t *testing.T) {
err1 := errors.New("hello1")
err2 := errors.New("hello2")
Expand Down
23 changes: 15 additions & 8 deletions float32_ext.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020 Uber Technologies, Inc.
// 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
Expand Down Expand Up @@ -45,21 +45,28 @@ func (f *Float32) Sub(delta float32) float32 {

// CAS is an atomic compare-and-swap for float32 values.
//
// Note: CAS handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
// but CAS allows a stored NaN to compare equal to a passed in NaN.
// This avoids typical CAS loops from blocking forever, e.g.,
// Deprecated: Use CompareAndSwap
func (f *Float32) CAS(old, new float32) (swapped bool) {
return f.CompareAndSwap(old, new)
}

// 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.CAS(old, new) {
// if atom.CompareAndSwap(old, new) {
// break
// }
// }
//
// If CAS did not match NaN to match, then the above would loop forever.
func (f *Float32) CAS(old, new float32) (swapped bool) {
return f.v.CAS(math.Float32bits(old), math.Float32bits(new))
// If CompareAndSwap did not match NaN to match, then the above would loop forever.
func (f *Float32) CompareAndSwap(old, new float32) (swapped bool) {
return f.v.CompareAndSwap(math.Float32bits(old), math.Float32bits(new))
}

// String encodes the wrapped value as a string.
Expand Down
23 changes: 15 additions & 8 deletions float64_ext.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020 Uber Technologies, Inc.
// 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
Expand Down Expand Up @@ -45,21 +45,28 @@ func (f *Float64) Sub(delta float64) float64 {

// CAS is an atomic compare-and-swap for float64 values.
//
// Note: CAS handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
// but CAS allows a stored NaN to compare equal to a passed in NaN.
// This avoids typical CAS loops from blocking forever, e.g.,
// Deprecated: Use CompareAndSwap
func (f *Float64) CAS(old, new float64) (swapped bool) {
return f.CompareAndSwap(old, new)
}

// 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.CAS(old, new) {
// if atom.CompareAndSwap(old, new) {
// break
// }
// }
//
// If CAS did not match NaN to match, then the above would loop forever.
func (f *Float64) CAS(old, new float64) (swapped bool) {
return f.v.CAS(math.Float64bits(old), math.Float64bits(new))
// If CompareAndSwap did not match NaN to match, then the above would loop forever.
func (f *Float64) CompareAndSwap(old, new float64) (swapped bool) {
return f.v.CompareAndSwap(math.Float64bits(old), math.Float64bits(new))
}

// String encodes the wrapped value as a string.
Expand Down
7 changes: 7 additions & 0 deletions int32.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ func (i *Int32) Dec() int32 {
}

// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (i *Int32) CAS(old, new int32) (swapped bool) {
return i.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap.
func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) {
return atomic.CompareAndSwapInt32(&i.v, old, new)
}

Expand Down
7 changes: 7 additions & 0 deletions int64.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ func (i *Int64) Dec() int64 {
}

// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (i *Int64) CAS(old, new int64) (swapped bool) {
return i.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap.
func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) {
return atomic.CompareAndSwapInt64(&i.v, old, new)
}

Expand Down
9 changes: 8 additions & 1 deletion internal/gen-atomicint/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020 Uber Technologies, Inc.
// 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
Expand Down Expand Up @@ -180,7 +180,14 @@ func (i *{{ .Name }}) Dec() {{ .Wrapped }} {
}
// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (i *{{ .Name }}) CAS(old, new {{ .Wrapped }}) (swapped bool) {
return i.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap.
func (i *{{ .Name }}) CompareAndSwap(old, new {{ .Wrapped }}) (swapped bool) {
return atomic.CompareAndSwap{{ .Name }}(&i.v, old, new)
}
Expand Down
11 changes: 9 additions & 2 deletions internal/gen-atomicwrapper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,18 @@ func (x *{{ .Name }}) Store(val {{ .Type }}) {
{{ if .CAS -}}
// CAS is an atomic compare-and-swap for {{ .Type }} values.
//
// Deprecated: Use CompareAndSwap
func (x *{{ .Name }}) CAS(old, new {{ .Type }}) (swapped bool) {
return x.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap for {{ .Type }} values.
func (x *{{ .Name }}) CompareAndSwap(old, new {{ .Type }}) (swapped bool) {
{{ if .Pack -}}
return x.v.CAS({{ .Pack }}(old), {{ .Pack }}(new))
return x.v.CompareAndSwap({{ .Pack }}(old), {{ .Pack }}(new))
{{- else -}}{{- /* assume go.uber.org/atomic.Value */ -}}
return x.v.CAS(old, new)
return x.v.CompareAndSwap(old, new)
{{- end }}
}
{{- end }}
Expand Down
9 changes: 8 additions & 1 deletion string.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,15 @@ func (x *String) Store(val string) {
}

// CAS is an atomic compare-and-swap for string values.
//
// Deprecated: Use CompareAndSwap
func (x *String) CAS(old, new string) (swapped bool) {
return x.v.CAS(old, new)
return x.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap for string values.
func (x *String) CompareAndSwap(old, new string) (swapped bool) {
return x.v.CompareAndSwap(old, new)
}

// Swap atomically stores the given string and returns the old
Expand Down
12 changes: 12 additions & 0 deletions string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ func TestString(t *testing.T) {
"String() returned an unexpected value.")
})

t.Run("CompareAndSwap", func(t *testing.T) {
atom := NewString("foo")

swapped := atom.CompareAndSwap("bar", "bar")
require.Equal(t, swapped, false, "swapped isn't false")
require.Equal(t, atom.Load(), "foo", "Load returned wrong value")

swapped = atom.CompareAndSwap("foo", "bar")
require.Equal(t, swapped, true, "swapped isn't true")
require.Equal(t, atom.Load(), "bar", "Load returned wrong value")
})

t.Run("CAS", func(t *testing.T) {
atom := NewString("foo")

Expand Down
7 changes: 7 additions & 0 deletions uint32.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ func (i *Uint32) Dec() uint32 {
}

// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (i *Uint32) CAS(old, new uint32) (swapped bool) {
return i.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap.
func (i *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
return atomic.CompareAndSwapUint32(&i.v, old, new)
}

Expand Down
7 changes: 7 additions & 0 deletions uint64.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ func (i *Uint64) Dec() uint64 {
}

// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (i *Uint64) CAS(old, new uint64) (swapped bool) {
return i.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap.
func (i *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
return atomic.CompareAndSwapUint64(&i.v, old, new)
}

Expand Down
7 changes: 7 additions & 0 deletions uintptr.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ func (i *Uintptr) Dec() uintptr {
}

// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (i *Uintptr) CAS(old, new uintptr) (swapped bool) {
return i.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap.
func (i *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
return atomic.CompareAndSwapUintptr(&i.v, old, new)
}

Expand Down
9 changes: 8 additions & 1 deletion unsafe_pointer.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2021 Uber Technologies, Inc.
// 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
Expand Down Expand Up @@ -53,6 +53,13 @@ func (p *UnsafePointer) Swap(val unsafe.Pointer) (old unsafe.Pointer) {
}

// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (p *UnsafePointer) CAS(old, new unsafe.Pointer) (swapped bool) {
return p.CompareAndSwap(old, new)
}

// CompareAndSwap is an atomic compare-and-swap.
func (p *UnsafePointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) {
return atomic.CompareAndSwapPointer(&p.v, old, new)
}
2 changes: 2 additions & 0 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ type Value struct {
}

// CAS is an atomic compare-and-swap for Value
//
// Deprecated: Use CompareAndSwap
func (v *Value) CAS(old, new interface{}) (swapped bool) {
return v.CompareAndSwap(old, new)
}

0 comments on commit bcb50e9

Please sign in to comment.