Skip to content

Commit 40cac0c

Browse files
committed
schema: fix race condition
Use an additional atomic.Pointer
1 parent 193e7fe commit 40cac0c

File tree

1 file changed

+27
-20
lines changed

1 file changed

+27
-20
lines changed

schema/elements.go

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package schema
1818

1919
import (
2020
"sync"
21+
"sync/atomic"
2122
)
2223

2324
// Schema is a list of named types.
@@ -28,7 +29,7 @@ type Schema struct {
2829
Types []TypeDef `yaml:"types,omitempty"`
2930

3031
once sync.Once
31-
m map[string]TypeDef
32+
m atomic.Pointer[map[string]TypeDef]
3233

3334
lock sync.Mutex
3435
// Cached results of resolving type references to atoms. Only stores
@@ -144,26 +145,28 @@ type Map struct {
144145
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
145146

146147
once sync.Once
147-
m map[string]StructField
148+
m atomic.Pointer[map[string]StructField]
148149
}
149150

150151
// FindField is a convenience function that returns the referenced StructField,
151152
// if it exists, or (nil, false) if it doesn't.
152153
func (m *Map) FindField(name string) (StructField, bool) {
153154
m.once.Do(func() {
154-
m.m = make(map[string]StructField, len(m.Fields))
155+
mm := make(map[string]StructField, len(m.Fields))
155156
for _, field := range m.Fields {
156-
m.m[field.Name] = field
157+
mm[field.Name] = field
157158
}
159+
m.m.Store(&mm)
158160
})
159-
sf, ok := m.m[name]
161+
sf, ok := (*m.m.Load())[name]
160162
return sf, ok
161163
}
162164

163-
// CopyInto this instance of Map into the other
164-
// If other is nil this method does nothing.
165-
// If other is already initialized, overwrites it with this instance
166-
// Warning: Not thread safe
165+
// CopyInto clones this instance of Map into dst
166+
//
167+
// If dst is nil this method does nothing.
168+
// If dst is already initialized, overwrites it with this instance.
169+
// Warning: Not thread safe. Only use dst after this function returns.
167170
func (m *Map) CopyInto(dst *Map) {
168171
if dst == nil {
169172
return
@@ -175,12 +178,13 @@ func (m *Map) CopyInto(dst *Map) {
175178
dst.Unions = m.Unions
176179
dst.ElementRelationship = m.ElementRelationship
177180

178-
if m.m != nil {
181+
mm := m.m.Load()
182+
if mm != nil {
179183
// If cache is non-nil then the once token had been consumed.
180184
// Must reset token and use it again to ensure same semantics.
181185
dst.once = sync.Once{}
182186
dst.once.Do(func() {
183-
dst.m = m.m
187+
dst.m.Store(mm)
184188
})
185189
}
186190
}
@@ -274,12 +278,13 @@ type List struct {
274278
// if it exists, or (nil, false) if it doesn't.
275279
func (s *Schema) FindNamedType(name string) (TypeDef, bool) {
276280
s.once.Do(func() {
277-
s.m = make(map[string]TypeDef, len(s.Types))
281+
sm := make(map[string]TypeDef, len(s.Types))
278282
for _, t := range s.Types {
279-
s.m[t.Name] = t
283+
sm[t.Name] = t
280284
}
285+
s.m.Store(&sm)
281286
})
282-
t, ok := s.m[name]
287+
t, ok := (*s.m.Load())[name]
283288
return t, ok
284289
}
285290

@@ -352,10 +357,11 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
352357
return result, true
353358
}
354359

355-
// Clones this instance of Schema into the other
356-
// If other is nil this method does nothing.
357-
// If other is already initialized, overwrites it with this instance
358-
// Warning: Not thread safe
360+
// CopyInto clones this instance of Schema into dst
361+
//
362+
// If dst is nil this method does nothing.
363+
// If dst is already initialized, overwrites it with this instance.
364+
// Warning: Not thread safe. Only use dst after this function returns.
359365
func (s *Schema) CopyInto(dst *Schema) {
360366
if dst == nil {
361367
return
@@ -364,12 +370,13 @@ func (s *Schema) CopyInto(dst *Schema) {
364370
// Schema type is considered immutable so sharing references
365371
dst.Types = s.Types
366372

367-
if s.m != nil {
373+
sm := s.m.Load()
374+
if sm != nil {
368375
// If cache is non-nil then the once token had been consumed.
369376
// Must reset token and use it again to ensure same semantics.
370377
dst.once = sync.Once{}
371378
dst.once.Do(func() {
372-
dst.m = s.m
379+
dst.m.Store(sm)
373380
})
374381
}
375382
}

0 commit comments

Comments
 (0)