Quellcode durchsuchen

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 vor 11 Jahren
Ursprung
Commit
eaf0b22478
1 geänderte Dateien mit 9 neuen und 14 gelöschten Zeilen
  1. 9 14
      breaker/breaker.go

+ 9 - 14
breaker/breaker.go

@@ -4,6 +4,7 @@ package breaker
 import (
 import (
 	"errors"
 	"errors"
 	"sync"
 	"sync"
+	"sync/atomic"
 	"time"
 	"time"
 )
 )
 
 
@@ -11,10 +12,8 @@ import (
 // because the breaker is currently open.
 // because the breaker is currently open.
 var ErrBreakerOpen = errors.New("circuit breaker is open")
 var ErrBreakerOpen = errors.New("circuit breaker is open")
 
 
-type state int
-
 const (
 const (
-	closed state = iota
+	closed uint32 = iota
 	open
 	open
 	halfOpen
 	halfOpen
 )
 )
@@ -24,8 +23,8 @@ type Breaker struct {
 	errorThreshold, successThreshold int
 	errorThreshold, successThreshold int
 	timeout                          time.Duration
 	timeout                          time.Duration
 
 
-	lock              sync.RWMutex
-	state             state
+	lock              sync.Mutex
+	state             uint32
 	errors, successes int
 	errors, successes int
 	lastError         time.Time
 	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
 // 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.
 // value. It is safe to call Run concurrently on the same Breaker.
 func (b *Breaker) Run(work func() error) error {
 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 {
 	if state == open {
 		return ErrBreakerOpen
 		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
 // the return value of the function. It is safe to call Go concurrently on the
 // same Breaker.
 // same Breaker.
 func (b *Breaker) Go(work func() error) error {
 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 {
 	if state == open {
 		return ErrBreakerOpen
 		return ErrBreakerOpen
@@ -80,7 +75,7 @@ func (b *Breaker) Go(work func() error) error {
 	return nil
 	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{}
 	var panicValue interface{}
 
 
 	result := func() error {
 	result := func() error {
@@ -159,8 +154,8 @@ func (b *Breaker) timer() {
 	b.changeState(halfOpen)
 	b.changeState(halfOpen)
 }
 }
 
 
-func (b *Breaker) changeState(newState state) {
+func (b *Breaker) changeState(newState uint32) {
 	b.errors = 0
 	b.errors = 0
 	b.successes = 0
 	b.successes = 0
-	b.state = newState
+	atomic.StoreUint32(&b.state, newState)
 }
 }