diff --git a/command/agent/local.go b/command/agent/local.go index 4e6a944a8c..62e818bf84 100644 --- a/command/agent/local.go +++ b/command/agent/local.go @@ -109,18 +109,21 @@ func (l *localState) ConsulServerUp() { // Pause is used to pause state synchronization, this can be // used to make batch changes func (l *localState) Pause() { - atomic.StoreInt32(&l.paused, 1) + atomic.AddInt32(&l.paused, 1) } // Resume is used to resume state synchronization func (l *localState) Resume() { - atomic.StoreInt32(&l.paused, 0) + paused := atomic.AddInt32(&l.paused, -1) + if paused < 0 { + panic("unbalanced localState.Resume() detected") + } l.changeMade() } // isPaused is used to check if we are paused func (l *localState) isPaused() bool { - return atomic.LoadInt32(&l.paused) == 1 + return atomic.LoadInt32(&l.paused) > 0 } // ServiceToken returns the configured ACL token for the given diff --git a/command/agent/local_test.go b/command/agent/local_test.go index 4b7b566e26..d8f6cfbfee 100644 --- a/command/agent/local_test.go +++ b/command/agent/local_test.go @@ -760,6 +760,38 @@ func TestAgent_checkTokens(t *testing.T) { } } +func TestAgent_nestedPauseResume(t *testing.T) { + l := new(localState) + if l.isPaused() != false { + t.Fatal("localState should be unPaused after init") + } + l.Pause() + if l.isPaused() != true { + t.Fatal("localState should be Paused after first call to Pause()") + } + l.Pause() + if l.isPaused() != true { + t.Fatal("localState should STILL be Paused after second call to Pause()") + } + l.Resume() + if l.isPaused() != true { + t.Fatal("localState should STILL be Paused after FIRST call to Resume()") + } + l.Resume() + if l.isPaused() != false { + t.Fatal("localState should NOT be Paused after SECOND call to Resume()") + } + + defer func() { + err := recover() + if err == nil { + t.Fatal("unbalanced Resume() should cause a panic()") + } + }() + l.Resume() + +} + var testRegisterRules = ` service "api" { policy = "write"