File: Threading\TaskJoinExtensions.cs
Web Access
Project: src\src\EditorFeatures\TestUtilities\Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities.csproj (Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities)
// 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.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using Xunit;
 
namespace Roslyn.Test.Utilities
{
    public static class TaskJoinExtensions
    {
        /// <summary>
        /// Joins a <see cref="Task"/> to the current thread with a <see cref="Dispatcher"/> message pump in place
        /// during the join operation.
        /// </summary>
        public static void JoinUsingDispatcher(this Task task, CancellationToken cancellationToken)
        {
            JoinUsingDispatcherNoResult(task, cancellationToken);
 
            // Handle task completion by throwing the appropriate exception on failure
            task.GetAwaiter().GetResult();
        }
 
        /// <summary>
        /// Joins a <see cref="Task{TResult}"/> to the current thread with a <see cref="Dispatcher"/> message pump in
        /// place during the join operation.
        /// </summary>
        public static TResult JoinUsingDispatcher<TResult>(this Task<TResult> task, CancellationToken cancellationToken)
        {
            JoinUsingDispatcherNoResult(task, cancellationToken);
 
            // Handle task completion by throwing the appropriate exception on failure
            return task.GetAwaiter().GetResult();
        }
 
        private static void JoinUsingDispatcherNoResult(Task task, CancellationToken cancellationToken)
        {
            var frame = new DispatcherFrame();
 
            // When the task completes or cancellation is requested, mark the frame so we leave the message pump
            task.ContinueWith(
                t => frame.Continue = false,
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously,
                TaskScheduler.Default);
 
            using (var registration = cancellationToken.Register(() => frame.Continue = false))
            {
                Dispatcher.PushFrame(frame);
            }
 
            // Handle cancellation by throwing an exception
            if (!task.IsCompleted)
            {
                Assert.True(cancellationToken.IsCancellationRequested);
                cancellationToken.ThrowIfCancellationRequested();
            }
        }
    }
}