Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add ProviderDisposeDelayedWaitingOnConcurrentRead test
  • Loading branch information
halter73 committed Dec 6, 2021
commit d26846a15688f40e97e6978b7bfc6b7b9f3642d9
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,12 @@ public async Task ProviderCanBlockLoadWaitingOnConcurrentRead()
{
using var mre = new ManualResetEventSlim(false);
var provider = new BlockLoadOnMREProvider(mre, timeout: TimeSpan.FromSeconds(30));
var source = new TestConfigurationSource(provider);

var config = new ConfigurationManager();
IConfigurationBuilder builder = config;

// builder.Add(source) calls provider.Load().
var loadTask = Task.Run(() => builder.Add(source));
var loadTask = Task.Run(() => builder.Add(new TestConfigurationSource(provider)));

await provider.LoadStartedTask;

Expand All @@ -198,6 +197,35 @@ public async Task ProviderCanBlockLoadWaitingOnConcurrentRead()
await loadTask;
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))]
public async Task ProviderDisposeDelayedWaitingOnConcurrentRead()
{
using var mre = new ManualResetEventSlim(false);
var provider = new BlockTryGetOnMREProvider(mre, timeout: TimeSpan.FromSeconds(30));

var config = new ConfigurationManager();
IConfigurationBuilder builder = config;

builder.Add(new TestConfigurationSource(provider));

// Reading configuration will block on provider.TryRead().
var readTask = Task.Run(() => config["key"]);

// Removing the source normally disposes the provider except when there provider is in use as is the case here.
builder.Sources.Clear();

Assert.False(provider.IsDisposed);

// Unblock provider.TryRead()
mre.Set();

// This will throw if provider.TryRead() timed out instead of unblocking gracefully after setting the MRE.
await readTask;

// The provider should be disposed when provider.TryRead() releases the last reference to the provider.
Assert.True(provider.IsDisposed);
}

// Moq heavily utilizes RefEmit, which does not work on most aot workloads
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
public void DisposesChangeTokenRegistrationsOnDispose()
Expand Down Expand Up @@ -1178,6 +1206,29 @@ public override void Load()
}
}

private class BlockTryGetOnMREProvider : ConfigurationProvider, IDisposable
{
private readonly ManualResetEventSlim _mre;
private readonly TimeSpan _timeout;

public BlockTryGetOnMREProvider(ManualResetEventSlim mre, TimeSpan timeout)
{
_mre = mre;
_timeout = timeout;
}

public bool IsDisposed { get; set; }

public override bool TryGet(string key, out string? value)
{
Assert.True(_mre.Wait(_timeout), "BlockTryGetOnMREProvider.TryGet() timed out.");
return base.TryGet(key, out value);
}

public void Dispose()
=> IsDisposed = true;
}

private class DisposableTestConfigurationProvider : ConfigurationProvider, IDisposable
{
public bool IsDisposed { get; set; }
Expand Down