summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-03-31 08:54:22 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-03-31 09:17:03 +0200
commit79b34c2f1e0ba91ff5f4f879dc42eddfd82cc563 (patch)
treeb2e6c2bb48e4cd7b2b8ecadae3a4f5b075cd576b /tpl
parent3c6b16d5a237acb97372c8d64a49478c8c8bd2d1 (diff)
tplimpl: Fix deadlock in getJSON
Fixes #3211
Diffstat (limited to 'tpl')
-rw-r--r--tpl/tplimpl/template_resources.go9
-rw-r--r--tpl/tplimpl/template_resources_test.go43
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()