From 6d06721fc8f0607bb65554e00b7ea2e997776188 Mon Sep 17 00:00:00 2001 From: Windfarer Date: Wed, 11 Aug 2021 07:59:58 +0800 Subject: [PATCH] fix(config): fix data race (#1316) * data race * fix values * lock --- config/file/file_test.go | 49 +++++++++++++++++++++++++++++++++------- config/reader.go | 13 +++++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/config/file/file_test.go b/config/file/file_test.go index 57fae145a..5f9621fa2 100644 --- a/config/file/file_test.go +++ b/config/file/file_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "sync" "testing" "time" @@ -68,14 +69,14 @@ const ( } }` -// _testYaml = ` -//Foo: -// bar : -// - {name: nihao,age: 1} -// - {name: nihao,age: 1} -// -// -//` + // _testYaml = ` + //Foo: + // bar : + // - {name: nihao,age: 1} + // - {name: nihao,age: 1} + // + // + //` ) //func TestScan(t *testing.T) { @@ -288,3 +289,35 @@ func testScan(t *testing.T, c config.Config) { } t.Log(conf) } + +func TestMergeDataRace(t *testing.T) { + path := filepath.Join(t.TempDir(), "test_config.json") + defer os.Remove(path) + if err := ioutil.WriteFile(path, []byte(_testJSON), 0666); err != nil { + t.Error(err) + } + c := config.New(config.WithSource( + NewSource(path), + )) + wg := &sync.WaitGroup{} + wg.Add(2) + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + var conf struct{} + if err := c.Scan(&conf); err != nil { + t.Error(err) + } + } + }() + + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + if err := c.Load(); err != nil { + t.Error(err) + } + } + }() + wg.Wait() +} diff --git a/config/reader.go b/config/reader.go index c18b91d34..1410780e8 100644 --- a/config/reader.go +++ b/config/reader.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "strings" + "sync" "github.com/imdario/mergo" "google.golang.org/protobuf/encoding/protojson" @@ -23,17 +24,21 @@ type Reader interface { type reader struct { opts options values map[string]interface{} + lock sync.Mutex } func newReader(opts options) Reader { return &reader{ opts: opts, values: make(map[string]interface{}), + lock: sync.Mutex{}, } } func (r *reader) Merge(kvs ...*KeyValue) error { + r.lock.Lock() merged, err := cloneMap(r.values) + r.lock.Unlock() if err != nil { return err } @@ -46,19 +51,27 @@ func (r *reader) Merge(kvs ...*KeyValue) error { return err } } + r.lock.Lock() r.values = merged + r.lock.Unlock() return nil } func (r *reader) Value(path string) (Value, bool) { + r.lock.Lock() + defer r.lock.Unlock() return readValue(r.values, path) } func (r *reader) Source() ([]byte, error) { + r.lock.Lock() + defer r.lock.Unlock() return marshalJSON(convertMap(r.values)) } func (r *reader) Resolve() error { + r.lock.Lock() + defer r.lock.Unlock() return r.opts.resolver(r.values) }