Skip to content

Ensure that awaits do not continue on the captured context. #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion src/KubernetesClient/Kubernetes.WebSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ public partial class Kubernetes
{
// Copy the default (credential-related) request headers from the HttpClient to the WebSocket
HttpRequestMessage message = new HttpRequestMessage();
await this.Credentials.ProcessHttpRequestAsync(message, cancellationToken);
await this.Credentials.ProcessHttpRequestAsync(message, cancellationToken).ConfigureAwait(false);

foreach (var _header in message.Headers)
{
Expand Down
10 changes: 5 additions & 5 deletions src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public static async Task<KubernetesClientConfiguration> BuildConfigFromConfigFil
throw new NullReferenceException(nameof(kubeconfig));
}

var k8SConfig = await LoadKubeConfigAsync(kubeconfig, useRelativePaths);
var k8SConfig = await LoadKubeConfigAsync(kubeconfig, useRelativePaths).ConfigureAwait(false);
var k8SConfiguration = GetKubernetesClientConfiguration(currentContext, masterUrl, k8SConfig);

return k8SConfiguration;
Expand Down Expand Up @@ -139,7 +139,7 @@ public static async Task<KubernetesClientConfiguration> BuildConfigFromConfigFil

kubeconfig.Position = 0;

var k8SConfig = await Yaml.LoadFromStreamAsync<K8SConfiguration>(kubeconfig);
var k8SConfig = await Yaml.LoadFromStreamAsync<K8SConfiguration>(kubeconfig).ConfigureAwait(false);
var k8SConfiguration = GetKubernetesClientConfiguration(currentContext, masterUrl, k8SConfig);

return k8SConfiguration;
Expand Down Expand Up @@ -486,7 +486,7 @@ public static async Task<K8SConfiguration> LoadKubeConfigAsync(string kubeconfig
{
var fileInfo = new FileInfo(kubeconfigPath ?? KubeConfigDefaultLocation);

return await LoadKubeConfigAsync(fileInfo, useRelativePaths);
return await LoadKubeConfigAsync(fileInfo, useRelativePaths).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -517,7 +517,7 @@ public static async Task<K8SConfiguration> LoadKubeConfigAsync(FileInfo kubeconf

using (var stream = kubeconfig.OpenRead())
{
var config = await Yaml.LoadFromStreamAsync<K8SConfiguration>(stream);
var config = await Yaml.LoadFromStreamAsync<K8SConfiguration>(stream).ConfigureAwait(false);

if (useRelativePaths)
{
Expand Down Expand Up @@ -547,7 +547,7 @@ public static K8SConfiguration LoadKubeConfig(FileInfo kubeconfig, bool useRelat
/// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns>
public static async Task<K8SConfiguration> LoadKubeConfigAsync(Stream kubeconfigStream)
{
return await Yaml.LoadFromStreamAsync<K8SConfiguration>(kubeconfigStream);
return await Yaml.LoadFromStreamAsync<K8SConfiguration>(kubeconfigStream).ConfigureAwait(false);
}

/// <summary>
Expand Down
5 changes: 1 addition & 4 deletions src/KubernetesClient/StreamDemuxer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public StreamDemuxer(WebSocket webSocket, StreamType streamType = StreamType.Rem
/// </summary>
public void Start()
{
this.runLoop = this.RunLoop(this.cts.Token);
this.runLoop = Task.Run(async () => await this.RunLoop(this.cts.Token));
}

/// <inheritdoc/>
Expand Down Expand Up @@ -193,9 +193,6 @@ public Stream GetStream(byte? inputIndex, byte? outputIndex)

protected async Task RunLoop(CancellationToken cancellationToken)
{
// This is a background task. Immediately yield to the caller.
await Task.Yield();

// Get a 1KB buffer
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024 * 1024);
// This maps remembers bytes skipped for each stream.
Expand Down
9 changes: 3 additions & 6 deletions src/KubernetesClient/Watcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public Watcher(Func<Task<StreamReader>> streamReaderCreator, Action<WatchEventTy
OnClosed += onClosed;

_cts = new CancellationTokenSource();
_watcherLoop = this.WatcherLoop(_cts.Token);
_watcherLoop = Task.Run(async () => await this.WatcherLoop(_cts.Token));
}

/// <inheritdoc/>
Expand Down Expand Up @@ -91,14 +91,11 @@ public class WatchEvent

private async Task WatcherLoop(CancellationToken cancellationToken)
{
// Make sure we run async
await Task.Yield();

try
{
Watching = true;
string line;
_streamReader = await _streamReaderCreator();
_streamReader = await _streamReaderCreator().ConfigureAwait(false);

// ReadLineAsync will return null when we've reached the end of the stream.
while ((line = await _streamReader.ReadLineAsync().ConfigureAwait(false)) != null)
Expand Down Expand Up @@ -164,7 +161,7 @@ public static Watcher<T> Watch<T, L>(this Task<HttpOperationResponse<L>> respons
Action onClosed = null)
{
return new Watcher<T>(async () => {
var response = await responseTask;
var response = await responseTask.ConfigureAwait(false);

if (!(response.Response.Content is WatcherDelegatingHandler.LineSeparatedHttpContent content))
{
Expand Down
12 changes: 6 additions & 6 deletions src/KubernetesClient/WatcherDelegatingHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class WatcherDelegatingHandler : DelegatingHandler
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var originResponse = await base.SendAsync(request, cancellationToken);
var originResponse = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);

if (originResponse.IsSuccessStatusCode)
{
Expand Down Expand Up @@ -47,18 +47,18 @@ public LineSeparatedHttpContent(HttpContent originContent)

protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
_originStream = await _originContent.ReadAsStreamAsync();
_originStream = await _originContent.ReadAsStreamAsync().ConfigureAwait(false);

StreamReader = new PeekableStreamReader(_originStream);

var firstLine = await StreamReader.PeekLineAsync();
var firstLine = await StreamReader.PeekLineAsync().ConfigureAwait(false);

var writer = new StreamWriter(stream);

// using (writer) // leave open
{
await writer.WriteAsync(firstLine);
await writer.FlushAsync();
await writer.WriteAsync(firstLine).ConfigureAwait(false);
await writer.FlushAsync().ConfigureAwait(false);
}
}

Expand Down Expand Up @@ -94,7 +94,7 @@ public override Task<string> ReadLineAsync()
}
public async Task<string> PeekLineAsync()
{
var line = await ReadLineAsync();
var line = await ReadLineAsync().ConfigureAwait(false);
_buffer.Enqueue(line);
return line;
}
Expand Down
6 changes: 3 additions & 3 deletions src/KubernetesClient/Yaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class Yaml {
/// </param>
public static async Task<List<object>> LoadAllFromStreamAsync(Stream stream, Dictionary<String, Type> typeMap) {
var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
var content = await reader.ReadToEndAsync().ConfigureAwait(false);
return LoadAllFromString(content, typeMap);
}

Expand Down Expand Up @@ -95,13 +95,13 @@ public static List<object> LoadAllFromString(String content, Dictionary<String,

public static async Task<T> LoadFromStreamAsync<T>(Stream stream) {
var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
var content = await reader.ReadToEndAsync().ConfigureAwait(false);
return LoadFromString<T>(content);
}

public static async Task<T> LoadFromFileAsync<T> (string file) {
using (FileStream fs = File.OpenRead(file)) {
return await LoadFromStreamAsync<T>(fs);
return await LoadFromStreamAsync<T>(fs).ConfigureAwait(false);
}
}

Expand Down
1 change: 1 addition & 0 deletions tests/KubernetesClient.Tests/KubernetesClient.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="Xunit.StaFact" Version="0.3.18" />
<PackageReference Include="Moq" Version="4.13.1" />

<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
Expand Down
12 changes: 12 additions & 0 deletions tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.IO;
using System.Linq;
using System.Threading;
using k8s.Exceptions;
using k8s.KubeConfigModels;
using Xunit;
Expand Down Expand Up @@ -406,6 +407,17 @@ public void LoadKubeConfigStream()
AssertConfigEqual(expectedCfg, cfg);
}

/// <summary>
/// Ensures Kube config file can be loaded from within a non-default <see cref="SynchronizationContext"/>.
/// The use of <see cref="UIFactAttribute"/> ensures the test is run from within a UI-like <see cref="SynchronizationContext"/>.
/// </summary>
[UIFact]
public void BuildConfigFromConfigFileInfoOnNonDefaultSynchronizationContext()
{
var fi = new FileInfo("assets/kubeconfig.yml");
KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, "federal-context", useRelativePaths: false);
}

private void AssertConfigEqual(K8SConfiguration expected, K8SConfiguration actual)
{
Assert.Equal(expected.ApiVersion, actual.ApiVersion);
Expand Down