Skip to content

Commit 4e3f4ae

Browse files
committed
fix(jsonutils): fixed data race with lexer
Signed-off-by: Frederic BIDON <[email protected]>
1 parent a7a1158 commit 4e3f4ae

File tree

6 files changed

+48
-19
lines changed

6 files changed

+48
-19
lines changed

.github/workflows/go-test.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ jobs:
102102

103103
steps:
104104
- uses: actions/checkout@v5
105-
#- name: Ensure cache is available
106-
# run: |
107-
# mkdir -p /home/runner/go/pkg/mod
108-
# mkdir -p /home/runner/.cache/go.build
109105
- uses: actions/setup-go@v6
110106
with:
111107
go-version: '${{ matrix.go_version }}'
@@ -119,6 +115,7 @@ jobs:
119115
# *.coverage.* pattern is automatically detected by codecov
120116
COVER_PROFILE: 'all_modules.coverage.${{ matrix.os }}.${{ matrix.go_version }}.out'
121117
run: |
118+
# when go1.25 becomes the oldstable, we may replace this bash with "go work test"
122119
declare -a ALL_MODULES
123120
BASH_MAJOR=$(echo $BASH_VERSION|cut -d'.' -f1)
124121
if [[ "${BASH_MAJOR}" -ge 4 ]] ; then

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fix data race https://github.com/go-openapi/swag/actions/runs/17989156861/job/51174860188

hack/tag_modules.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ echo "Tagging all modules in repo ${root##*/}..."
1818

1919
while read module_location ; do
2020
relative_location=${module_location#"$root"/}
21+
relative_location=${relative_location#"$root"}
2122
module_dir=${relative_location%"/go.mod"}
2223
base_tag="${module_dir#"./"}"
23-
if [[ "${base_tag}" == "." ]] ; then
24+
if [[ "${base_tag}" == "" ]] ; then
2425
module_tag="${tag}" # e.g. "v0.24.0"
2526
else
2627
module_tag="${base_tag}/${tag}" # e.g. "mangling/v0.24.0"

jsonutils/adapters/stdlib/json/lexer.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,23 +143,18 @@ func newLexer(data []byte) *jlexer {
143143
// current: undefToken,
144144
next: undefToken,
145145
}
146-
if data != nil {
147-
l.buf = &bytesReader{
148-
buf: data,
149-
}
150-
l.dec = stdjson.NewDecoder(l.buf) // unfortunately, cannot pool this
146+
l.buf = &bytesReader{
147+
buf: data,
151148
}
149+
l.dec = stdjson.NewDecoder(l.buf) // unfortunately, cannot pool this
152150

153151
return l
154152
}
155153

156154
func (l *jlexer) Reset() {
157-
l.dec = nil
158155
l.err = nil
159156
l.next = undefToken
160-
if l.buf != nil {
161-
l.buf.Reset()
162-
}
157+
// leave l.dec and l.buf alone, since they are replaced at every Borrow
163158
}
164159

165160
func (l *jlexer) Error() error {

jsonutils/adapters/stdlib/json/ordered_map_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ package json
1616

1717
import (
1818
stdjson "encoding/json"
19+
"fmt"
20+
"io"
21+
"sync"
1922
"testing"
2023

24+
"github.com/stretchr/testify/assert"
2125
"github.com/stretchr/testify/require"
2226

2327
fixtures "github.com/go-openapi/swag/jsonutils/fixtures_test"
@@ -127,3 +131,35 @@ func TestLexerErrors(t *testing.T) {
127131
})
128132
}
129133
}
134+
135+
func TestReproDataRace(t *testing.T) {
136+
t.Parallel()
137+
const parallelRoutines = 1000
138+
139+
// NOTE: with go1.25, use synctest.Test
140+
var wg sync.WaitGroup
141+
142+
for range parallelRoutines {
143+
wg.Add(1)
144+
go func() {
145+
defer func() {
146+
wg.Done()
147+
}()
148+
149+
toks := make([]token, 0, 4)
150+
buf := []byte(`{"test":"data"}`)
151+
l := poolOfLexers.Borrow(buf)
152+
153+
for tok := l.NextToken(); tok != eofToken; tok = l.NextToken() {
154+
toks = append(toks, tok)
155+
}
156+
assert.Len(t, toks, 4)
157+
fmt.Fprintf(io.Discard, "%d", len(toks))
158+
defer func() {
159+
poolOfLexers.Redeem(l)
160+
}()
161+
}()
162+
}
163+
164+
wg.Wait()
165+
}

jsonutils/adapters/stdlib/json/pool.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,18 @@ func (p *lexersPool) Borrow(data []byte) *jlexer {
6262
ptr := p.Get()
6363

6464
l := ptr.(*jlexer)
65-
l.Reset()
66-
6765
l.buf = poolOfReaders.Borrow(data)
6866
l.dec = json.NewDecoder(l.buf) // cannot pool, not exposed by the encoding/json API
67+
l.Reset()
6968

7069
return l
7170
}
7271

7372
func (p *lexersPool) Redeem(l *jlexer) {
7473
l.dec = nil
75-
if l.buf != nil {
76-
poolOfReaders.Redeem(l.buf)
77-
}
74+
discard := l.buf
75+
l.buf = nil
76+
poolOfReaders.Redeem(discard)
7877
p.Put(l)
7978
}
8079

0 commit comments

Comments
 (0)