engine: graph: Use an atomic bool instead of a mutex

The isStateOK variable can be accessed concurrently as these are
supposed to be "benign" races. As such, they need to be labelled as such
so that we don't hit some undefined compiler behaviour.

Here are five good references relating to "benign" data races in golang.

1) https://web.archive.org/web/20181022150257/https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong

2) https://go.dev/ref/mem - "Informal Overview" section.

3) https://docs.oracle.com/cd/E19205-01/820-0619/gecqt/index.html

4) https://www.usenix.org/legacy/event/hotpar11/tech/final_files/Boehm.pdf

5) https://go.dev/doc/articles/race_detector

TL;DR: wrap your benign races with sync/atomic or eliminate them.
This commit is contained in:
James Shubin
2024-01-05 15:49:19 -05:00
parent c2f508e261
commit bc63b7608e
3 changed files with 14 additions and 9 deletions

View File

@@ -21,6 +21,7 @@ import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/purpleidea/mgmt/converger"
@@ -60,9 +61,9 @@ type State struct {
// Logf is the logging function that should be used to display messages.
Logf func(format string, v ...interface{})
timestamp int64 // last updated timestamp
isStateOK bool // is state OK or do we need to run CheckApply ?
workerErr error // did the Worker error?
timestamp int64 // last updated timestamp
isStateOK *atomic.Bool // is state OK or do we need to run CheckApply ?
workerErr error // did the Worker error?
mutex *sync.RWMutex // used for editing state properties
@@ -145,6 +146,8 @@ func (obj *State) Init() error {
return fmt.Errorf("the Logf function is missing")
}
obj.isStateOK = &atomic.Bool{}
obj.mutex = &sync.RWMutex{}
obj.doneCtx, obj.doneCtxCancel = context.WithCancel(context.Background())
@@ -390,9 +393,9 @@ func (obj *State) event() {
// CheckApply will have some work to do in order to converge it.
func (obj *State) setDirty() {
obj.tuid.StopTimer()
obj.mutex.Lock()
obj.isStateOK = false // concurrent write
obj.mutex.Unlock()
//obj.mutex.Lock()
obj.isStateOK.Store(false) // concurrent write
//obj.mutex.Unlock()
}
// poll is a replacement for Watch when the Poll metaparameter is used.