diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-03-31 08:54:22 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-03-31 09:17:03 +0200 |
commit | 79b34c2f1e0ba91ff5f4f879dc42eddfd82cc563 (patch) | |
tree | b2e6c2bb48e4cd7b2b8ecadae3a4f5b075cd576b /tpl | |
parent | 3c6b16d5a237acb97372c8d64a49478c8c8bd2d1 (diff) |
tplimpl: Fix deadlock in getJSON
Fixes #3211
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/tplimpl/template_resources.go | 9 | ||||
-rw-r--r-- | tpl/tplimpl/template_resources_test.go | 43 |
2 files changed, 51 insertions, 1 deletions
diff --git a/tpl/tplimpl/template_resources.go b/tpl/tplimpl/template_resources.go index 48a79d066..f10aa72e3 100644 --- a/tpl/tplimpl/template_resources.go +++ b/tpl/tplimpl/template_resources.go @@ -36,6 +36,7 @@ var ( remoteURLLock = &remoteLock{m: make(map[string]*sync.Mutex)} resSleep = time.Second * 2 // if JSON decoding failed sleep for n seconds before retrying resRetries = 1 // number of retries to load the JSON from URL or local file system + resCacheMu sync.RWMutex ) type remoteLock struct { @@ -49,8 +50,8 @@ func (l *remoteLock) URLLock(url string) { if _, ok := l.m[url]; !ok { l.m[url] = &sync.Mutex{} } - l.m[url].Lock() l.Unlock() + l.m[url].Lock() } // URLUnlock unlocks an URL when the download has been finished. Use only in defer calls. @@ -70,6 +71,9 @@ func getCacheFileID(cfg config.Provider, id string) string { // resGetCache returns the content for an ID from the file cache or an error // if the file is not found returns nil,nil func resGetCache(id string, fs afero.Fs, cfg config.Provider, ignoreCache bool) ([]byte, error) { + resCacheMu.RLock() + defer resCacheMu.RUnlock() + if ignoreCache { return nil, nil } @@ -88,6 +92,9 @@ func resGetCache(id string, fs afero.Fs, cfg config.Provider, ignoreCache bool) // resWriteCache writes bytes to an ID into the file cache func resWriteCache(id string, c []byte, fs afero.Fs, cfg config.Provider, ignoreCache bool) error { + resCacheMu.Lock() + defer resCacheMu.Unlock() + if ignoreCache { return nil } diff --git a/tpl/tplimpl/template_resources_test.go b/tpl/tplimpl/template_resources_test.go index 8a9f62659..e7e7f7782 100644 --- a/tpl/tplimpl/template_resources_test.go +++ b/tpl/tplimpl/template_resources_test.go @@ -20,7 +20,9 @@ import ( "net/http/httptest" "net/url" "strings" + "sync" "testing" + "time" "github.com/spf13/afero" "github.com/spf13/hugo/helpers" @@ -173,6 +175,47 @@ func TestScpGetRemote(t *testing.T) { } } +func TestScpGetRemoteParallel(t *testing.T) { + t.Parallel() + fs := new(afero.MemMapFs) + content := []byte(`T€st Content 123`) + url := "http://Foo.Bar/foo_Bar-Foo" + srv, cl := getTestServer(func(w http.ResponseWriter, r *http.Request) { + w.Write(content) + }) + defer func() { srv.Close() }() + + for _, ignoreCache := range []bool{false, true} { + + cfg := viper.New() + cfg.Set("ignoreCache", ignoreCache) + + var wg sync.WaitGroup + + for i := 0; i < 50; i++ { + wg.Add(1) + go func(gor int) { + defer wg.Done() + for j := 0; j < 10; j++ { + c, err := resGetRemote(url, fs, cfg, cl) + if err != nil { + t.Errorf("Error getting resource content: %s", err) + } + if !bytes.Equal(c, content) { + t.Errorf("\nNet Expected: %s\nNet Actual: %s\n", string(content), string(c)) + } + + time.Sleep(23 * time.Millisecond) + } + }(i) + } + + wg.Wait() + } + + t.Log("Done!") +} + func TestParseCSV(t *testing.T) { t.Parallel() |