Cookbook for Visual Studio

Important for CPS extension authors and clients: please replace all references to ThreadHelper.JoinableTaskFactory with this.ThreadHandling.AsyncPump, where this.ThreadHandling is an [Import] IThreadHandling.

Initial setup

  • Add a reference to Microsoft.VisualStudio.Threading.

  • Add these using directives to your C# source file:

    using System.Threading.Tasks;
    using Microsoft.VisualStudio.Threading;
    
  • When using Microsoft.VisualStudio.Shell types in the same source file, import that namespace along with explicitly assigning Task to the .NET namespace so you can more conveniently use .NET Tasks:

    using Microsoft.VisualStudio.Shell;
    using Task = System.Threading.Tasks.Task;
    

Installing the analyzers

It is highly recommended that you install the Microsoft.VisualStudio.Threading.Analyzers NuGet package which adds analyzers to your project to help catch many common violations of the threading rules, helping you prevent your code from deadlocking.

How to switch to a background thread

await TaskScheduler.Default;

How to switch to the UI thread

In an async method

await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

In a regular synchronous method

First, consider carefully whether you can make your method async first and then follow the above pattern. If you must remain synchronous, you can switch to the UI thread like this:

ThreadHelper.JoinableTaskFactory.Run(async delegate
{
    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
    // You're now on the UI thread.
});

Canceling requests for the main thread

The SwitchToMainThreadAsync method has an overload that takes a CancellationToken. It is generally recommended to supply a token to this method to avoid the main thread transition when the operation is no longer necessary.

When a request for the main thread is canceled, a new continuation is scheduled on the threadpool to execute the remainder of the calling async method. When the thread pool continuation executes, an OperationCanceledException will be thrown at the caller. Note that in some cases, the original request for the main thread may be fulfilled after the token has been canceled but before the threadpool continuation has run. In that case, your async method will continue on the main thread and no exception will be thrown.

The method never throws or yields when the caller is already on the main thread, unless you specify the optional alwaysYield: true argument to the method.

When you definitely do not want the code following the transition to the main thread to execute if the token has been canceled, you should follow up the request for the main thread with a call to cancellationToken.ThrowIfCancellationRequested();

How to switch to or use the UI thread with a specific priority

For those times when you need the UI thread but you don’t want to introduce UI delays for the user, you can use the StartOnIdle extension method which will run your code on the UI thread when it is otherwise idle.

await ThreadHelper.JoinableTaskFactory.StartOnIdle(
    async delegate
    {
        for (int i = 0; i < 10; i++)
        {
            DoSomeWorkOn(i);

            // Ensure we frequently yield the UI thread in case user input is waiting.
            await Task.Yield();
        }
    });

If you have a requirement for a specific priority (which may be higher or lower than background), you can use the WithPriority extension method, like this:

var databindPriorityJTF = ThreadHelper.JoinableTaskFactory.WithPriority(someDispatcher, DispatcherPriority.DataBind);
await databindPriorityJTF.RunAsync(
  async delegate
  {
    // The JTF instance you use to actually make the switch is irrelevant here.
    // The priority is dictated by the scenario owner, which is the one
    // running JTF.RunAsync at the bottom of the stack (or just above us in this sample).
    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
  });

The way the priority is dictated by the JoinableTaskFactory instance that RunAsync is called on rather than the one on which SwitchToMainThreadAsync is invoked allows you to set the priority for the scenario and then call arbitrary code in VS and expect all of the switches to the main thread that code might require to honor that priority that you set as the scenario owner.

There are several WithPriority extension method overloads, allowing you to typically match exactly the priority your code was accustomed to before switching to use JoinableTaskFactory for switching to the main thread.

Call async code from a synchronous method (and block on the result)

ThreadHelper.JoinableTaskFactory.Run(async delegate
{
    await SomeOperationAsync(...);
});

How to write a “fire and forget” method responsibly?

“Fire and forget” methods are async methods that are called without awaiting the Task that they (may) return.

Do not implement your fire and forget async method by returning async void. These methods will crash the process if they throw an unhandled exception. They also may cause a crash if they are still running when VS tears down the AppDomain on shutdown.

There are two styles of “fire and forget”:

void-returning fire and forget methods

These methods are designed to always be called in a fire and forget fashion, as the caller only gets a void return value and cannot await the async method even if it wanted to.

These methods should be named to indicate that they start an operation. For example, instead of calling the method Task DoSomethingAsync() you might call it void StartSomething().

Notwithstanding the void return type, you’ll want to be async internally in order to actually do the work later instead of on your caller’s callstack. But you also should be sure your async work finishes before your object claims to be disposed. You can accomplish both of these objectives using the JoinableTaskFactory.RunAsync method, and tacking on an extension method that captures failures and reports them for your analysis later. For the Visual Studio team’s own internal use, the FileAndForget method can be tacked on at the end to send failure reports via VS telemetry fault events. You may want to handle your own exceptions within the async delegate as well.

void StartOperation()
{
  this.JoinableTaskFactory.RunAsync(async delegate
  {
    await Task.Yield(); // get off the caller's callstack.
    DoWork();
    this.DisposalToken.ThrowIfCancellationRequested();
    DoMoreWork();
  }).FileAndForget("vs/YOUR-FEATURE/YOUR-ACTION"); // Microsoft's own internal extension method
}

Where this.JoinableTaskFactory is defined by your AsyncPackage or, if you don’t have one, by re-implementing this pattern in your own class:

class MyResponsibleType : IDisposable
{
  private readonly CancellationTokenSource disposeCancellationTokenSource = new CancellationTokenSource();

  internal MyResponsibleType()
  {
    this.JoinableTaskCollection = ThreadHelper.JoinableTaskContext.CreateCollection();
    this.JoinableTaskFactory = ThreadHelper.JoinableTaskContext.CreateFactory(this.JoinableTaskCollection);
  }

  JoinableTaskFactory JoinableTaskFactory { get; }
  JoinableTaskCollection JoinableTaskCollection { get; }

  /// <summary>
  /// Gets a <see cref="CancellationToken"/> that can be used to check if the package has been disposed.
  /// </summary>
  CancellationToken DisposalToken => this.disposeCancellationTokenSource.Token;

  public void Dispose()
  {
    this.Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (disposing)
    {
      this.disposeCancellationTokenSource.Cancel();

      try
      {
        // Block Dispose until all async work has completed.
        ThreadHelper.JoinableTaskFactory.Run(this.JoinableTaskCollection.JoinTillEmptyAsync);
      }
      catch (OperationCanceledException)
      {
        // this exception is expected because we signaled the cancellation token
      }
      catch (AggregateException ex)
      {
        // ignore AggregateException containing only OperationCanceledException
        ex.Handle(inner => (inner is OperationCanceledException));
      }
      finally
      {
        this.disposeCancellationTokenSource.Dispose();
      }
    }
  }
}

Task-returning fire and forget methods

These are your typical async methods, but called without awaiting the resulting Task.

Not awaiting on the Task means that if that async method were to throw an exception (thus faulting the returned Task) that no one would notice. The error would go unreported and undetected – until the side effects of the async method are discovered to be incomplete some other way. In a few cases this may be desirable. Since calling a Task-returning method without awaiting its result will often make C# emit a compiler warning, a void-returning Forget() extension method exists so you can tack it onto the end of an async method call to suppress the compiler warning, and make it clear to others reading the code that you are intentionally ignoring the result.

DoSomeWork();
DoSomeWorkAsync().Forget(); // no await here
DoABitMore();

Better than Forget() is to use FileAndForget(string) which will handle faulted Tasks by adding the error to the VS activity log and file a fault event for telemetry collection so the fault can be detected and fixed.

DoSomeWork();
DoSomeWorkAsync().FileAndForget("vs/YOUR-FEATURE/YOUR-ACTION"); // no await here
DoABitMore();

How can I await IVsTask?

Make sure you have the using directives defined at the top of this document. That adds a GetAwaiter extension method to IVsTask that makes it awaitable.

You can safely await an IVsTask in an ordinary C# async method or inside a JoinableTaskFactory.Run or RunAsync delegate.

How can I return IVsTask from a C# async method?

The preferred way is to use the JoinableTaskFactory.RunAsyncAsVsTask(Func<CancellationToken, Task>) extension method. This gives you a CancellationToken that is tied to IVsTask.Cancel().

IVsTask IVsYourNativeCompatibleInterface.DoAsync(string arg1)
{
    return ThreadHelper.JoinableTaskFactory.RunAsyncAsVsTask(
        VsTaskRunContext.UIThreadNormalPriority,
        cancellationToken => this.DoAsync(arg1, cancellationToken));
}

public async Task DoAsync(string arg1, CancellationToken cancellationToken)
{
    await Task.Yield();
}

In a pinch, where all you have is a JoinableTask, you can readily convert it to an IVsTask by calling the JoinableTask.AsVsTask() extension method as shown below. But this means the IVsTask.Cancel() method will not be able to communicate the cancellation request to the async delegate.

public IVsTask DoAsync()
{
    JoinableTask jt = ThreadHelper.JoinableTaskFactory.RunAsync(
        async delegate
        {
            await SomethingAsync();
            await SomethingElseAsync();
        });
    return jt.AsVsTask();
}

How can I block the UI thread for long or async work while displaying a (cancelable) wait dialog?

While it’s generally preferable that you do work quickly or asynchronously so as to not block the user from interacting with the IDE, it is sometimes necessary and/or desirable to block user input. While doing so, it’s good to provide feedback so the user knows the application hasn’t hung. To display a dialog explaining what’s going on while you’re on and blocking the UI thread, and even give the user a chance to cancel the operation, you can call this extension method on JoinableTaskFactory:

ThreadHelper.JoinableTaskFactory.Run(
    "I'm churning on your data",
    async (progress, cancellationToken) =>
    {
        DoLongRunningWork();
        progress.Report(new ThreadedWaitDialogProgressData("Getting closer to being done.", isCancelable: true));
        await AndSomeAsyncWorkButBlockMainThreadAnyway(cancellationToken);
    });

If your operation cannot be canceled, just drop the CancellationToken parameter from your anonymous delegate and the dialog that is shown to the user will not include a Cancel button:

ThreadHelper.JoinableTaskFactory.Run(
    "I'm churning on your data",
    async (progress) =>
    {
        DoLongRunningWork();
        progress.Report(new ThreadedWaitDialogProgressData("Getting closer to being done."));
        await AndSomeAsyncWorkButBlockMainThreadAnyway();
    });

How can I initialize my VS package asynchronously?

As of Visual Studio 2015 (Dev14) you can define async VS packages, allowing binaries to be loaded on background threads and for your package to complete async work without blocking the UI thread as part of package load. Async Package 101

How do I make sure my async work has finished before my VS Package closes?

This is a very important topic because we know VS can crash on shutdown because async tasks or background threads still actively running after all VS packages have supposedly shutdown. The recommended pattern to solve this problem is to derive from AsyncPackage instead of Package. Then for any async work your package is responsible for starting but that isn’t awaited on somewhere, you should start that work with your AsyncPackage’s JoinableTaskFactory property, calling the RunAsync method. This ensures that on package close, your async work must complete before the AppDomain is shutdown. Your async work should generally also honor the AsyncPackage.DisposalToken and cancel itself right away when that token is signaled so that VS shutdown is not slowed down significantly by work that no longer matters.

How do I effectively verify that my code is fully free-threaded?

Code is free-threaded if it (and all code it invokes transitively) can complete on the caller’s thread, no matter which thread that is. Code that is not free-threaded is said to be thread-affinitized, meaning some of its work may require execution on a particular thread.

It is not always necessary for code to be free-threaded. There are many cases where code must run on the main thread because it calls into other code that itself has thread-affinity or is not thread-safe. Important places to be free-threaded include:

  1. Code that runs in the MEF activation code paths (e.g. importing constructors, OnImportsSatisfied callbacks, and any code called therefrom).
  2. Code that may run on a background thread, especially when run within an async context.
  3. Static field initializers and static constructors.

In Visual Studio, thread-affinitized code typically requires the main thread (a.k.a. the UI thread). Code that has main thread affinity may appear to work when called from background threads for a few reasons, including:

  1. The code can sometimes run without requiring the main thread. For example, it only requires the main thread the first time it runs, and subsequent calls are effectively free-threaded.
  2. The code may switch itself to the main thread if its caller didn’t call it on that thread. This approach only works if the main thread is available to service such a request to switch from a background thread.

The foregoing conditions can make it difficult to test whether your code is truly free-threaded. Knowing your code is free-threaded is important before executing it off the main thread, particularly when that code executes synchronously, since otherwise it can deadlock or contribute to threadpool starvation.

To test whether your code executes without any requirement on the main thread, you can run that code in VS within such a construct as this:

ThreadHelper.ThrowIfNotOnUIThread(); // this test only catches issues when it blocks the UI thread.
jtf.Run(async delegate
{
   using (jtf.Context.SuppressRelevance())
   {
      await Task.Run(delegate
      {
         var c = ActivateComponent();
         c.UseComponent();
      });
   }
});

This would effectively ensure that the main thread will not respond to any request from your test method, thus forcing a deadlock if one was lurking and could occur in otherwise non-deterministic conditions.

Try to execute such test code as early in the VS process as possible. This will help you catch any issues with code you may call that is thread affinitized the first time it is executed.

Note that being free-threaded is not the same thing as being thread-safe, which is an independent metric. Code can be free-threaded, thread-safe, both, or neither.

How do I effectively verify that my code is fully thread-safe?

Code is thread-safe if your code does not malfunction when called from more than one thread at a time.

It is not always necessary for code to be thread-safe. In fact many classes in the Base Class Library of .NET itself is not thread-safe. Writing thread-safe code involves mitigating data corruption, deadlocks, and higher level goals such as avoiding lock contention and threadpool starvation. Validating that code is thread-safe requires exhaustive reviews and testing, and thread-safety bugs can still slip through. Whether thread-safety should be a goal should typically be determined at the class level and clearly documented. Changing from thread-safe to non-thread-safe should be considered a breaking change. A class should be made thread-safe if being called from multiple threads at once is a supported scenario.

Techniques for writing thread-safe code include:

  1. Using synchronization primitives such as C# lock when accessing fields.
  2. Using immutable data structures and interlocked exchange methods while handling race conditions.

Techniques for verifying that code is thread-safe include both thorough code reviews and automated tests. Automated tests should execute your code concurrently. You may find you can shake out different bugs by testing with concurrency levels equal to Environment.ProcessorCount to maximize parallelism and throughput as well as a multiplier of that number (e.g. Environment.ProcessorCount * 3) so that hyper-switching of the CPU leads to different time slices and unique thread-safety bugs to be found. Such tests should verify that unexpected exceptions are not thrown, no hangs occur, and that the data at the conclusion of such concurrent execution is not corrupted.

Code can be made to run concurrently from a unit test using this technique:

int concurrencyLevel = Environment.ProcessorCount; // * 3
await Task.WhenAll(Enumerable.Range(1, concurrencyLevel).Select(i => Task.Run(delegate {
    CallYourCodeHere();
})));

If CallYourCodeHere() executes fast enough, it’s possible that the above does not lead to actual concurrent execution since Task.Run does not guarantee that the delegate executes at a particular time. To increase the chances of concurrent execution, you can use the CountdownEvent class:

int concurrencyLevel = Environment.ProcessorCount; // * 3
var countdown = new CountdownEvent(concurrencyLevel);
await Task.WhenAll(Enumerable.Range(1, concurrencyLevel).Select(i => Task.Run(delegate {
    countdown.Signal();
    countdown.Wait();
    CallYourCodeHere();
})));

The above code will force allocation of a thread for each degree of concurrency you specify, and each thread will block until all threads are ready to go, at which point they will all unblock together (subject to kernel thread scheduling).

Note that being thread-safe is not the same thing as being free-threaded, which is an independent metric. Code can be free-threaded, thread-safe, both, or neither.

Should I await a Task with .ConfigureAwait(false)?

What does it even mean?

When you await an expression (e.g. a Task or Task<T>), that expression must produce an “awaiter”. This is mostly hidden as an implementation detail by the compiler. At runtime this “awaiter” can indicate whether the expression being awaited on represents a completed operation or one that is not yet complete. When the operation is complete, the awaiting code simply continues execution immediately (no yielding, no thread switching, etc.). When the operation is not complete, the awaiter is asked to execute a delegate when the operation is done. At a high level, this means that when you write await SomethingAsync() the next line of code in your method will not execute until the Task returned by SomethingAsync() is complete.

Suppose that SomethingAsync() returns a Task that does not complete for 5 seconds. During that time, your method is no longer on the callstack (because it yielded when the Task wasn’t complete). So when the Task completes, it now has a responsibility to invoke the callback the compiler created in order to “resume” your method right where it left off. Which thread should it use to invoke your delegate? That is up to the awaiter to decide.

When you await a Task, the policy for what thread the next part of your async method executes on is determined by a type called TaskAwaiter. It will execute the callback on the same thread/context that your async method was on when it originally awaited the Task. This allows you to be on the UI thread, await something, and then continue your code, still on the UI thread. This is independent of which thread the Task itself may have been running on. TaskAwaiter does this by capturing the SynchronizationContext or TaskScheduler.Current from the caller and using either of those as a means of scheduling the invocation of the callback. When neither of those are present, it will simply schedule the callback to execute on the threadpool.

But what if your code may be on the UI thread, but does not need the UI thread to finish its work after the awaited Task has completed? You can express to the awaited Task that you don’t mind executing on the threadpool by adding .ConfigureAwait(false) to the end of the Task. This causes the compiler to interact with ConfiguredTaskAwaiter instead of TaskAwaiter. The ConfiguredTaskAwaiter’s policy is (if you pass in false when creating it) to always schedule continuations to the threadpool (or in some cases inline the continuation immediately after the Task itself is completed).

Note: Use of .ConfigureAwait(true) is equivalent to awaiting a Task directly. Using this suffix is a way to suppress the warning emitted by some analyzers that like to see .ConfigureAwait(false) everywhere. Where no such analyzer is active, omitting the suffix is recommended for improved code readability.

Short answer

  • Use ConfigureAwait(false) when writing app-independent library code. Such a library should avoid frequent use of Task.Wait() and Task.Result.
  • Use ConfigureAwait(true) when writing code where a JoinableTaskFactory is available. Use await TaskScheduler.Default; before CPU intensive, free-threaded work.

For Visual Studio packages, the recommendation is to not use .ConfigureAwait(false).

Justification

Awaiting tasks with .ConfigureAwait(false) is a popular trend for a couple reasons:

  1. It allows the continuation (when scheduled) to run on the threadpool instead of returning to an unknown SynchronizationContext set by the caller. This can improve efficiency, and keep CPU intensive work off the UI thread, thereby increasing an application’s responsiveness to user input.
  2. It can avoid deadlocks when people use Task.Wait() or Task.Result from the UI thread.

But .ConfigureAwait(false) carries disadvantages as well:

  1. The tendency for Task.Wait() to work actually encourages such synchronously blocking code, yet deadlocks can still happen if the UI thread is actually required for one of the scheduled continuations.
  2. It makes for harder to read and maintain async code.
  3. It may not actually move CPU intensive work off the UI thread since it only makes the switch on the first yielding await.
  4. It contributes to threadpool starvation when the code using it is called in the context of a JoinableTaskFactory.Run delegate.

The last disadvantage above deserves some elaboration. The JoinableTaskFactory.Run method sets a special SynchronizationContext when invoking its delegate so that async continuations automatically run on the original thread (without deadlocking). When awaits use .ConfigureAwait(false) it ignores the SynchronizationContext and defeats this optimization. As a result the scheduled continuation will occupy a thread from the threadpool, even though the JTF.Run thread is blocked waiting for the delegate to complete and could have executed the continuation. In this scenario, two threads are allocated although only one is active.

The problem grows when multiple frames in the callstack use Task.Wait or JoinableTaskFactory.Run. With each synchronously blocking frame, that thread now becomes useless till the whole operation that it is waiting for is complete, and yet another thread is allocated to make that possible. In some severe cases, we’ve seen the application hang for over a minute while 75+ threadpool threads were allocated one at a time, each to try to make progress after the thread previously allocated just synchronously blocks for completion. Using JoinableTaskFactory.Run consistently prevents this, but only when the code executed by the delegate passed to it avoids using .ConfigureAwait(false).

Code invoked from within JoinableTaskFactory.RunAsync (the async version) does not immediately synchronously block and thus tends to be less of a concern when using .ConfigureAwait(false). However, since a delegated passed to this method can become blocking later using JoinableTask.Join() or await the JoinableTask within another JoinableTaskFactory.Run call, it is similarly recommended to avoid .ConfigureAwait(false) in all JTF contexts.

So how do we get the best of both worlds? How can we have a responsive app, keeping CPU intensive work off the UI thread while not using .ConfigureAwait(false)? The guideline is that when you’re about to start some CPU intensive, free-threaded work is to first explicitly switch to the threadpool using await TaskScheduler.Default;. This simple approach works consistently without many of the disadvantages listed above.

Some important notes

  1. Using .ConfigureAwait(false) does not guarantee that code after it will be on the threadpool. It has absolutely no effect at all if the Task itself is already complete since the compiler will simply continue execution immediately as if there were no await there.
  2. If you have a policy to use .ConfigureAwait(false) it is important to use it everywhere (not just on the first await in a method), because the first awaited expression might not yield but the second one may, and therefore the yielding expression must have that suffix to get the effect.
  3. If the awaited Task completes on the UI thread, continuations are typically not inlined, even if .ConfigureAwait(false) is used when awaiting the Task.
  4. If the awaited Task completes on a threadpool thread, then it will usually inline continuations that are expecting to be invoked on the threadpool as an optimization. This includes continuations scheduled with .ConfigureAwait(false) and those that were scheduled while already on the threadpool.