From 7afff28c37d544582bcf088ab443a62f590dfe6b Mon Sep 17 00:00:00 2001 From: Akshay Shah Date: Mon, 30 May 2016 11:10:24 -0700 Subject: [PATCH] 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 --- atomic.go | 39 +++++++++++++++++++++++++++++++++++++++ atomic_test.go | 15 +++++++++++++++ stress_test.go | 17 +++++++++++++++++ 3 files changed, 71 insertions(+) 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) Store(n uint64) { 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 @@ func TestUint64(t *testing.T) { 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 TestStressUint64(t *testing.T) { }() } } + +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() + } + }() + } +}