Add an atomic Boolean type (#4)
* Add an atomic Boolean type
Fixes #2.
* Feedback from CR
- Rename Swap to Toggle
- Add a more Swap-like Swap
* Add boolToInt helper
diff --git a/atomic.go b/atomic.go
index 2282e7e..4d8b34e 100644
--- a/atomic.go
+++ b/atomic.go
@@ -215,3 +215,42 @@
func (i *Uint64) Swap(n uint64) uint64 {
return atomic.SwapUint64(&i.uint64, n)
}
+
+// Bool is an atomic Boolean.
+type Bool struct{ uint32 }
+
+// NewBool creates a Bool.
+func NewBool(initial bool) *Bool {
+ return &Bool{boolToInt(initial)}
+}
+
+// Load atomically loads the Boolean.
+func (b *Bool) Load() bool {
+ return truthy(atomic.LoadUint32(&b.uint32))
+}
+
+// Store atomically stores the passed value.
+func (b *Bool) Store(new bool) {
+ atomic.StoreUint32(&b.uint32, boolToInt(new))
+}
+
+// Swap sets the given value and returns the previous value.
+func (b *Bool) Swap(new bool) bool {
+ return truthy(atomic.SwapUint32(&b.uint32, boolToInt(new)))
+}
+
+// Toggle atomically negates the Boolean and returns the previous value.
+func (b *Bool) Toggle() bool {
+ return truthy(atomic.AddUint32(&b.uint32, 1) - 1)
+}
+
+func truthy(n uint32) bool {
+ return n&1 == 1
+}
+
+func boolToInt(b bool) uint32 {
+ if b {
+ return 1
+ }
+ return 0
+}
diff --git a/atomic_test.go b/atomic_test.go
index 24d6576..7257033 100644
--- a/atomic_test.go
+++ b/atomic_test.go
@@ -101,3 +101,18 @@
atom.Store(42)
require.Equal(t, uint64(42), atom.Load(), "Store didn't set the correct value.")
}
+
+func TestBool(t *testing.T) {
+ atom := NewBool(false)
+ require.False(t, atom.Toggle(), "Expected swap to return previous value.")
+ require.True(t, atom.Load(), "Unexpected state after swap.")
+
+ atom.Store(false)
+ require.False(t, atom.Load(), "Unexpected state after store.")
+
+ prev := atom.Swap(false)
+ require.False(t, prev, "Expected Swap to return previous value.")
+
+ prev = atom.Swap(true)
+ require.False(t, prev, "Expected Swap to return previous value.")
+}
diff --git a/stress_test.go b/stress_test.go
index 5703067..83d3e85 100644
--- a/stress_test.go
+++ b/stress_test.go
@@ -103,3 +103,20 @@
}()
}
}
+
+func TestStressBool(t *testing.T) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism))
+ atom := NewBool(false)
+ for i := 0; i < _parallelism; i++ {
+ go func() {
+ for j := 0; j < _iterations; j++ {
+ atom.Load()
+ atom.Store(false)
+ atom.Swap(true)
+ atom.Load()
+ atom.Toggle()
+ atom.Toggle()
+ }
+ }()
+ }
+}