Skip to content

Commit a5e5306

Browse files
committed
fixed concurrency issues in deferredMarshaller
1 parent 9972471 commit a5e5306

File tree

2 files changed

+43
-19
lines changed

2 files changed

+43
-19
lines changed

gateway/router/marshal/json/cache.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,23 @@ func (m *marshallersCache) loadMarshaller(rType reflect.Type, config *config.IOC
100100
return marshaller, nil
101101
}
102102

103-
func (c *pathCache) loadOrGetMarshaller(rType reflect.Type, config *config.IOConfig, path string, outputPath string, tag *format.Tag, options ...interface{}) (marshaler, error) {
104-
value, ok := c.cache.Load(rType)
103+
func (c *pathCache) loadOrGetMarshaller(rType reflect.Type, cfg *config.IOConfig, path, outPath string, tag *format.Tag, options ...interface{}) (marshaler, error) {
104+
105+
placeholder := newDeferred()
106+
value, ok := c.cache.LoadOrStore(rType, placeholder)
105107
if ok {
106108
return value.(marshaler), nil
107109
}
108110

109-
// Place a deferred placeholder to break recursive graphs for this path and type.
110-
placeholder := &deferredMarshaller{}
111-
c.storeMarshaler(rType, placeholder)
112-
113-
aMarshaler, err := c.getMarshaller(rType, config, path, outputPath, tag, options...)
111+
aMarshaller, err := c.getMarshaller(rType, cfg, path, outPath, tag, options...)
114112
if err != nil {
113+
placeholder.fail(err) // unblock anyone holding the promise
114+
c.cache.CompareAndDelete(rType, placeholder) // allow a clean retry later
115115
return nil, err
116116
}
117117

118-
// Swap placeholder with the real marshaller and set target for any users that captured it.
119-
placeholder.setTarget(aMarshaler)
120-
c.storeMarshaler(rType, aMarshaler)
121-
return aMarshaler, nil
118+
placeholder.setTarget(aMarshaller) // resolve success
119+
return aMarshaller, nil
122120
}
123121

124122
func (c *pathCache) getMarshaller(rType reflect.Type, config *config.IOConfig, path string, outputPath string, tag *format.Tag, options ...interface{}) (marshaler, error) {

gateway/router/marshal/json/marshaller_deferred.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,56 @@ package json
22

33
import (
44
"fmt"
5-
"github.com/francoispqt/gojay"
65
"unsafe"
6+
7+
"github.com/francoispqt/gojay"
78
)
89

910
// deferredMarshaller is a placeholder used to break recursive type graphs during construction.
1011
// It forwards calls to the actual target once it is set.
1112
type deferredMarshaller struct {
1213
target marshaler
14+
ready chan struct{}
15+
err error
16+
}
17+
18+
func newDeferred() *deferredMarshaller {
19+
return &deferredMarshaller{ready: make(chan struct{})}
1320
}
1421

1522
func (d *deferredMarshaller) setTarget(m marshaler) {
1623
d.target = m
24+
close(d.ready)
25+
}
26+
27+
func (d *deferredMarshaller) fail(e error) {
28+
d.err = e
29+
close(d.ready) // writes to err happen-before any receive on ready
1730
}
1831

19-
func (d *deferredMarshaller) MarshallObject(ptr unsafe.Pointer, session *MarshallSession) error {
32+
func (d *deferredMarshaller) resolved() (marshaler, error) {
33+
<-d.ready // wait for resolve/fail
34+
if d.err != nil {
35+
return nil, d.err
36+
}
2037
if d.target == nil {
21-
return fmt.Errorf("marshaller not initialized")
38+
return nil, fmt.Errorf("marshaller not initialized")
2239
}
23-
return d.target.MarshallObject(ptr, session)
40+
return d.target, nil
2441
}
2542

26-
func (d *deferredMarshaller) UnmarshallObject(pointer unsafe.Pointer, decoder *gojay.Decoder, auxiliaryDecoder *gojay.Decoder, session *UnmarshalSession) error {
27-
if d.target == nil {
28-
return fmt.Errorf("marshaller not initialized")
43+
func (d *deferredMarshaller) MarshallObject(ptr unsafe.Pointer, s *MarshallSession) error {
44+
m, err := d.resolved()
45+
if err != nil {
46+
return err
47+
}
48+
return m.MarshallObject(ptr, s)
49+
}
50+
51+
func (d *deferredMarshaller) UnmarshallObject(p unsafe.Pointer, dec, aux *gojay.Decoder, s *UnmarshalSession) error {
52+
m, err := d.resolved()
53+
if err != nil {
54+
return err
2955
}
30-
return d.target.UnmarshallObject(pointer, decoder, auxiliaryDecoder, session)
56+
return m.UnmarshallObject(p, dec, aux, s)
3157
}

0 commit comments

Comments
 (0)