Repository: uber-go/atomic (go)

Issue:
Document how to use atomic.Add for uint* or have a Sub
Uint64 and Uint32 only provide an Add which takes another uint. This is a little tricky to use if you want to subtract a value, see:
https://godoc.org/sync/atomic#AddUint32

We can either document this, or provide a `Sub` or `Subtract` helper.



Discussion:

Patch:
diff --git a/atomic.go b/atomic.go
--- a/atomic.go
+++ b/atomic.go
@@ -42,6 +42,11 @@ func (i *Int32) Add(n int32) int32 {
 	return atomic.AddInt32(&i.int32, n)
 }
 
+// Sub atomically subtracts from the wrapped int32 and returns the new value.
+func (i *Int32) Sub(n int32) int32 {
+	return atomic.AddInt32(&i.int32, -n)
+}
+
 // Inc atomically increments the wrapped int32 and returns the new value.
 func (i *Int32) Inc() int32 {
 	return i.Add(1)
@@ -49,7 +54,7 @@ func (i *Int32) Inc() int32 {
 
 // Dec atomically decrements the wrapped int32 and returns the new value.
 func (i *Int32) Dec() int32 {
-	return i.Add(-1)
+	return i.Sub(1)
 }
 
 // CAS is an atomic compare-and-swap.
@@ -85,6 +90,11 @@ func (i *Int64) Add(n int64) int64 {
 	return atomic.AddInt64(&i.int64, n)
 }
 
+// Sub atomically subtracts from the wrapped int64 and returns the new value.
+func (i *Int64) Sub(n int64) int64 {
+	return atomic.AddInt64(&i.int64, -n)
+}
+
 // Inc atomically increments the wrapped int64 and returns the new value.
 func (i *Int64) Inc() int64 {
 	return i.Add(1)
@@ -92,7 +102,7 @@ func (i *Int64) Inc() int64 {
 
 // Dec atomically decrements the wrapped int64 and returns the new value.
 func (i *Int64) Dec() int64 {
-	return i.Add(-1)
+	return i.Sub(1)
 }
 
 // CAS is an atomic compare-and-swap.
@@ -128,6 +138,11 @@ func (i *Uint32) Add(n uint32) uint32 {
 	return atomic.AddUint32(&i.uint32, n)
 }
 
+// Sub atomically subtracts from the wrapped uint32 and returns the new value.
+func (i *Uint32) Sub(n uint32) uint32 {
+	return atomic.AddUint32(&i.uint32, ^(n - 1))
+}
+
 // Inc atomically increments the wrapped uint32 and returns the new value.
 func (i *Uint32) Inc() uint32 {
 	return i.Add(1)
@@ -135,7 +150,7 @@ func (i *Uint32) Inc() uint32 {
 
 // Dec atomically decrements the wrapped int32 and returns the new value.
 func (i *Uint32) Dec() uint32 {
-	return i.Add(^uint32(0))
+	return i.Sub(1)
 }
 
 // CAS is an atomic compare-and-swap.
@@ -171,6 +186,11 @@ func (i *Uint64) Add(n uint64) uint64 {
 	return atomic.AddUint64(&i.uint64, n)
 }
 
+// Sub atomically subtracts from the wrapped uint64 and returns the new value.
+func (i *Uint64) Sub(n uint64) uint64 {
+	return atomic.AddUint64(&i.uint64, ^(n - 1))
+}
+
 // Inc atomically increments the wrapped uint64 and returns the new value.
 func (i *Uint64) Inc() uint64 {
 	return i.Add(1)
@@ -178,7 +198,7 @@ func (i *Uint64) Inc() uint64 {
 
 // Dec atomically decrements the wrapped uint64 and returns the new value.
 func (i *Uint64) Dec() uint64 {
-	return i.Add(^uint64(0))
+	return i.Sub(1)
 }
 
 // CAS is an atomic compare-and-swap.


Test patch:
diff --git a/atomic_test.go b/atomic_test.go
--- a/atomic_test.go
+++ b/atomic_test.go
@@ -30,7 +30,8 @@ func TestInt32(t *testing.T) {
 	atom := NewInt32(42)
 
 	require.Equal(t, int32(42), atom.Load(), "Load didn't work.")
-	require.Equal(t, int32(44), atom.Add(2), "Add didn't work.")
+	require.Equal(t, int32(46), atom.Add(4), "Add didn't work.")
+	require.Equal(t, int32(44), atom.Sub(2), "Sub didn't work.")
 	require.Equal(t, int32(45), atom.Inc(), "Inc didn't work.")
 	require.Equal(t, int32(44), atom.Dec(), "Dec didn't work.")
 
@@ -48,7 +49,8 @@ func TestInt64(t *testing.T) {
 	atom := NewInt64(42)
 
 	require.Equal(t, int64(42), atom.Load(), "Load didn't work.")
-	require.Equal(t, int64(44), atom.Add(2), "Add didn't work.")
+	require.Equal(t, int64(46), atom.Add(4), "Add didn't work.")
+	require.Equal(t, int64(44), atom.Sub(2), "Sub didn't work.")
 	require.Equal(t, int64(45), atom.Inc(), "Inc didn't work.")
 	require.Equal(t, int64(44), atom.Dec(), "Dec didn't work.")
 
@@ -66,7 +68,8 @@ func TestUint32(t *testing.T) {
 	atom := NewUint32(42)
 
 	require.Equal(t, uint32(42), atom.Load(), "Load didn't work.")
-	require.Equal(t, uint32(44), atom.Add(2), "Add didn't work.")
+	require.Equal(t, uint32(46), atom.Add(4), "Add didn't work.")
+	require.Equal(t, uint32(44), atom.Sub(2), "Sub didn't work.")
 	require.Equal(t, uint32(45), atom.Inc(), "Inc didn't work.")
 	require.Equal(t, uint32(44), atom.Dec(), "Dec didn't work.")
 
@@ -84,7 +87,8 @@ func TestUint64(t *testing.T) {
 	atom := NewUint64(42)
 
 	require.Equal(t, uint64(42), atom.Load(), "Load didn't work.")
-	require.Equal(t, uint64(44), atom.Add(2), "Add didn't work.")
+	require.Equal(t, uint64(46), atom.Add(4), "Add didn't work.")
+	require.Equal(t, uint64(44), atom.Sub(2), "Sub didn't work.")
 	require.Equal(t, uint64(45), atom.Inc(), "Inc didn't work.")
 	require.Equal(t, uint64(44), atom.Dec(), "Dec didn't work.")
 
diff --git a/stress_test.go b/stress_test.go
--- a/stress_test.go
+++ b/stress_test.go
@@ -36,6 +36,7 @@ func TestStressInt32(t *testing.T) {
 			for j := 0; j < _iterations; j++ {
 				atom.Load()
 				atom.Add(1)
+				atom.Sub(2)
 				atom.Inc()
 				atom.Dec()
 				atom.CAS(1, 0)
@@ -54,6 +55,7 @@ func TestStressInt64(t *testing.T) {
 			for j := 0; j < _iterations; j++ {
 				atom.Load()
 				atom.Add(1)
+				atom.Sub(2)
 				atom.Inc()
 				atom.Dec()
 				atom.CAS(1, 0)
@@ -72,6 +74,7 @@ func TestStressUint32(t *testing.T) {
 			for j := 0; j < _iterations; j++ {
 				atom.Load()
 				atom.Add(1)
+				atom.Sub(2)
 				atom.Inc()
 				atom.Dec()
 				atom.CAS(1, 0)
@@ -90,6 +93,7 @@ func TestStressUint64(t *testing.T) {
 			for j := 0; j < _iterations; j++ {
 				atom.Load()
 				atom.Add(1)
+				atom.Sub(2)
 				atom.Inc()
 				atom.Dec()
 				atom.CAS(1, 0)