New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
May fail when parsing YAML many times in parallel ( with yaml.MapSlice ) #316
Comments
This is due to context pool contamination; contexts that are released into the pool are not properly cleared wrt the The relevant lines are here. Note that no action to clear the context is made Lines 48 to 50 in 33858b4
and when the context is obtained, it is cleared with a call to Lines 42 to 46 in 33858b4
but Lines 52 to 59 in 33858b4
I would not be surprised if different YAML source would result in poisoning different flags, so just clearing the This is a fix that is future safe. diff --git a/scanner/context.go b/scanner/context.go
index e629a5c..1f0ce1b 100644
--- a/scanner/context.go
+++ b/scanner/context.go
@@ -50,12 +50,13 @@ func (c *Context) release() {
}
func (c *Context) reset(src []rune) {
- c.idx = 0
- c.size = len(src)
- c.src = src
- c.tokens = c.tokens[:0]
- c.resetBuffer()
- c.isSingleLine = true
+ *c = Context{
+ buf: c.buf[:0],
+ size: len(src),
+ src: src,
+ tokens: c.tokens[:0],
+ isSingleLine: true,
+ }
}
func (c *Context) resetBuffer() {
diff --git a/yaml_test.go b/yaml_test.go
index 4e344c8..a691145 100644
--- a/yaml_test.go
+++ b/yaml_test.go
@@ -2,6 +2,7 @@ package yaml_test
import (
"bytes"
+ "fmt"
"reflect"
"testing"
@@ -582,3 +583,53 @@ baz:
}
})
}
+
+const mapSliceYAML = `steps:
+ req0:
+ desc: Get /users/1
+ req:
+ /users/1:
+ get: nil
+ test: |
+ current.res.status == 200
+ req1:
+ desc: Get /private
+ req:
+ /private:
+ get: nil
+ test: |
+ current.res.status == 403
+ req2:
+ desc: Get /users
+ req:
+ /users:
+ get: nil
+ test: |
+ current.res.status == 200
+`
+
+type mappedSteps struct {
+ Steps yaml.MapSlice `yaml:"steps,omitempty"`
+}
+
+func TestParallel(t *testing.T) {
+ for i := 0; i < 100; i++ {
+ t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) {
+ t.Parallel()
+ for i := 0; i < 10; i++ {
+ m := mappedSteps{
+ Steps: yaml.MapSlice{},
+ }
+ if err := yaml.Unmarshal([]byte(mapSliceYAML), &m); err != nil {
+ t.Fatal(err)
+ }
+ for _, s := range m.Steps {
+ _, ok := s.Value.(map[string]interface{})
+ if !ok {
+ t.Fatal("failed to parse")
+ }
+ }
+ }
+ })
+ }
+} |
Thank you for your report ! |
Hi @goccy. I always use your packages conveniently. Thank you !!
When trying to unmarshal YAML by preparing the following struct, it always succeeds if it is done once.
However, it may fail when trying to unmarshal the same YAML many times in parallel.
Please run playground: https://go.dev/play/p/OpLrdNGRItg
If there is a workaround for this problem, please let me know.
The text was updated successfully, but these errors were encountered: