Skip to content

Commit 199d20b

Browse files
egonelbreFontinalis
authored andcommitted
Ensure that introspection types don't race during initialization. (#469)
* Ensure that introspection types don't race during initialization. * Ensure tests run on Go1.8+
1 parent bed865f commit 199d20b

File tree

3 files changed

+78
-0
lines changed

3 files changed

+78
-0
lines changed

definition.go

+7
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,13 @@ func NewObject(config ObjectConfig) *Object {
399399

400400
return objectType
401401
}
402+
403+
// ensureCache ensures that both fields and interfaces have been initialized properly,
404+
// to prevent races.
405+
func (gt *Object) ensureCache() {
406+
gt.Fields()
407+
gt.Interfaces()
408+
}
402409
func (gt *Object) AddFieldConfig(fieldName string, fieldConfig *Field) {
403410
if fieldName == "" || fieldConfig == nil {
404411
return

introspection.go

+7
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,13 @@ func init() {
611611
Type: TypeType,
612612
})
613613

614+
SchemaType.ensureCache()
615+
DirectiveType.ensureCache()
616+
TypeType.ensureCache()
617+
FieldType.ensureCache()
618+
InputValueType.ensureCache()
619+
EnumValueType.ensureCache()
620+
614621
// Note that these are FieldDefinition and not FieldConfig,
615622
// so the format for args is different.
616623
SchemaMetaFieldDef = &FieldDefinition{

race_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package graphql_test
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"testing"
9+
)
10+
11+
func TestRace(t *testing.T) {
12+
tempdir, err := ioutil.TempDir("", "race")
13+
if err != nil {
14+
t.Fatal(err)
15+
}
16+
defer os.RemoveAll(tempdir)
17+
18+
filename := filepath.Join(tempdir, "example.go")
19+
err = ioutil.WriteFile(filename, []byte(`
20+
package main
21+
22+
import (
23+
"runtime"
24+
"sync"
25+
26+
"github.com/graphql-go/graphql"
27+
)
28+
29+
func main() {
30+
var wg sync.WaitGroup
31+
wg.Add(2)
32+
for i := 0; i < 2; i++ {
33+
go func() {
34+
defer wg.Done()
35+
schema, _ := graphql.NewSchema(graphql.SchemaConfig{
36+
Query: graphql.NewObject(graphql.ObjectConfig{
37+
Name: "RootQuery",
38+
Fields: graphql.Fields{
39+
"hello": &graphql.Field{
40+
Type: graphql.String,
41+
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
42+
return "world", nil
43+
},
44+
},
45+
},
46+
}),
47+
})
48+
runtime.KeepAlive(schema)
49+
}()
50+
}
51+
52+
wg.Wait()
53+
}
54+
`), 0755)
55+
if err != nil {
56+
t.Fatal(err)
57+
}
58+
59+
result, err := exec.Command("go", "run", "-race", filename).CombinedOutput()
60+
if err != nil || len(result) != 0 {
61+
t.Log(string(result))
62+
t.Fatal(err)
63+
}
64+
}

0 commit comments

Comments
 (0)