|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
namespace Roslyn.Utilities;
[SuppressMessage("ApiDesign", "CA1068", Justification = "Matching TPL Signatures")]
internal static partial class TaskExtensions
{
public static T WaitAndGetResult<T>(this Task<T> task, CancellationToken cancellationToken)
{
#if DEBUG
if (Thread.CurrentThread.IsThreadPoolThread)
{
// If you hit this when running tests then your code is in error. WaitAndGetResult
// should only be called from a foreground thread. There are a few ways you may
// want to fix this.
//
// First, if you're actually calling this directly *in test code* then you could
// either:
//
// 1) Mark the test with [WpfFact]. This is not preferred, and should only be
// when testing an actual UI feature (like command handlers).
// 2) Make the test actually async (preferred).
//
// If you are calling WaitAndGetResult from product code, then that code must
// be a foreground thread (i.e. a command handler). It cannot be from a threadpool
// thread *ever*.
throw new InvalidOperationException($"{nameof(WaitAndGetResult)} cannot be called from a thread pool thread.");
}
#endif
return WaitAndGetResult_CanCallOnBackground(task, cancellationToken);
}
// Only call this *extremely* special situations. This will synchronously block a threadpool
// thread. In the future we are going ot be removing this and disallowing its use.
public static T WaitAndGetResult_CanCallOnBackground<T>(this Task<T> task, CancellationToken cancellationToken)
{
try
{
task.Wait(cancellationToken);
}
catch (AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
}
return task.Result;
}
/// <summary>
/// Asserts the <see cref="Task"/> passed has already been completed.
/// </summary>
/// <remarks>
/// This is useful for a specific case: sometimes you might be calling an API that is "sometimes" async, and you're
/// calling it from a synchronous method where you know it should have completed synchronously. This is an easy
/// way to assert that while silencing any compiler complaints.
/// </remarks>
public static void VerifyCompleted(this Task task)
{
Contract.ThrowIfFalse(task.IsCompleted);
// Propagate any exceptions that may have been thrown.
task.GetAwaiter().GetResult();
}
/// <summary>
/// Asserts the <see cref="Task"/> passed has already been completed.
/// </summary>
/// <remarks>
/// This is useful for a specific case: sometimes you might be calling an API that is "sometimes" async, and you're
/// calling it from a synchronous method where you know it should have completed synchronously. This is an easy
/// way to assert that while silencing any compiler complaints.
/// </remarks>
public static TResult VerifyCompleted<TResult>(this Task<TResult> task)
{
Contract.ThrowIfFalse(task.IsCompleted);
// Propagate any exceptions that may have been thrown.
return task.GetAwaiter().GetResult();
}
}
|