Jelajahi Sumber

More efficient synchronization

Use atomic ops on the state variable. This lets new requests go ahead even when
something has the main lock, and lets us simplify that lock to a normal Mutex
instead of an RWMutex.
Evan Huus 11 tahun lalu
induk
melakukan
eaf0b22478
1 mengubah file dengan 9 tambahan dan 14 penghapusan
  1. 9 14
      breaker/breaker.go

+ 9 - 14
breaker/breaker.go

@@ -4,6 +4,7 @@ package breaker
 import (
 	"errors"
 	"sync"
+	"sync/atomic"
 	"time"
 )
 
@@ -11,10 +12,8 @@ import (
 // because the breaker is currently open.
 var ErrBreakerOpen = errors.New("circuit breaker is open")
 
-type state int
-
 const (
-	closed state = iota
+	closed uint32 = iota
 	open
 	halfOpen
 )
@@ -24,8 +23,8 @@ type Breaker struct {
 	errorThreshold, successThreshold int
 	timeout                          time.Duration
 
-	lock              sync.RWMutex
-	state             state
+	lock              sync.Mutex
+	state             uint32
 	errors, successes int
 	lastError         time.Time
 }
@@ -47,9 +46,7 @@ func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker {
 // already open, or it will run the given function and pass along its return
 // value. It is safe to call Run concurrently on the same Breaker.
 func (b *Breaker) Run(work func() error) error {
-	b.lock.RLock()
-	state := b.state
-	b.lock.RUnlock()
+	state := atomic.LoadUint32(&b.state)
 
 	if state == open {
 		return ErrBreakerOpen
@@ -64,9 +61,7 @@ func (b *Breaker) Run(work func() error) error {
 // the return value of the function. It is safe to call Go concurrently on the
 // same Breaker.
 func (b *Breaker) Go(work func() error) error {
-	b.lock.RLock()
-	state := b.state
-	b.lock.RUnlock()
+	state := atomic.LoadUint32(&b.state)
 
 	if state == open {
 		return ErrBreakerOpen
@@ -80,7 +75,7 @@ func (b *Breaker) Go(work func() error) error {
 	return nil
 }
 
-func (b *Breaker) doWork(state state, work func() error) error {
+func (b *Breaker) doWork(state uint32, work func() error) error {
 	var panicValue interface{}
 
 	result := func() error {
@@ -159,8 +154,8 @@ func (b *Breaker) timer() {
 	b.changeState(halfOpen)
 }
 
-func (b *Breaker) changeState(newState state) {
+func (b *Breaker) changeState(newState uint32) {
 	b.errors = 0
 	b.successes = 0
-	b.state = newState
+	atomic.StoreUint32(&b.state, newState)
 }