New Upstream Release - golang-github-tevino-abool

Ready changes

Summary

Merged new upstream version: 0.0~git20220531.edeb703+ds (was: 0.0~git20170917.9b9efcf).

Resulting package

Built on 2022-11-10T06:48 (took 5m21s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-tevino-abool-dev

Lintian Result

Diff

diff --git a/README.md b/README.md
index 042d1ca..2f2c28e 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,26 @@
 # ABool :bulb:
+
 [![Go Report Card](https://goreportcard.com/badge/github.com/tevino/abool)](https://goreportcard.com/report/github.com/tevino/abool)
 [![GoDoc](https://godoc.org/github.com/tevino/abool?status.svg)](https://godoc.org/github.com/tevino/abool)
 
-Atomic Boolean library for Go, optimized for performance yet simple to use.
+Atomic Boolean package for Go, optimized for performance yet simple to use.
 
-Use this for cleaner code.
+Designed for cleaner code.
 
 ## Usage
 
 ```go
-import "github.com/tevino/abool"
+import "github.com/tevino/abool/v2"
 
-cond := abool.New()  // default to false
+cond := abool.New()     // default to false
 
-cond.Set()                 // Set to true
-cond.IsSet()               // Returns true
-cond.UnSet()               // Set to false
-cond.SetTo(true)           // Set to whatever you want
-cond.SetToIf(false, true)  // Set to true if it is false, returns false(not set)
+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(old, new)  // Sets to `new` only if the Boolean matches the `old`, returns whether succeeded
+cond.Toggle()           // Flip the value of `cond`, returns the value before flipping
 
 
 // embedding
@@ -26,24 +29,39 @@ type Foo struct {
 }
 ```
 
-## Benchmark:
+## Benchmark
 
-- Go 1.6.2
-- OS X 10.11.4
+```
+go: v1.18.2
+goos: darwin
+goarch: amd64
+cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
 
-```shell
 # Read
-BenchmarkMutexRead-4       	100000000	        21.0 ns/op
-BenchmarkAtomicValueRead-4 	200000000	         6.30 ns/op
-BenchmarkAtomicBoolRead-4  	300000000	         4.21 ns/op  # <--- This package
+BenchmarkMutexRead-12           	100000000	        10.24   ns/op
+BenchmarkAtomicValueRead-12     	1000000000	         0.4690 ns/op
+BenchmarkAtomicBoolRead-12      	1000000000	         0.2345 ns/op  # <--- This package
 
 # Write
-BenchmarkMutexWrite-4      	100000000	        21.6 ns/op
-BenchmarkAtomicValueWrite-4	 30000000	        43.4 ns/op
-BenchmarkAtomicBoolWrite-4 	200000000	         9.87 ns/op  # <--- This package
+BenchmarkMutexWrite-12          	100000000	        10.19  ns/op
+BenchmarkAtomicValueWrite-12    	164918696	         7.235 ns/op
+BenchmarkAtomicBoolWrite-12     	278729533	         4.341 ns/op  # <--- This package
 
 # CAS
-BenchmarkMutexCAS-4        	 30000000	        44.9 ns/op
-BenchmarkAtomicBoolCAS-4   	100000000	        11.7 ns/op   # <--- This package
+BenchmarkMutexCAS-12            	57333123	        20.26  ns/op
+BenchmarkAtomicBoolCAS-12       	203575494	         5.755 ns/op  # <--- This package
+
+# Toggle
+BenchmarkAtomicBoolToggle-12    	145249862	         8.196 ns/op  # <--- This package
 ```
 
+## Special thanks to contributors
+
+- [barryz](https://github.com/barryz)
+  - Added the `Toggle` method
+- [Lucas Rouckhout](https://github.com/LucasRouckhout)
+  - Implemented JSON Unmarshal and Marshal interface
+- [Sebastian Schicho](https://github.com/schicho)
+  - Reported a regression with test case
+  - Hide the underlying `int`
+  - Reintroduced the `Toggle` method
diff --git a/bool.go b/bool.go
index fdda210..87256b7 100644
--- a/bool.go
+++ b/bool.go
@@ -2,14 +2,17 @@
 // better performance.
 package abool
 
-import "sync/atomic"
+import (
+	"encoding/json"
+	"sync/atomic"
+)
 
-// New creates an AtomicBool with default to false
+// New creates an AtomicBool with default set to false.
 func New() *AtomicBool {
 	return new(AtomicBool)
 }
 
-// NewBool creates an AtomicBool with given default value
+// NewBool creates an AtomicBool with given default value.
 func NewBool(ok bool) *AtomicBool {
 	ab := New()
 	if ok {
@@ -18,39 +21,56 @@ func NewBool(ok bool) *AtomicBool {
 	return ab
 }
 
-// AtomicBool is an atomic Boolean
-// 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
-type AtomicBool int32
+// AtomicBool is an atomic Boolean.
+// 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.
+type AtomicBool struct {
+	boolean int32
+}
 
-// Set sets the Boolean to true
+// Set sets the Boolean to true.
 func (ab *AtomicBool) Set() {
-	atomic.StoreInt32((*int32)(ab), 1)
+	atomic.StoreInt32(&ab.boolean, 1)
 }
 
-// UnSet sets the Boolean to false
+// UnSet sets the Boolean to false.
 func (ab *AtomicBool) UnSet() {
-	atomic.StoreInt32((*int32)(ab), 0)
+	atomic.StoreInt32(&ab.boolean, 0)
 }
 
-// IsSet returns whether the Boolean is true
+// IsSet returns whether the Boolean is true.
 func (ab *AtomicBool) IsSet() bool {
-	return atomic.LoadInt32((*int32)(ab)) == 1
+	return atomic.LoadInt32(&ab.boolean)&1 == 1
+}
+
+// IsNotSet returns whether the Boolean is false.
+func (ab *AtomicBool) IsNotSet() bool {
+	return !ab.IsSet()
 }
 
-// SetTo sets the boolean with given Boolean
+// SetTo sets the boolean with given Boolean.
 func (ab *AtomicBool) SetTo(yes bool) {
 	if yes {
-		atomic.StoreInt32((*int32)(ab), 1)
+		atomic.StoreInt32(&ab.boolean, 1)
 	} else {
-		atomic.StoreInt32((*int32)(ab), 0)
+		atomic.StoreInt32(&ab.boolean, 0)
+	}
+}
+
+// Toggle inverts the Boolean then returns the value before inverting.
+// Based on: https://github.com/uber-go/atomic/blob/3504dfaa1fa414923b1c8693f45d2f6931daf229/bool_ext.go#L40
+func (ab *AtomicBool) Toggle() bool {
+	var old bool
+	for {
+		old = ab.IsSet()
+		if ab.SetToIf(old, !old) {
+			return old
+		}
 	}
 }
 
-// SetToIf sets the Boolean to new only if the Boolean matches the old
-// Returns whether the set was done
+// SetToIf sets the Boolean to new only if the Boolean matches the old.
+// Returns whether the set was done.
 func (ab *AtomicBool) SetToIf(old, new bool) (set bool) {
 	var o, n int32
 	if old {
@@ -59,5 +79,23 @@ func (ab *AtomicBool) SetToIf(old, new bool) (set bool) {
 	if new {
 		n = 1
 	}
-	return atomic.CompareAndSwapInt32((*int32)(ab), o, n)
+	return atomic.CompareAndSwapInt32(&ab.boolean, o, n)
+}
+
+// MarshalJSON behaves the same as if the AtomicBool is a builtin.bool.
+// NOTE: There's no lock during the process, usually it shouldn't be called with other methods in parallel.
+func (ab *AtomicBool) MarshalJSON() ([]byte, error) {
+	return json.Marshal(ab.IsSet())
+}
+
+// UnmarshalJSON behaves the same as if the AtomicBool is a builtin.bool.
+// NOTE: There's no lock during the process, usually it shouldn't be called with other methods in parallel.
+func (ab *AtomicBool) UnmarshalJSON(b []byte) error {
+	var v bool
+	err := json.Unmarshal(b, &v)
+
+	if err == nil {
+		ab.SetTo(v)
+	}
+	return err
 }
diff --git a/bool_test.go b/bool_test.go
index d63517f..2a977dd 100644
--- a/bool_test.go
+++ b/bool_test.go
@@ -1,26 +1,42 @@
 package abool
 
 import (
+	"encoding/json"
 	"sync"
 	"sync/atomic"
 	"testing"
 )
 
-func TestBool(t *testing.T) {
-	v := NewBool(true)
+func TestDefaultValue(t *testing.T) {
+	t.Parallel()
+	v := New()
+	if v.IsSet() {
+		t.Fatal("Empty value of AtomicBool should be false")
+	}
+
+	v = NewBool(true)
 	if !v.IsSet() {
-		t.Fatal("NewValue(true) failed")
+		t.Fatal("NewValue(true) should be true")
 	}
 
 	v = NewBool(false)
 	if v.IsSet() {
-		t.Fatal("NewValue(false) failed")
+		t.Fatal("NewValue(false) should be false")
 	}
+}
 
-	v = New()
-	if v.IsSet() {
-		t.Fatal("Empty value of AtomicBool should be false")
+func TestIsNotSet(t *testing.T) {
+	t.Parallel()
+	v := New()
+
+	if v.IsSet() == v.IsNotSet() {
+		t.Fatal("AtomicBool.IsNotSet() should be the opposite of IsSet()")
 	}
+}
+
+func TestSetUnSet(t *testing.T) {
+	t.Parallel()
+	v := New()
 
 	v.Set()
 	if !v.IsSet() {
@@ -31,6 +47,11 @@ func TestBool(t *testing.T) {
 	if v.IsSet() {
 		t.Fatal("AtomicBool.UnSet() failed")
 	}
+}
+
+func TestSetTo(t *testing.T) {
+	t.Parallel()
+	v := New()
 
 	v.SetTo(true)
 	if !v.IsSet() {
@@ -51,6 +72,43 @@ func TestBool(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 TestRace(t *testing.T) {
 	repeat := 10000
 	var wg sync.WaitGroup
@@ -83,12 +141,105 @@ func TestRace(t *testing.T) {
 	wg.Wait()
 }
 
+func TestSetToIfAfterMultipleToggles(t *testing.T) {
+	v := New() // false
+
+	v.Toggle() // true
+	v.Toggle() // false
+	v.Toggle() // true
+
+	// As v is true, it should now be flipped to false
+	v.SetToIf(true, false)
+	expected := false
+
+	if v.IsSet() != expected {
+		t.Fatalf("Toggling the value atleast 3 times, until it's true, `SetToIf(true, false)` should flip v to false, expected: %v, got %v", expected, v.IsSet())
+	}
+}
+
+func TestJSONCompatibleWithBuiltinBool(t *testing.T) {
+	for _, value := range []bool{true, false} {
+		// Test bool -> bytes -> AtomicBool
+
+		// act 1. bool -> bytes
+		buf, err := json.Marshal(value)
+		if err != nil {
+			t.Fatalf("json.Marshal(%t) failed: %s", value, err)
+		}
+
+		// act 2. bytes -> AtomicBool
+		//
+		// Try to unmarshall the JSON byte slice
+		// of a normal boolean into an AtomicBool
+		//
+		// Create an AtomicBool with the oppsite default to ensure the unmarshal process did the work
+		ab := NewBool(!value)
+		err = json.Unmarshal(buf, ab)
+		if err != nil {
+			t.Fatalf(`json.Unmarshal("%s", %T) failed: %s`, buf, ab, err)
+		}
+		// assert
+		if ab.IsSet() != value {
+			t.Fatalf("Expected AtomicBool to represent %t but actual value was %t", value, ab.IsSet())
+		}
+
+		// Test AtomicBool -> bytes -> bool
+
+		// act 3. AtomicBool -> bytes
+		buf, err = json.Marshal(ab)
+		if err != nil {
+			t.Fatalf("json.Marshal(%T) failed: %s", ab, err)
+		}
+
+		// using the opposite value for the same reason as the former case
+		b := ab.IsNotSet()
+		// act 4. bytes -> bool
+		err = json.Unmarshal(buf, &b)
+		if err != nil {
+			t.Fatalf(`json.Unmarshal("%s", %T) failed: %s`, buf, &b, err)
+		}
+		// assert
+		if b != ab.IsSet() {
+			t.Fatalf(`json.Unmarshal("%s", %T) didn't work, expected %t, got %t`, buf, ab, ab.IsSet(), b)
+		}
+	}
+}
+
+func TestUnmarshalJSONErrorNoWrite(t *testing.T) {
+	for _, val := range []bool{true, false} {
+		ab := NewBool(val)
+		oldVal := ab.IsSet()
+		buf := []byte("invalid-json")
+		err := json.Unmarshal(buf, ab)
+		if err == nil {
+			t.Fatalf(`Error expected from json.Unmarshal("%s", %T)`, buf, ab)
+		}
+		if oldVal != ab.IsSet() {
+			t.Fatal("Failed json.Unmarshal modified the value of AtomicBool which is not expected")
+		}
+	}
+}
+
 func ExampleAtomicBool() {
-	cond := New()    // default to false
-	cond.Set()       // set to true
-	cond.IsSet()     // returns true
-	cond.UnSet()     // set to false
-	cond.SetTo(true) // set to whatever you want
+	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
+}
+
+func BenchmarkAtomicBoolToggle(b *testing.B) {
+	v := New()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		_ = v.Toggle()
+	}
 }
 
 // Benchmark Read
@@ -164,7 +315,6 @@ func BenchmarkMutexCAS(b *testing.B) {
 		}
 		m.Unlock()
 	}
-	b.StopTimer()
 }
 
 func BenchmarkAtomicBoolCAS(b *testing.B) {
diff --git a/debian/changelog b/debian/changelog
index 214eee4..bcc3f58 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,10 @@
-golang-github-tevino-abool (0.0~git20170917.9b9efcf-3) UNRELEASED; urgency=low
+golang-github-tevino-abool (0.0~git20220531.edeb703+ds-1) UNRELEASED; urgency=low
 
   * Set upstream metadata fields: Bug-Database, Bug-Submit.
   * Update standards version to 4.5.0, no changes needed.
+  * New upstream snapshot.
 
- -- Debian Janitor <janitor@jelmer.uk>  Fri, 28 Aug 2020 21:39:23 -0000
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 10 Nov 2022 06:45:21 -0000
 
 golang-github-tevino-abool (0.0~git20170917.9b9efcf-2) unstable; urgency=medium
 
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..878307a
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/tevino/abool
+
+go 1.14
diff --git a/v2/LICENSE b/v2/LICENSE
new file mode 100644
index 0000000..f20dac8
--- /dev/null
+++ b/v2/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Tevin Zhang
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/v2/bool.go b/v2/bool.go
new file mode 100644
index 0000000..bf1eb86
--- /dev/null
+++ b/v2/bool.go
@@ -0,0 +1,87 @@
+// Package abool provides atomic Boolean type for cleaner code and
+// better performance.
+package abool
+
+import (
+	"encoding/json"
+	"sync/atomic"
+)
+
+// New creates an AtomicBool with default set to false.
+func New() *AtomicBool {
+	return new(AtomicBool)
+}
+
+// NewBool creates an AtomicBool with given default value.
+func NewBool(ok bool) *AtomicBool {
+	ab := New()
+	if ok {
+		ab.Set()
+	}
+	return ab
+}
+
+// AtomicBool is an atomic Boolean.
+// 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.
+type AtomicBool int32
+
+// Set sets the Boolean to true.
+func (ab *AtomicBool) Set() {
+	atomic.StoreInt32((*int32)(ab), 1)
+}
+
+// UnSet sets the Boolean to false.
+func (ab *AtomicBool) UnSet() {
+	atomic.StoreInt32((*int32)(ab), 0)
+}
+
+// IsSet returns whether the Boolean is true.
+func (ab *AtomicBool) IsSet() bool {
+	return atomic.LoadInt32((*int32)(ab)) == 1
+}
+
+// IsNotSet returns whether the Boolean is false.
+func (ab *AtomicBool) IsNotSet() bool {
+	return !ab.IsSet()
+}
+
+// SetTo sets the boolean with given Boolean.
+func (ab *AtomicBool) SetTo(yes bool) {
+	if yes {
+		atomic.StoreInt32((*int32)(ab), 1)
+	} else {
+		atomic.StoreInt32((*int32)(ab), 0)
+	}
+}
+
+// SetToIf sets the Boolean to new only if the Boolean matches the old.
+// Returns whether the set was done.
+func (ab *AtomicBool) SetToIf(old, new bool) (set bool) {
+	var o, n int32
+	if old {
+		o = 1
+	}
+	if new {
+		n = 1
+	}
+	return atomic.CompareAndSwapInt32((*int32)(ab), o, n)
+}
+
+// MarshalJSON behaves the same as if the AtomicBool is a builtin.bool.
+// NOTE: There's no lock during the process, usually it shouldn't be called with other methods in parallel.
+func (ab *AtomicBool) MarshalJSON() ([]byte, error) {
+	return json.Marshal(ab.IsSet())
+}
+
+// UnmarshalJSON behaves the same as if the AtomicBool is a builtin.bool.
+// NOTE: There's no lock during the process, usually it shouldn't be called with other methods in parallel.
+func (ab *AtomicBool) UnmarshalJSON(b []byte) error {
+	var v bool
+	err := json.Unmarshal(b, &v)
+
+	if err == nil {
+		ab.SetTo(v)
+	}
+	return err
+}
diff --git a/v2/bool_test.go b/v2/bool_test.go
new file mode 100644
index 0000000..d418c48
--- /dev/null
+++ b/v2/bool_test.go
@@ -0,0 +1,265 @@
+package abool
+
+import (
+	"encoding/json"
+	"sync"
+	"sync/atomic"
+	"testing"
+)
+
+func TestDefaultValue(t *testing.T) {
+	t.Parallel()
+	v := New()
+	if v.IsSet() {
+		t.Fatal("Empty value of AtomicBool should be false")
+	}
+
+	v = NewBool(true)
+	if !v.IsSet() {
+		t.Fatal("NewValue(true) should be true")
+	}
+
+	v = NewBool(false)
+	if v.IsSet() {
+		t.Fatal("NewValue(false) should be false")
+	}
+}
+
+func TestIsNotSet(t *testing.T) {
+	t.Parallel()
+	v := New()
+
+	if v.IsSet() == v.IsNotSet() {
+		t.Fatal("AtomicBool.IsNotSet() should be the opposite of IsSet()")
+	}
+}
+
+func TestSetUnSet(t *testing.T) {
+	t.Parallel()
+	v := New()
+
+	v.Set()
+	if !v.IsSet() {
+		t.Fatal("AtomicBool.Set() failed")
+	}
+
+	v.UnSet()
+	if v.IsSet() {
+		t.Fatal("AtomicBool.UnSet() failed")
+	}
+}
+
+func TestSetTo(t *testing.T) {
+	t.Parallel()
+	v := New()
+
+	v.SetTo(true)
+	if !v.IsSet() {
+		t.Fatal("AtomicBool.SetTo(true) failed")
+	}
+
+	v.SetTo(false)
+	if v.IsSet() {
+		t.Fatal("AtomicBool.SetTo(false) failed")
+	}
+
+	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")
+	}
+}
+
+func TestRace(t *testing.T) {
+	repeat := 10000
+	var wg sync.WaitGroup
+	wg.Add(repeat * 3)
+	v := New()
+
+	// Writer
+	go func() {
+		for i := 0; i < repeat; i++ {
+			v.Set()
+			wg.Done()
+		}
+	}()
+
+	// Reader
+	go func() {
+		for i := 0; i < repeat; i++ {
+			v.IsSet()
+			wg.Done()
+		}
+	}()
+
+	// Writer
+	go func() {
+		for i := 0; i < repeat; i++ {
+			v.UnSet()
+			wg.Done()
+		}
+	}()
+	wg.Wait()
+}
+
+func TestJSONCompatibleWithBuiltinBool(t *testing.T) {
+	for _, value := range []bool{true, false} {
+		// Test bool -> bytes -> AtomicBool
+
+		// act 1. bool -> bytes
+		buf, err := json.Marshal(value)
+		if err != nil {
+			t.Fatalf("json.Marshal(%t) failed: %s", value, err)
+		}
+
+		// act 2. bytes -> AtomicBool
+		//
+		// Try to unmarshall the JSON byte slice
+		// of a normal boolean into an AtomicBool
+		//
+		// Create an AtomicBool with the oppsite default to ensure the unmarshal process did the work
+		ab := NewBool(!value)
+		err = json.Unmarshal(buf, ab)
+		if err != nil {
+			t.Fatalf(`json.Unmarshal("%s", %T) failed: %s`, buf, ab, err)
+		}
+		// assert
+		if ab.IsSet() != value {
+			t.Fatalf("Expected AtomicBool to represent %t but actual value was %t", value, ab.IsSet())
+		}
+
+		// Test AtomicBool -> bytes -> bool
+
+		// act 3. AtomicBool -> bytes
+		buf, err = json.Marshal(ab)
+		if err != nil {
+			t.Fatalf("json.Marshal(%T) failed: %s", ab, err)
+		}
+
+		// using the opposite value for the same reason as the former case
+		b := ab.IsNotSet()
+		// act 4. bytes -> bool
+		err = json.Unmarshal(buf, &b)
+		if err != nil {
+			t.Fatalf(`json.Unmarshal("%s", %T) failed: %s`, buf, &b, err)
+		}
+		// assert
+		if b != ab.IsSet() {
+			t.Fatalf(`json.Unmarshal("%s", %T) didn't work, expected %t, got %t`, buf, ab, ab.IsSet(), b)
+		}
+	}
+}
+
+func TestUnmarshalJSONErrorNoWrite(t *testing.T) {
+	for _, val := range []bool{true, false} {
+		ab := NewBool(val)
+		oldVal := ab.IsSet()
+		buf := []byte("invalid-json")
+		err := json.Unmarshal(buf, ab)
+		if err == nil {
+			t.Fatalf(`Error expected from json.Unmarshal("%s", %T)`, buf, ab)
+		}
+		if oldVal != ab.IsSet() {
+			t.Fatal("Failed json.Unmarshal modified the value of AtomicBool which is not expected")
+		}
+	}
+}
+
+func ExampleAtomicBool() {
+	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
+}
+
+// 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()
+	}
+}
+
+// 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()
+	}
+}
+
+func BenchmarkAtomicBoolCAS(b *testing.B) {
+	v := New()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		v.SetToIf(false, true)
+	}
+}
diff --git a/v2/go.mod b/v2/go.mod
new file mode 100644
index 0000000..53e2f65
--- /dev/null
+++ b/v2/go.mod
@@ -0,0 +1,3 @@
+module github.com/tevino/abool/v2
+
+go 1.17

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/tevino/abool/go.mod
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/tevino/abool/v2/bool.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/tevino/abool/v2/bool_test.go

No differences were encountered in the control files

More details

Full run details