Skip to content

Commit

Permalink
Atomically swap config data (dotnet/extensions#1202)
Browse files Browse the repository at this point in the history
Swap the configuration providers' data atomically, rather than directly changing the property, so that any enumeration of the dictionary running during the reload operation does not throw an InvalidOperationException due to the collection being modified.
Relates to dotnet/extensions#1189.\n\nCommit migrated from dotnet/extensions@192abfd
  • Loading branch information
martincostello authored and Tratcher committed Mar 4, 2019
1 parent aff09d1 commit 09b9a49
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ private static string TrimNewLine(string value)
/// </summary>
public override void Load()
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

if (Source.FileProvider == null)
{
if (Source.Optional)
{
Data = data;
return;
}

Expand All @@ -61,10 +62,12 @@ public override void Load()
{
if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name))
{
Data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd()));
data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd()));
}
}
}

Data = data;
}

private string GetDirectoryName()
Expand Down
43 changes: 43 additions & 0 deletions src/Configuration.KeyPerFile/test/KeyPerFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using Xunit;
Expand Down Expand Up @@ -179,6 +181,47 @@ public void CanUnIgnoreDefaultFiles()
Assert.Equal("SecretValue1", config["ignore.Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}

[Fact]
public void BindingDoesNotThrowIfReloadedDuringBinding()
{
var testFileProvider = new TestFileProvider(
new TestFile("Number", "-2"),
new TestFile("Text", "Foo"));

var config = new ConfigurationBuilder()
.AddKeyPerFile(o => o.FileProvider = testFileProvider)
.Build();

MyOptions options = null;

using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(250)))
{
void ReloadLoop()
{
while (!cts.IsCancellationRequested)
{
config.Reload();
}
}

_ = Task.Run(ReloadLoop);

while (!cts.IsCancellationRequested)
{
options = config.Get<MyOptions>();
}
}

Assert.Equal(-2, options.Number);
Assert.Equal("Foo", options.Text);
}

private sealed class MyOptions
{
public int Number { get; set; }
public string Text { get; set; }
}
}

class TestFileProvider : IFileProvider
Expand Down

0 comments on commit 09b9a49

Please sign in to comment.