Skip to content

Commit 63ab9f3

Browse files
committed
Fixed initialization race-condition in generated code. (#7834)
* Fixed initialization race-condition in generated code. * Updated Snapshots
1 parent 4d6e37d commit 63ab9f3

10 files changed

+318
-191
lines changed

src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public string WriteBeginClass(string typeName)
3737
_writer.WriteIndentedLine("internal static class {0}", typeName);
3838
_writer.WriteIndentedLine("{");
3939
_writer.IncreaseIndent();
40+
_writer.WriteIndentedLine("private static readonly object _sync = new object();");
4041
_writer.WriteIndentedLine("private static bool _bindingsInitialized;");
4142
return typeName;
4243
}
@@ -94,15 +95,18 @@ public void AddParameterInitializer(IEnumerable<Resolver> resolvers, ILocalTypeL
9495

9596
if (first)
9697
{
97-
_writer.WriteIndentedLine("if (_bindingsInitialized)");
98+
_writer.WriteIndentedLine("if (!_bindingsInitialized)");
9899
_writer.WriteIndentedLine("{");
99-
using (_writer.IncreaseIndent())
100-
{
101-
_writer.WriteIndentedLine("return;");
102-
}
100+
_writer.IncreaseIndent();
101+
102+
_writer.WriteIndentedLine("lock (_sync)");
103+
_writer.WriteIndentedLine("{");
104+
_writer.IncreaseIndent();
105+
106+
_writer.WriteIndentedLine("if (!_bindingsInitialized)");
107+
_writer.WriteIndentedLine("{");
108+
_writer.IncreaseIndent();
103109

104-
_writer.WriteIndentedLine("}");
105-
_writer.WriteIndentedLine("_bindingsInitialized = true;");
106110
_writer.WriteLine();
107111
_writer.WriteIndentedLine(
108112
"const global::{0} bindingFlags =",
@@ -120,6 +124,8 @@ public void AddParameterInitializer(IEnumerable<Resolver> resolvers, ILocalTypeL
120124
_writer.WriteIndentedLine("var type = typeof({0});", method.ContainingType.ToFullyQualified());
121125
_writer.WriteIndentedLine("global::System.Reflection.MethodInfo resolver = default!;");
122126
_writer.WriteIndentedLine("global::System.Reflection.ParameterInfo[] parameters = default!;");
127+
128+
_writer.WriteIndentedLine("_bindingsInitialized = true;");
123129
first = false;
124130
}
125131

@@ -182,8 +188,8 @@ public void AddParameterInitializer(IEnumerable<Resolver> resolvers, ILocalTypeL
182188
using (_writer.WriteForEach("binding", $"_args_{resolver.TypeName}_{resolver.Member.Name}"))
183189
{
184190
using (_writer.WriteIfClause(
185-
"binding.Kind == global::{0}.Argument",
186-
WellKnownTypes.ArgumentKind))
191+
"binding.Kind == global::{0}.Argument",
192+
WellKnownTypes.ArgumentKind))
187193
{
188194
_writer.WriteIndentedLine("argumentCount++;");
189195
}
@@ -204,8 +210,8 @@ public void AddParameterInitializer(IEnumerable<Resolver> resolvers, ILocalTypeL
204210
using (_writer.IncreaseIndent())
205211
{
206212
_writer.WriteIndentedLine(
207-
".SetMessage(\"The node resolver `{0}.{1}` mustn't have more than one " +
208-
"argument. Node resolvers can only have a single argument called `id`.\")",
213+
".SetMessage(\"The node resolver `{0}.{1}` mustn't have more than one "
214+
+ "argument. Node resolvers can only have a single argument called `id`.\")",
209215
resolver.Member.ContainingType.ToDisplayString(),
210216
resolver.Member.Name);
211217
_writer.WriteIndentedLine(".Build());");
@@ -214,6 +220,16 @@ public void AddParameterInitializer(IEnumerable<Resolver> resolvers, ILocalTypeL
214220
}
215221
}
216222
}
223+
224+
if (!first)
225+
{
226+
_writer.DecreaseIndent();
227+
_writer.WriteIndentedLine("}");
228+
_writer.DecreaseIndent();
229+
_writer.WriteIndentedLine("}");
230+
_writer.DecreaseIndent();
231+
_writer.WriteIndentedLine("}");
232+
}
217233
}
218234

219235
_writer.WriteIndentedLine("}");
@@ -224,8 +240,7 @@ private static string ToFullyQualifiedString(
224240
IMethodSymbol resolverMethod,
225241
ILocalTypeLookup typeLookup)
226242
{
227-
if (type.TypeKind is TypeKind.Error &&
228-
typeLookup.TryGetTypeName(type, resolverMethod, out var typeDisplayName))
243+
if (type.TypeKind is TypeKind.Error && typeLookup.TryGetTypeName(type, resolverMethod, out var typeDisplayName))
229244
{
230245
return typeDisplayName;
231246
}
@@ -269,9 +284,9 @@ private void AddStaticStandardResolver(
269284
ILocalTypeLookup typeLookup)
270285
{
271286
using (_writer.WriteMethod(
272-
"public static",
273-
returnType: WellKnownTypes.FieldResolverDelegates,
274-
methodName: $"{resolver.TypeName}_{resolver.Member.Name}"))
287+
"public static",
288+
returnType: WellKnownTypes.FieldResolverDelegates,
289+
methodName: $"{resolver.TypeName}_{resolver.Member.Name}"))
275290
{
276291
using (_writer.WriteIfClause(condition: "!_bindingsInitialized"))
277292
{
@@ -341,9 +356,9 @@ private void AddStaticStandardResolver(
341356
private void AddStaticPureResolver(Resolver resolver, IMethodSymbol resolverMethod, ILocalTypeLookup typeLookup)
342357
{
343358
using (_writer.WriteMethod(
344-
"public static",
345-
returnType: WellKnownTypes.FieldResolverDelegates,
346-
methodName: $"{resolver.TypeName}_{resolver.Member.Name}"))
359+
"public static",
360+
returnType: WellKnownTypes.FieldResolverDelegates,
361+
methodName: $"{resolver.TypeName}_{resolver.Member.Name}"))
347362
{
348363
using (_writer.WriteIfClause(condition: "!_bindingsInitialized"))
349364
{
@@ -436,9 +451,9 @@ private void AddStaticPureResolver(Resolver resolver, IMethodSymbol resolverMeth
436451
private void AddStaticPropertyResolver(Resolver resolver)
437452
{
438453
using (_writer.WriteMethod(
439-
"public static",
440-
returnType: WellKnownTypes.FieldResolverDelegates,
441-
methodName: $"{resolver.TypeName}_{resolver.Member.Name}"))
454+
"public static",
455+
returnType: WellKnownTypes.FieldResolverDelegates,
456+
methodName: $"{resolver.TypeName}_{resolver.Member.Name}"))
442457
{
443458
using (_writer.WriteIfClause(condition: "!_bindingsInitialized"))
444459
{
@@ -489,13 +504,13 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho
489504
{
490505
var parameter = resolver.Parameters[i];
491506

492-
if(resolver.IsNodeResolver
493-
&& parameter.Kind is ResolverParameterKind.Argument or ResolverParameterKind.Unknown
494-
&& (parameter.Name == "id" || parameter.Key == "id"))
507+
if (resolver.IsNodeResolver
508+
&& parameter.Kind is ResolverParameterKind.Argument or ResolverParameterKind.Unknown
509+
&& (parameter.Name == "id" || parameter.Key == "id"))
495510
{
496511
_writer.WriteIndentedLine(
497-
"var args{0} = context.GetLocalState<{1}>(" +
498-
"global::HotChocolate.WellKnownContextData.InternalId);",
512+
"var args{0} = context.GetLocalState<{1}>("
513+
+ "global::HotChocolate.WellKnownContextData.InternalId);",
499514
i,
500515
parameter.Type.ToFullyQualified());
501516
continue;
@@ -524,8 +539,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho
524539
break;
525540
case ResolverParameterKind.EventMessage:
526541
_writer.WriteIndentedLine(
527-
"var args{0} = context.GetScopedState<{1}>(" +
528-
"global::HotChocolate.WellKnownContextData.EventMessage);",
542+
"var args{0} = context.GetScopedState<{1}>("
543+
+ "global::HotChocolate.WellKnownContextData.EventMessage);",
529544
i,
530545
parameter.Type.ToFullyQualified());
531546
break;
@@ -593,8 +608,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho
593608
}
594609
case ResolverParameterKind.SetGlobalState:
595610
_writer.WriteIndentedLine(
596-
"var args{0} = new HotChocolate.SetState<{1}>(" +
597-
"value => context.SetGlobalState(\"{2}\", value));",
611+
"var args{0} = new HotChocolate.SetState<{1}>("
612+
+ "value => context.SetGlobalState(\"{2}\", value));",
598613
i,
599614
((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(),
600615
parameter.Key);
@@ -633,8 +648,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho
633648
}
634649
case ResolverParameterKind.SetScopedState:
635650
_writer.WriteIndentedLine(
636-
"var args{0} = new HotChocolate.SetState<{1}>(" +
637-
"value => context.SetScopedState(\"{2}\", value));",
651+
"var args{0} = new HotChocolate.SetState<{1}>("
652+
+ "value => context.SetScopedState(\"{2}\", value));",
638653
i,
639654
((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(),
640655
parameter.Key);
@@ -673,8 +688,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho
673688
}
674689
case ResolverParameterKind.SetLocalState:
675690
_writer.WriteIndentedLine(
676-
"var args{0} = new HotChocolate.SetState<{1}>(" +
677-
"value => context.SetLocalState(\"{2}\", value));",
691+
"var args{0} = new HotChocolate.SetState<{1}>("
692+
+ "value => context.SetLocalState(\"{2}\", value));",
678693
i,
679694
((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(),
680695
parameter.Key);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// <auto-generated/>
2+
3+
#nullable enable
4+
#pragma warning disable
5+
6+
using System;
7+
using System.Runtime.CompilerServices;
8+
using HotChocolate;
9+
using HotChocolate.Types;
10+
using HotChocolate.Execution.Configuration;
11+
using HotChocolate.Internal;
12+
13+
namespace HotChocolate.Types
14+
{
15+
internal static class EntityInterfaceResolvers2
16+
{
17+
private static readonly object _sync = new object();
18+
private static bool _bindingsInitialized;
19+
private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_EntityInterface_IdString = new global::HotChocolate.Internal.IParameterBinding[1];
20+
21+
public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver)
22+
{
23+
if (!_bindingsInitialized)
24+
{
25+
lock (_sync)
26+
{
27+
if (!_bindingsInitialized)
28+
{
29+
const global::System.Reflection.BindingFlags bindingFlags =
30+
global::System.Reflection.BindingFlags.Public
31+
| global::System.Reflection.BindingFlags.NonPublic
32+
| global::System.Reflection.BindingFlags.Static;
33+
34+
var type = typeof(global::HotChocolate.Types.EntityInterface);
35+
global::System.Reflection.MethodInfo resolver = default!;
36+
global::System.Reflection.ParameterInfo[] parameters = default!;
37+
38+
resolver = type.GetMethod(
39+
"IdString",
40+
bindingFlags,
41+
new global::System.Type[] { typeof(global::HotChocolate.Types.IEntity) })!;
42+
parameters = resolver.GetParameters();
43+
_args_EntityInterface_IdString[0] = bindingResolver.GetBinding(parameters[0]);
44+
45+
_bindingsInitialized = true;
46+
}
47+
}
48+
}
49+
}
50+
51+
public static HotChocolate.Resolvers.FieldResolverDelegates EntityInterface_IdString()
52+
{
53+
if(!_bindingsInitialized)
54+
{
55+
throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created.");
56+
}
57+
return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: EntityInterface_IdString_Resolver);
58+
}
59+
60+
private static global::System.Object? EntityInterface_IdString_Resolver(global::HotChocolate.Resolvers.IResolverContext context)
61+
{
62+
var args0 = context.Parent<global::HotChocolate.Types.IEntity>();
63+
var result = global::HotChocolate.Types.EntityInterface.IdString(args0);
64+
return result;
65+
}
66+
}
67+
}
68+

src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,43 @@ namespace TestNamespace
1919
{
2020
internal static class BookNodeResolvers
2121
{
22+
private static readonly object _sync = new object();
2223
private static bool _bindingsInitialized;
2324
private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_BookNode_GetAuthorAsync = new global::HotChocolate.Internal.IParameterBinding[2];
2425

2526
public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver)
2627
{
27-
if (_bindingsInitialized)
28+
if (!_bindingsInitialized)
2829
{
29-
return;
30-
}
31-
_bindingsInitialized = true;
32-
33-
const global::System.Reflection.BindingFlags bindingFlags =
34-
global::System.Reflection.BindingFlags.Public
35-
| global::System.Reflection.BindingFlags.NonPublic
36-
| global::System.Reflection.BindingFlags.Static;
37-
38-
var type = typeof(global::TestNamespace.BookNode);
39-
global::System.Reflection.MethodInfo resolver = default!;
40-
global::System.Reflection.ParameterInfo[] parameters = default!;
41-
42-
resolver = type.GetMethod(
43-
"GetAuthorAsync",
44-
bindingFlags,
45-
new global::System.Type[]
30+
lock (_sync)
4631
{
47-
typeof(global::TestNamespace.Book),
48-
typeof(global::System.Threading.CancellationToken)
49-
})!;
50-
parameters = resolver.GetParameters();
51-
_args_BookNode_GetAuthorAsync[0] = bindingResolver.GetBinding(parameters[0]);
52-
_args_BookNode_GetAuthorAsync[1] = bindingResolver.GetBinding(parameters[1]);
32+
if (!_bindingsInitialized)
33+
{
34+
35+
const global::System.Reflection.BindingFlags bindingFlags =
36+
global::System.Reflection.BindingFlags.Public
37+
| global::System.Reflection.BindingFlags.NonPublic
38+
| global::System.Reflection.BindingFlags.Static;
39+
40+
var type = typeof(global::TestNamespace.BookNode);
41+
global::System.Reflection.MethodInfo resolver = default!;
42+
global::System.Reflection.ParameterInfo[] parameters = default!;
43+
_bindingsInitialized = true;
44+
45+
resolver = type.GetMethod(
46+
"GetAuthorAsync",
47+
bindingFlags,
48+
new global::System.Type[]
49+
{
50+
typeof(global::TestNamespace.Book),
51+
typeof(global::System.Threading.CancellationToken)
52+
})!;
53+
parameters = resolver.GetParameters();
54+
_args_BookNode_GetAuthorAsync[0] = bindingResolver.GetBinding(parameters[0]);
55+
_args_BookNode_GetAuthorAsync[1] = bindingResolver.GetBinding(parameters[1]);
56+
}
57+
}
58+
}
5359
}
5460

5561
public static HotChocolate.Resolvers.FieldResolverDelegates BookNode_GetAuthorAsync()

src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,41 @@ namespace TestNamespace
1919
{
2020
internal static class TestTypeResolvers
2121
{
22+
private static readonly object _sync = new object();
2223
private static bool _bindingsInitialized;
2324
private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1];
2425

2526
public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver)
2627
{
27-
if (_bindingsInitialized)
28+
if (!_bindingsInitialized)
2829
{
29-
return;
30-
}
31-
_bindingsInitialized = true;
32-
33-
const global::System.Reflection.BindingFlags bindingFlags =
34-
global::System.Reflection.BindingFlags.Public
35-
| global::System.Reflection.BindingFlags.NonPublic
36-
| global::System.Reflection.BindingFlags.Static;
37-
38-
var type = typeof(global::TestNamespace.TestType);
39-
global::System.Reflection.MethodInfo resolver = default!;
40-
global::System.Reflection.ParameterInfo[] parameters = default!;
41-
42-
resolver = type.GetMethod(
43-
"GetTest",
44-
bindingFlags,
45-
new global::System.Type[]
30+
lock (_sync)
4631
{
47-
typeof(int)
48-
})!;
49-
parameters = resolver.GetParameters();
50-
_args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]);
32+
if (!_bindingsInitialized)
33+
{
34+
35+
const global::System.Reflection.BindingFlags bindingFlags =
36+
global::System.Reflection.BindingFlags.Public
37+
| global::System.Reflection.BindingFlags.NonPublic
38+
| global::System.Reflection.BindingFlags.Static;
39+
40+
var type = typeof(global::TestNamespace.TestType);
41+
global::System.Reflection.MethodInfo resolver = default!;
42+
global::System.Reflection.ParameterInfo[] parameters = default!;
43+
_bindingsInitialized = true;
44+
45+
resolver = type.GetMethod(
46+
"GetTest",
47+
bindingFlags,
48+
new global::System.Type[]
49+
{
50+
typeof(int)
51+
})!;
52+
parameters = resolver.GetParameters();
53+
_args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]);
54+
}
55+
}
56+
}
5157
}
5258

5359
public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest()

0 commit comments

Comments
 (0)