hide internal int32 data

This commit is contained in:
Johann Sebastian Schicho 2021-09-02 00:50:36 +02:00
parent 7e73116d51
commit 00beeaa184
2 changed files with 81 additions and 7 deletions

21
bool.go
View File

@ -24,21 +24,23 @@ func NewBool(ok bool) *AtomicBool {
// AtomicBool is an atomic Boolean. // AtomicBool is an atomic Boolean.
// Its methods are all atomic, thus safe to be called by multiple goroutines simultaneously. // Its methods are all atomic, thus safe to be called by multiple goroutines simultaneously.
// Note: When embedding into a struct one should always use *AtomicBool to avoid copy. // Note: When embedding into a struct one should always use *AtomicBool to avoid copy.
type AtomicBool int32 type AtomicBool struct {
boolean int32
}
// Set sets the Boolean to true. // Set sets the Boolean to true.
func (ab *AtomicBool) Set() { func (ab *AtomicBool) Set() {
atomic.StoreInt32((*int32)(ab), 1) atomic.StoreInt32((*int32)(&ab.boolean), 1)
} }
// UnSet sets the Boolean to false. // UnSet sets the Boolean to false.
func (ab *AtomicBool) UnSet() { func (ab *AtomicBool) UnSet() {
atomic.StoreInt32((*int32)(ab), 0) atomic.StoreInt32((*int32)(&ab.boolean), 0)
} }
// 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.boolean))&1 == 1
} }
// IsNotSet returns whether the Boolean is false. // IsNotSet returns whether the Boolean is false.
@ -49,12 +51,17 @@ func (ab *AtomicBool) IsNotSet() bool {
// 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.boolean), 1)
} else { } else {
atomic.StoreInt32((*int32)(ab), 0) atomic.StoreInt32((*int32)(&ab.boolean), 0)
} }
} }
// Toggle inverts the Boolean then returns the value before inverting.
func (ab *AtomicBool) Toggle() bool {
return atomic.AddInt32((*int32)(&ab.boolean), 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) {
@ -65,7 +72,7 @@ func (ab *AtomicBool) SetToIf(old, new bool) (set bool) {
if new { if new {
n = 1 n = 1
} }
return atomic.CompareAndSwapInt32((*int32)(ab), o, n) return atomic.CompareAndSwapInt32((*int32)(&ab.boolean), o, n)
} }
// MarshalJSON behaves the same as if the AtomicBool is a builtin.bool. // MarshalJSON behaves the same as if the AtomicBool is a builtin.bool.

View File

@ -2,6 +2,7 @@ package abool
import ( import (
"encoding/json" "encoding/json"
"math"
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
@ -72,6 +73,72 @@ func TestSetTo(t *testing.T) {
} }
} }
func TestToggle(t *testing.T) {
t.Parallel()
v := New()
_ = 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 := v.boolean
// 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 = v.boolean
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) {
repeat := 10000 repeat := 10000
var wg sync.WaitGroup var wg sync.WaitGroup