abool/bool_test.go

290 lines
5.0 KiB
Go
Raw Normal View History

2016-05-25 06:42:19 +03:00
package abool
import (
2020-06-30 05:09:54 +03:00
"math"
2016-05-25 06:42:19 +03:00
"sync"
"sync/atomic"
"testing"
)
func TestBool(t *testing.T) {
2020-06-30 05:10:38 +03:00
t.Parallel()
2016-05-25 10:06:49 +03:00
v := NewBool(true)
if !v.IsSet() {
t.Fatal("NewValue(true) failed")
}
v = NewBool(false)
if v.IsSet() {
t.Fatal("NewValue(false) failed")
}
v = New()
2016-05-25 06:42:19 +03:00
if v.IsSet() {
t.Fatal("Empty value of AtomicBool should be false")
}
2020-07-16 09:46:58 +03:00
if v.IsSet() == v.IsNotSet() {
t.Fatal("AtomicBool.IsNotSet() should be the opposite of IsSet()")
}
2016-05-25 06:42:19 +03:00
v.Set()
if !v.IsSet() {
t.Fatal("AtomicBool.Set() failed")
}
v.UnSet()
if v.IsSet() {
t.Fatal("AtomicBool.UnSet() failed")
}
v.SetTo(true)
if !v.IsSet() {
t.Fatal("AtomicBool.SetTo(true) failed")
}
v.SetTo(false)
if v.IsSet() {
t.Fatal("AtomicBool.SetTo(false) failed")
}
2016-06-02 06:47:41 +03:00
if set := v.SetToIf(true, false); set || v.IsSet() {
t.Fatal("AtomicBool.SetTo(true, false) failed")
}
if set := v.SetToIf(false, true); !set || !v.IsSet() {
t.Fatal("AtomicBool.SetTo(false, true) failed")
}
2018-09-07 17:17:26 +03:00
v = New()
2018-09-07 17:17:26 +03:00
if v.IsSet() {
t.Fatal("Empty value of AtomicBool should be false")
}
_ = v.Toggle()
if !v.IsSet() {
t.Fatal("AtomicBool.Toggle() to true failed")
2018-09-07 17:17:26 +03:00
}
prev := v.Toggle()
if v.IsSet() == prev {
t.Fatal("AtomicBool.Toggle() to false failed")
}
2016-05-25 06:42:19 +03:00
}
2020-06-30 05:09:54 +03:00
func TestToogleMultipleTimes(t *testing.T) {
t.Parallel()
v := New()
pre := !v.IsSet()
2020-06-30 05:09:54 +03:00
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)
2020-06-30 05:09:54 +03:00
}
}
}
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())
}
}
2016-05-25 09:57:10 +03:00
func TestRace(t *testing.T) {
2020-06-30 05:10:38 +03:00
t.Parallel()
2016-05-25 09:57:10 +03:00
repeat := 10000
var wg sync.WaitGroup
2018-09-07 17:17:26 +03:00
wg.Add(repeat * 4)
2016-05-25 09:57:10 +03:00
v := New()
2016-05-25 10:06:49 +03:00
// Writer
2016-05-25 09:57:10 +03:00
go func() {
for i := 0; i < repeat; i++ {
v.Set()
wg.Done()
}
}()
2016-05-25 10:06:49 +03:00
// Reader
2016-05-25 09:57:10 +03:00
go func() {
for i := 0; i < repeat; i++ {
v.IsSet()
wg.Done()
}
}()
2016-05-25 10:06:49 +03:00
// Writer
2016-05-25 09:57:10 +03:00
go func() {
for i := 0; i < repeat; i++ {
v.UnSet()
wg.Done()
}
}()
2018-09-07 17:17:26 +03:00
// Reader And Writer
go func() {
for i := 0; i < repeat; i++ {
v.Toggle()
2018-09-07 17:17:26 +03:00
wg.Done()
}
}()
2016-05-25 09:57:10 +03:00
wg.Wait()
}
2016-05-25 09:16:55 +03:00
func ExampleAtomicBool() {
2020-07-16 09:46:58 +03:00
cond := New() // default to false
any := true
old := any
new := !any
cond.Set() // Sets to true
cond.IsSet() // Returns true
cond.UnSet() // Sets to false
cond.IsNotSet() // Returns true
cond.SetTo(any) // Sets to whatever you want
cond.SetToIf(new, old) // Sets to `new` only if the Boolean matches the `old`, returns whether succeeded
cond.Toggle() // Inverts the boolean then returns the value before inverting
2016-05-25 09:16:55 +03:00
}
2016-05-25 06:42:19 +03:00
// Benchmark Read
func BenchmarkMutexRead(b *testing.B) {
var m sync.RWMutex
var v bool
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.RLock()
_ = v
m.RUnlock()
}
}
func BenchmarkAtomicValueRead(b *testing.B) {
var v atomic.Value
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = v.Load() != nil
}
}
func BenchmarkAtomicBoolRead(b *testing.B) {
v := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = v.IsSet()
}
}
// Benchmark Write
func BenchmarkMutexWrite(b *testing.B) {
var m sync.RWMutex
var v bool
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.RLock()
v = true
m.RUnlock()
}
b.StopTimer()
_ = v
}
func BenchmarkAtomicValueWrite(b *testing.B) {
var v atomic.Value
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Store(true)
}
}
func BenchmarkAtomicBoolWrite(b *testing.B) {
v := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Set()
}
}
2016-06-02 06:47:41 +03:00
// Benchmark CAS
func BenchmarkMutexCAS(b *testing.B) {
var m sync.RWMutex
var v bool
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.Lock()
if !v {
v = true
}
m.Unlock()
}
b.StopTimer()
}
func BenchmarkAtomicBoolCAS(b *testing.B) {
v := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.SetToIf(false, true)
}
}
2018-09-07 17:17:26 +03:00
// Benchmark toggle boolean value
2018-09-07 17:17:26 +03:00
func BenchmarkMutexToggle(b *testing.B) {
2018-09-07 17:17:26 +03:00
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) {
2018-09-07 17:17:26 +03:00
v := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Toggle()
2018-09-07 17:17:26 +03:00
}
}