}
// To deal with recursive types, populate the map with an
- // indirect func before we build it. This type waits on the
- // real func (f) to be ready and then calls it. This indirect
- // func is only used for recursive types.
- var (
- wg sync.WaitGroup
- f encoderFunc
- )
- wg.Add(1)
+ // indirect func before we build it. If the type is recursive,
+ // the second lookup for the type will return the indirect func.
+ //
+ // This indirect func is only used for recursive types,
+ // and briefly during racing calls to typeEncoder.
+ indirect := sync.OnceValue(func() encoderFunc {
+ return newTypeEncoder(t, true)
+ })
fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) {
- wg.Wait()
- f(e, v, opts)
+ indirect()(e, v, opts)
}))
if loaded {
return fi.(encoderFunc)
}
- // Compute the real encoder and replace the indirect func with it.
- f = newTypeEncoder(t, true)
- wg.Done()
+ f := indirect()
encoderCache.Store(t, f)
return f
}
"regexp"
"runtime/debug"
"strconv"
+ "sync"
"testing"
+ "testing/synctest"
"time"
)
}
}
}
+
+// Issue #73733: encoding/json used a WaitGroup to coordinate access to cache entries.
+// Since WaitGroup.Wait is durably blocking, this caused apparent deadlocks when
+// multiple bubbles called json.Marshal at the same time.
+func TestSynctestMarshal(t *testing.T) {
+ var wg sync.WaitGroup
+ for range 5 {
+ wg.Go(func() {
+ synctest.Test(t, func(t *testing.T) {
+ _, err := Marshal([]string{})
+ if err != nil {
+ t.Errorf("Marshal: %v", err)
+ }
+ })
+ })
+ }
+ wg.Wait()
+}
"regexp"
"runtime/debug"
"strconv"
+ "sync"
"testing"
+ "testing/synctest"
"time"
)
}
}
}
+
+// Issue #73733: encoding/json used a WaitGroup to coordinate access to cache entries.
+// Since WaitGroup.Wait is durably blocking, this caused apparent deadlocks when
+// multiple bubbles called json.Marshal at the same time.
+func TestSynctestMarshal(t *testing.T) {
+ var wg sync.WaitGroup
+ for range 5 {
+ wg.Go(func() {
+ synctest.Test(t, func(t *testing.T) {
+ _, err := Marshal([]string{})
+ if err != nil {
+ t.Errorf("Marshal: %v", err)
+ }
+ })
+ })
+ }
+ wg.Wait()
+}