diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-02-13 21:45:51 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-02-14 20:59:02 +0100 |
commit | 58382e9572559f88ed57f5c4899a37d489bcb220 (patch) | |
tree | 84a2616485e240f2cf5261e60d79a1bf45ae2b16 /resource/image_test.go | |
parent | 53dac9a5067dfd84283a2b2d837738a63b63b596 (diff) |
resource: Fix multi-threaded image processing issue
When doing something like this with the same image from a partial used in, say, both the home page and the single page:
```bash
{{ with $img }}
{{ $big := .Fill "1024x512 top" }}
{{ $small := $big.Resize "512x" }}
{{ end }}
```
There would be timing issues making Hugo in some cases try to process the same image with the same instructions in parallel.
You would experience errors of type:
```bash
png: invalid format: not enough pixel data
```
This commit works around that by adding a mutex per image. This should also improve the performance, sligthly, as it avoids duplicate work.
The current workaround before this fix is to always operate on the original:
```bash
{{ with $img }}
{{ $big := .Fill "1024x512 top" }}
{{ $small := .Fill "512x256 top" }}
{{ end }}
```
Fixes #4404
Diffstat (limited to 'resource/image_test.go')
-rw-r--r-- | resource/image_test.go | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/resource/image_test.go b/resource/image_test.go index de706b0ac..e981a208f 100644 --- a/resource/image_test.go +++ b/resource/image_test.go @@ -15,8 +15,12 @@ package resource import ( "fmt" + "math/rand" + "strconv" "testing" + "sync" + "github.com/stretchr/testify/require" ) @@ -141,6 +145,51 @@ func TestImageTransformLongFilename(t *testing.T) { assert.Equal("/a/_hu59e56ffff1bc1d8d122b1403d34e039f_90587_c876768085288f41211f768147ba2647.jpg", resized.RelPermalink()) } +func TestImageTransformConcurrent(t *testing.T) { + + var wg sync.WaitGroup + + assert := require.New(t) + + spec := newTestResourceOsFs(assert) + + image := fetchImageForSpec(spec, assert, "sunset.jpg") + + for i := 0; i < 4; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + for j := 0; j < 5; j++ { + img := image + for k := 0; k < 2; k++ { + r1, err := img.Resize(fmt.Sprintf("%dx", id-k)) + if err != nil { + t.Fatal(err) + } + + if r1.Width() != id-k { + t.Fatalf("Width: %d:%d", r1.Width(), j) + } + + r2, err := r1.Resize(fmt.Sprintf("%dx", id-k-1)) + if err != nil { + t.Fatal(err) + } + + _, err = r2.decodeSource() + if err != nil { + t.Fatal("Err decode:", err) + } + + img = r1 + } + } + }(i + 20) + } + + wg.Wait() +} + func TestDecodeImaging(t *testing.T) { assert := require.New(t) m := map[string]interface{}{ @@ -208,3 +257,22 @@ func TestImageWithMetadata(t *testing.T) { assert.Equal("Sunset #1", resized.Name()) } + +func BenchmarkResizeParallel(b *testing.B) { + assert := require.New(b) + img := fetchSunset(assert) + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + w := rand.Intn(10) + 10 + resized, err := img.Resize(strconv.Itoa(w) + "x") + if err != nil { + b.Fatal(err) + } + _, err = resized.Resize(strconv.Itoa(w-1) + "x") + if err != nil { + b.Fatal(err) + } + } + }) +} |