Merge pull request #3 from barryz/add_flip_method

Add Toggle method to invert the value
This commit is contained in:
Tevin 2020-07-07 15:51:04 +08:00 committed by GitHub
commit d91eb651d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 131 additions and 14 deletions

View File

@ -18,6 +18,7 @@ cond.IsSet() // Returns true
cond.UnSet() // Set to false cond.UnSet() // Set to false
cond.SetTo(true) // Set to whatever you want cond.SetTo(true) // Set to whatever you want
cond.SetToIf(false, true) // Set to true if it is false, returns false(not set) cond.SetToIf(false, true) // Set to true if it is false, returns false(not set)
cond.Toggle() *AtomicBool // Negates boolean atomically and returns a new AtomicBool object which holds previous boolean value.
// embedding // embedding
@ -26,24 +27,32 @@ type Foo struct {
} }
``` ```
## Benchmark: ## Benchmark
- Go 1.6.2 - Go 1.11.5
- OS X 10.11.4 - OS X 10.14.5
```shell ```shell
# Read # Read
BenchmarkMutexRead-4 100000000 21.0 ns/op BenchmarkMutexRead-4 100000000 14.7 ns/op
BenchmarkAtomicValueRead-4 200000000 6.30 ns/op BenchmarkAtomicValueRead-4 2000000000 0.45 ns/op
BenchmarkAtomicBoolRead-4 300000000 4.21 ns/op # <--- This package BenchmarkAtomicBoolRead-4 2000000000 0.35 ns/op # <--- This package
# Write # Write
BenchmarkMutexWrite-4 100000000 21.6 ns/op BenchmarkMutexWrite-4 100000000 14.5 ns/op
BenchmarkAtomicValueWrite-4 30000000 43.4 ns/op BenchmarkAtomicValueWrite-4 100000000 10.5 ns/op
BenchmarkAtomicBoolWrite-4 200000000 9.87 ns/op # <--- This package BenchmarkAtomicBoolWrite-4 300000000 5.21 ns/op # <--- This package
# CAS # CAS
BenchmarkMutexCAS-4 30000000 44.9 ns/op BenchmarkMutexCAS-4 50000000 31.3 ns/op
BenchmarkAtomicBoolCAS-4 100000000 11.7 ns/op # <--- This package BenchmarkAtomicBoolCAS-4 200000000 7.18 ns/op # <--- This package
# Toggle
BenchmarkMutexToggle-4 50000000 32.6 ns/op
BenchmarkAtomicBoolToggle-4 300000000 5.21 ns/op # <--- This package
``` ```
## Thanks to these Contributors
- [@barryz](https://github.com/barryz)
- Added the `Toggle` method

View File

@ -37,10 +37,10 @@ func (ab *AtomicBool) UnSet() {
// IsSet returns whether the Boolean is true // IsSet returns whether the Boolean is true
func (ab *AtomicBool) IsSet() bool { func (ab *AtomicBool) IsSet() bool {
return atomic.LoadInt32((*int32)(ab)) == 1 return atomic.LoadInt32((*int32)(ab))&1 == 1
} }
// SetTo sets the boolean with given Boolean // SetTo sets the boolean with given Boolean.
func (ab *AtomicBool) SetTo(yes bool) { func (ab *AtomicBool) SetTo(yes bool) {
if yes { if yes {
atomic.StoreInt32((*int32)(ab), 1) atomic.StoreInt32((*int32)(ab), 1)
@ -49,6 +49,11 @@ func (ab *AtomicBool) SetTo(yes bool) {
} }
} }
// Toggle inverts the boolean then returns the value before inverting.
func (ab *AtomicBool) Toggle() bool {
return atomic.AddInt32((*int32)(ab), 1)&1 == 0
}
// SetToIf sets the Boolean to new only if the Boolean matches the old // SetToIf sets the Boolean to new only if the Boolean matches the old
// Returns whether the set was done // Returns whether the set was done
func (ab *AtomicBool) SetToIf(old, new bool) (set bool) { func (ab *AtomicBool) SetToIf(old, new bool) (set bool) {

View File

@ -1,12 +1,15 @@
package abool package abool
import ( import (
"math"
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
) )
func TestBool(t *testing.T) { func TestBool(t *testing.T) {
t.Parallel()
v := NewBool(true) v := NewBool(true)
if !v.IsSet() { if !v.IsSet() {
t.Fatal("NewValue(true) failed") t.Fatal("NewValue(true) failed")
@ -49,12 +52,80 @@ func TestBool(t *testing.T) {
if set := v.SetToIf(false, true); !set || !v.IsSet() { if set := v.SetToIf(false, true); !set || !v.IsSet() {
t.Fatal("AtomicBool.SetTo(false, true) failed") t.Fatal("AtomicBool.SetTo(false, true) failed")
} }
v = New()
if v.IsSet() {
t.Fatal("Empty value of AtomicBool should be false")
}
_ = v.Toggle()
if !v.IsSet() {
t.Fatal("AtomicBool.Toggle() to true failed")
}
prev := v.Toggle()
if v.IsSet() == prev {
t.Fatal("AtomicBool.Toggle() to false failed")
}
}
func TestToogleMultipleTimes(t *testing.T) {
t.Parallel()
v := New()
pre := !v.IsSet()
for i := 0; i < 100; i++ {
v.SetTo(false)
for j := 0; j < i; j++ {
pre = v.Toggle()
}
expected := i%2 != 0
if v.IsSet() != expected {
t.Fatalf("AtomicBool.Toogle() doesn't work after %d calls, expected: %v, got %v", i, expected, v.IsSet())
}
if pre == v.IsSet() {
t.Fatalf("AtomicBool.Toogle() returned wrong value at the %dth calls, expected: %v, got %v", i, !v.IsSet(), pre)
}
}
}
func TestToogleAfterOverflow(t *testing.T) {
t.Parallel()
var value int32 = math.MaxInt32
v := (*AtomicBool)(&value)
valueBeforeToggle := *(*int32)(v)
// test first toggle after overflow
v.Toggle()
expected := math.MaxInt32%2 == 0
if v.IsSet() != expected {
t.Fatalf("AtomicBool.Toogle() doesn't work after overflow, expected: %v, got %v", expected, v.IsSet())
}
// make sure overflow happened
var valueAfterToggle int32 = *(*int32)(v)
if valueAfterToggle >= valueBeforeToggle {
t.Fatalf("Overflow does not happen as expected, before %d, after: %d", valueBeforeToggle, valueAfterToggle)
}
// test second toggle after overflow
v.Toggle()
expected = !expected
if v.IsSet() != expected {
t.Fatalf("AtomicBool.Toogle() doesn't work after the second call after overflow, expected: %v, got %v", expected, v.IsSet())
}
} }
func TestRace(t *testing.T) { func TestRace(t *testing.T) {
t.Parallel()
repeat := 10000 repeat := 10000
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(repeat * 3) wg.Add(repeat * 4)
v := New() v := New()
// Writer // Writer
@ -80,6 +151,15 @@ func TestRace(t *testing.T) {
wg.Done() wg.Done()
} }
}() }()
// Reader And Writer
go func() {
for i := 0; i < repeat; i++ {
v.Toggle()
wg.Done()
}
}()
wg.Wait() wg.Wait()
} }
@ -89,6 +169,7 @@ func ExampleAtomicBool() {
cond.IsSet() // returns true cond.IsSet() // returns true
cond.UnSet() // set to false cond.UnSet() // set to false
cond.SetTo(true) // set to whatever you want cond.SetTo(true) // set to whatever you want
cond.Toggle() // toggles the boolean value
} }
// Benchmark Read // Benchmark Read
@ -174,3 +255,25 @@ func BenchmarkAtomicBoolCAS(b *testing.B) {
v.SetToIf(false, true) v.SetToIf(false, true)
} }
} }
// Benchmark toggle boolean value
func BenchmarkMutexToggle(b *testing.B) {
var m sync.RWMutex
var v bool
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.Lock()
v = !v
m.Unlock()
}
b.StopTimer()
}
func BenchmarkAtomicBoolToggle(b *testing.B) {
v := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Toggle()
}
}