Skip to content

Commit 05e8213

Browse files
committed
Fixed options monitor race condition. (#7833)
1 parent 23cf377 commit 05e8213

File tree

1 file changed

+36
-27
lines changed

1 file changed

+36
-27
lines changed

src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorOptionsMonitor.cs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ internal sealed class DefaultRequestExecutorOptionsMonitor
99
private readonly SemaphoreSlim _semaphore = new(1, 1);
1010
private readonly IOptionsMonitor<RequestExecutorSetup> _optionsMonitor;
1111
private readonly IRequestExecutorOptionsProvider[] _optionsProviders;
12-
private readonly Dictionary<string, List<IConfigureRequestExecutorSetup>> _configs =
13-
new();
12+
private readonly Dictionary<string, List<IConfigureRequestExecutorSetup>> _configs = new();
1413
private readonly List<IDisposable> _disposables = [];
1514
private readonly List<Action<string>> _listeners = [];
1615
private bool _initialized;
@@ -28,7 +27,7 @@ public async ValueTask<RequestExecutorSetup> GetAsync(
2827
string schemaName,
2928
CancellationToken cancellationToken = default)
3029
{
31-
await InitializeAsync(cancellationToken).ConfigureAwait(false);
30+
await TryInitializeAsync(cancellationToken).ConfigureAwait(false);
3231

3332
var options = new RequestExecutorSetup();
3433
_optionsMonitor.Get(schemaName).CopyTo(options);
@@ -44,47 +43,57 @@ public async ValueTask<RequestExecutorSetup> GetAsync(
4443
return options;
4544
}
4645

47-
private async ValueTask InitializeAsync(CancellationToken cancellationToken)
46+
private async ValueTask TryInitializeAsync(CancellationToken cancellationToken)
4847
{
4948
if (!_initialized)
5049
{
5150
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
5251

53-
if (!_initialized)
52+
try
5453
{
55-
_configs.Clear();
54+
await TryInitializeUnsafeAsync(cancellationToken).ConfigureAwait(false);
55+
}
56+
finally
57+
{
58+
_semaphore.Release();
59+
}
60+
}
61+
}
5662

57-
foreach (var provider in _optionsProviders)
58-
{
59-
_disposables.Add(provider.OnChange(OnChange));
63+
private async ValueTask TryInitializeUnsafeAsync(CancellationToken cancellationToken)
64+
{
65+
if (!_initialized)
66+
{
67+
_configs.Clear();
68+
69+
foreach (var provider in _optionsProviders)
70+
{
71+
_disposables.Add(provider.OnChange(OnChange));
6072

61-
var allConfigurations =
62-
await provider.GetOptionsAsync(cancellationToken)
63-
.ConfigureAwait(false);
73+
var allConfigurations =
74+
await provider.GetOptionsAsync(cancellationToken)
75+
.ConfigureAwait(false);
6476

65-
foreach (var configuration in allConfigurations)
77+
foreach (var configuration in allConfigurations)
78+
{
79+
if (!_configs.TryGetValue(
80+
configuration.SchemaName,
81+
out var configurations))
6682
{
67-
if (!_configs.TryGetValue(
68-
configuration.SchemaName,
69-
out var configurations))
70-
{
71-
configurations = [];
72-
_configs.Add(configuration.SchemaName, configurations);
73-
}
74-
75-
configurations.Add(configuration);
83+
configurations = [];
84+
_configs.Add(configuration.SchemaName, configurations);
7685
}
77-
}
7886

79-
_initialized = true;
87+
configurations.Add(configuration);
88+
}
8089
}
8190

82-
_semaphore.Release();
91+
_initialized = true;
8392
}
8493
}
8594

86-
public IDisposable OnChange(Action<string> listener) =>
87-
new Session(this, listener);
95+
public IDisposable OnChange(Action<string> listener)
96+
=> new Session(this, listener);
8897

8998
private void OnChange(IConfigureRequestExecutorSetup changes)
9099
{

0 commit comments

Comments
 (0)