Unit test CAS
parent
5c4b48f798
commit
8bfa54d700
@ -0,0 +1,37 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCASConflict is returned when a CAS operation fails.
|
||||
ErrCASConflict = errors.New("cas conflict")
|
||||
)
|
||||
|
||||
// CheckAndSet is a simple concurrency control mechanism that allows
|
||||
// only one goroutine to execute a critical section at a time.
|
||||
type CheckAndSet struct {
|
||||
state atomic.Int32
|
||||
}
|
||||
|
||||
// NewCheckAndSet creates a new CheckAndSet instance.
|
||||
func NewCheckAndSet() *CheckAndSet {
|
||||
return &CheckAndSet{}
|
||||
}
|
||||
|
||||
// Begin attempts to enter the critical section. If another goroutine
|
||||
// is already in the critical section, Begin returns an error.
|
||||
func (c *CheckAndSet) Begin() error {
|
||||
if c.state.CompareAndSwap(0, 1) {
|
||||
return nil
|
||||
} else {
|
||||
return ErrCASConflict
|
||||
}
|
||||
}
|
||||
|
||||
// End exits the critical section.
|
||||
func (c *CheckAndSet) End() {
|
||||
c.state.Store(0)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package store
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_NewCAS(t *testing.T) {
|
||||
cas := NewCheckAndSet()
|
||||
if exp, got := int32(0), cas.state.Load(); exp != got {
|
||||
t.Fatalf("expected %d, got %d", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CASBegin(t *testing.T) {
|
||||
cas := NewCheckAndSet()
|
||||
if err := cas.Begin(); err != nil {
|
||||
t.Fatalf("expected nil, got %v", err)
|
||||
}
|
||||
|
||||
// Begin again, should fail
|
||||
if err := cas.Begin(); err != ErrCASConflict {
|
||||
t.Fatalf("expected %v, got %v", ErrCASConflict, err)
|
||||
}
|
||||
|
||||
// End, another begin should succeed
|
||||
cas.End()
|
||||
if err := cas.Begin(); err != nil {
|
||||
t.Fatalf("expected nil, got %v", err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue