diff --git a/Directory.Build.props b/Directory.Build.props
index e41668d8e..dad792fba 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -24,7 +24,7 @@
embedded
- https://github.com/Microsoft/vs-threading
+ https://microsoft.github.io/vs-threading/
Microsoft
Microsoft
© Microsoft Corporation. All rights reserved.
diff --git a/README.md b/README.md
index 97da5667f..689fbf5d2 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,9 @@
Async synchronization primitives, async collections, TPL and dataflow extensions. The JoinableTaskFactory allows synchronously blocking the UI thread for async work. This package is applicable to any .NET application (not just Visual Studio).
-[Overview documentation](doc/index.md).
+[Getting started](https://microsoft.github.io/vs-threading/docs/getting-started.md).
-[See the full list of features](src/Microsoft.VisualStudio.Threading/README.md).
+[See the full list of features](https://microsoft.github.io/vs-threading/docs/features.md).
## Microsoft.VisualStudio.Threading.Analyzers
@@ -19,6 +19,4 @@ Async synchronization primitives, async collections, TPL and dataflow extensions
Static code analyzer to detect common mistakes or potential issues regarding threading and async coding.
-[Diagnostic analyzer rules](doc/analyzers/index.md).
-
-[See the full list of features](src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/README.md).
+[Diagnostic analyzer rules](https://microsoft.github.io/vs-threading/analyzers/index.md).
diff --git a/doc/analyzers/VSTHRD001.md b/doc/analyzers/VSTHRD001.md
index 1dd47c1ac..0bd13cf04 100644
--- a/doc/analyzers/VSTHRD001.md
+++ b/doc/analyzers/VSTHRD001.md
@@ -1,82 +1 @@
-# VSTHRD001 Avoid legacy thread switching methods
-
-Switching to the UI thread should be done using `JoinableTaskFactory.SwitchToMainThreadAsync`
-rather than legacy methods such as `Dispatcher.Invoke` or `ThreadHelper.Invoke`.
-This avoids deadlocks and can reduce threadpool starvation.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-ThreadHelper.Generic.Invoke(delegate {
- DoSomething();
-});
-```
-
-or
-
-```cs
-Dispatcher.CurrentDispatcher.BeginInvoke(delegate {
- DoSomething();
-});
-```
-
-## Solution
-
-Use `await SwitchToMainThreadAsync()` instead, wrapping with the `JoinableTaskFactory`'s `Run` or `RunAsync` method if necessary:
-
-```csharp
-void Foo() {
- ThreadHelper.JoinableTaskFactory.Run(async delegate {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- DoSomething();
- });
-}
-```
-
-In the above example, we obtain a `JoinableTaskFactory` instance from the `ThreadHelper.JoinableTaskFactory` static property
-as it exists within Visual Studio itself. Other applications should create and expose their own `JoinableTaskContext` and/or `JoinableTaskFactory` for use in code that run in these applications.
-See our doc on [consuming `JoinableTaskFactory` from a library](https://github.com/microsoft/vs-threading/blob/main/doc/library_with_jtf.md) for more information.
-
-### Replacing Dispatcher.BeginInvoke
-
-When updating calls to `Dispatcher.BeginInvoke`, there are a few considerations to consider.
-
-1. `BeginInvoke` schedules the delegate for execution later.
-1. `BeginInvoke` always executes the delegate on the dispatcher's thread.
-1. `BeginInvoke` schedules the delegate at some given priority, or default priority determined by the dispatcher.
-
-To resolve a warning for such code, it is often sufficient to replace it with this, which is *roughly* equivalent:
-
-```cs
-await joinableTaskFactory.RunAsync(async delegate {
- await joinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true);
- DoSomething();
-})
-```
-
-The first line in the delegate is necessary to match the behaviors of 1 and 2 on the above list.
-When the caller is known to already be on the main thread, you can simplify it slightly to this:
-
-```cs
-await joinableTaskFactory.RunAsync(async delegate {
- await Task.Yield();
- DoSomething();
-})
-```
-
-Matching behavior 3 on the list above may be important when the dispatcher priority is specified in the BeginInvoke call and was chosen for a particular reason.
-In such a case, you can ensure that `JoinableTaskFactory` matches that priority instead of using its default by creating a special `JoinableTaskFactory` instance with the priority setting you require using the [`JoinableTaskFactory.WithPriority`](https://learn.microsoft.com/dotnet/api/microsoft.visualstudio.threading.dispatcherextensions.withpriority?view=visualstudiosdk-2022) method.
-
-Altogether, this might look like:
-
-```cs
-await joinableTaskFactory.WithPriority(DispatcherPriority.DataBind).RunAsync(async delegate {
- await joinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true);
- DoSomething();
-})
-```
-
-## Configuration
-
-This analyzer is configurable via the `vs-threading.LegacyThreadSwitchingMembers.txt` file.
-See our [configuration](configuration.md) topic for more information.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD001.html).
diff --git a/doc/analyzers/VSTHRD002.md b/doc/analyzers/VSTHRD002.md
index 6a05a94ca..69a4fc3c9 100644
--- a/doc/analyzers/VSTHRD002.md
+++ b/doc/analyzers/VSTHRD002.md
@@ -1,45 +1 @@
-# VSTHRD002 Avoid problematic synchronous waits
-
-Synchronously waiting on `Task`, `ValueTask`, or awaiters is dangerous and may cause dead locks.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-void DoSomething()
-{
- DoSomethingElseAsync().Wait();
- DoSomethingElseAsync().GetAwaiter().GetResult();
- var result = CalculateSomethingAsync().Result;
-}
-```
-
-## Solution
-
-Please consider the following options:
-
-1. Switch to asynchronous wait if the caller is already a "async" method.
-1. Change the chain of callers to be "async" methods, and then change this code to be asynchronous await.
-1. Use `JoinableTaskFactory.Run()` to wait on the tasks or awaiters.
-
-```csharp
-async Task DoSomethingAsync()
-{
- await DoSomethingElseAsync();
- await DoSomethingElseAsync();
- var result = await CalculateSomethingAsync();
-}
-
-void DoSomething()
-{
- joinableTaskFactory.Run(async delegate
- {
- await DoSomethingElseAsync();
- await DoSomethingElseAsync();
- var result = await CalculateSomethingAsync();
- });
-}
-```
-
-Refer to [Asynchronous and multithreaded programming within VS using the JoinableTaskFactory][1] for more information.
-
-[1]: https://devblogs.microsoft.com/premier-developer/asynchronous-and-multithreaded-programming-within-vs-using-the-joinabletaskfactory/
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD002.html).
diff --git a/doc/analyzers/VSTHRD003.md b/doc/analyzers/VSTHRD003.md
index 49d8ad87b..34aba3d45 100644
--- a/doc/analyzers/VSTHRD003.md
+++ b/doc/analyzers/VSTHRD003.md
@@ -1,267 +1 @@
-# VSTHRD003 Avoid awaiting foreign Tasks
-
-Tasks that are created and run from another context (not within the currently running method or delegate)
-should not be returned or awaited on. Doing so can result in deadlocks because awaiting a `Task`
-does not result in the awaiter "joining" the effort such that access to the main thread is shared.
-If the awaited `Task` requires the main thread, and the caller that is awaiting it is blocking the
-main thread, a deadlock will result.
-
-When required to await a task that was started earlier, start it within a delegate passed to
-`JoinableTaskFactory.RunAsync`, storing the resulting `JoinableTask` in a field or variable.
-You can safely await the `JoinableTask` later.
-
-## Simple examples of patterns that are flagged by this analyzer
-
-The following example would likely deadlock if `MyMethod` were called on the main thread,
-since `SomeOperationAsync` cannot gain access to the main thread in order to complete.
-
-```csharp
-void MyMethod()
-{
- System.Threading.Tasks.Task task = SomeOperationAsync();
- joinableTaskFactory.Run(async delegate
- {
- await task; /* This analyzer will report warning on this line. */
- });
-}
-```
-
-In the next example, `WaitForMyMethod` may deadlock when `this.task` has not completed
-and needs the main thread to complete.
-
-```csharp
-class SomeClass
-{
- System.Threading.Tasks.Task task;
-
- SomeClass()
- {
- this.task = SomeOperationAsync();
- }
-
- async Task MyMethodAsync()
- {
- await this.task; /* This analyzer will report warning on this line. */
- }
-
- void WaitForMyMethod()
- {
- joinableTaskFactory.Run(() => MyMethodAsync());
- }
-}
-```
-
-More [advanced examples](#advanced-cases) are further down in this document, below the solution section for the simpler examples.
-
-## Solution for simpler cases
-
-To await the result of an async method from with a JoinableTaskFactory.Run delegate,
-invoke the async method within the JoinableTaskFactory.Run delegate:
-
-```csharp
-void MyMethod()
-{
- joinableTaskFactory.Run(async delegate
- {
- System.Threading.Tasks.Task task = SomeOperationAsync();
- await task;
- });
-}
-```
-
-Alternatively wrap the original method invocation with JoinableTaskFactory.RunAsync:
-
-```csharp
-class SomeClass
-{
- JoinableTask joinableTask;
-
- SomeClass()
- {
- this.joinableTask = joinableTaskFactory.RunAsync(() => SomeOperationAsync());
- }
-
- async Task MyMethodAsync()
- {
- await this.joinableTask;
- }
-
- void WaitForMyMethod()
- {
- joinableTaskFactory.Run(() => MyMethodAsync());
- }
-}
-```
-
-## Advanced cases
-
-### `TaskCompletionSource`
-
-In the next example, a `TaskCompletionSource` is used as a black-box for unblocking functionality.
-It too represents awaiting a foreign task:
-
-```cs
-class SomeClass
-{
- TaskCompletionSource tcs = new();
-
- public async Task MyMethodAsync()
- {
- await this.tcs.Task; /* This analyzer will report warning on this line. */
- /* do more stuff */
- }
-
- void UnlockProgress()
- {
- this.tcs.TrySetResult(true);
- }
-}
-```
-
-The problem with the above code is that `MyMethodAsync()` waits for unknown work (whatever work will lead to the completion of the `TaskCompletionSource`) before making progress.
-If `UnlockProgress()` is never called, the caller of `MyMethodAsync()` will be awaiting forever.
-Now suppose that the caller of `MyMethodAsync()` is actually inside a `JoinableTaskFactory.Run` delegate:
-
-```cs
-void SomeCaller()
-{
- joinableTaskFactory.Run(async delegate
- {
- await someClass.MyMethodAsync();
- });
-}
-```
-
-If `SomeCaller()` runs on the main thread, then it will effectively block the main thread while waiting for `this.tcs.Task` from `SomeClass` to complete.
-Now suppose that another thread comes along and wants to do some work before calling `UnlockProgress()`:
-
-```cs
-partial class SomeClass
-{
- async Task KeyMasterAsync()
- {
- await joinableTaskFactory.SwitchToMainThreadAsync();
- // do some work
- // Unblock others
- someClass.UnlockProgress();
- }
-}
-```
-
-We have a deadlock, because `SomeCaller()` is blocking the main thread while waiting for `UnlockProgress()` to be called, but `UnlockProgress()` will not be called until `KeyMasterAsync` can reach the main thread.
-
-Fixing this fundamentally means that `SomeCaller` will need to *join* whatever work may be needed to ultimately call `UnlockProgress`. But for `SomeCaller`, that work is unknown, since it's at least partially inside another class.
-`TaskCompletionSource` is fundamentally a blackbox and the most difficult thing to use correctly while avoiding deadlocks.
-
-Preferred solutions involve replacing `TaskCompletionSource` with another type that makes tracking the work involved automatic.
-These include:
-
-1. Use `JoinableTaskFactory.RunAsync` and store the resulting `JoinableTask` in a field to await later.
-1. Use `AsyncLazy` for one-time init work that should only start if required. Be sure to pass in a `JoinableTaskFactory` instance to its constructor.
-
-Assuming you must keep using `TaskCompletionSource` though, here's how it can be done as safely as possible.
-Joining a set of unknown work is best done with the `JoinableTaskCollection` class.
-It is the responsibility of `SomeClass` in the example above to work with this collection to avoid deadlocks, like this:
-
-```cs
-class SomeClass
-{
- TaskCompletionSource tcs = new();
- JoinableTaskCollection jtc;
- JoinableTaskFactory jtf;
-
- internal SomeClass(JoinableTaskContext joinableTaskContext)
- {
- this.jtc = joinableTaskContext.CreateCollection();
- this.jtf = joinableTaskContext.CreateFactory(this.jtc);
- }
-
- public async Task MyMethodAsync()
- {
- // Our caller is interested in completion of the TaskCompletionSource,
- // so join the collected effort while waiting, to avoid deadlocks.
- using (this.jtc.Join())
- {
- await this.tcs.Task; /* This analyzer will report warning on this line. */
- }
-
- /* do more stuff */
- }
-
- void UnlockProgress()
- {
- this.tcs.TrySetResult(true);
- }
-
- async Task KeyMasterAsync()
- {
- // As this method must complete to signal the TaskCompletionSource,
- // all of its work must be done within the context of a JoinableTask
- // that belongs to the JoinableTaskCollection.
- // jtf.RunAsync will add the JoinableTask it creates to the jtc collection
- // because jtf was created with jtc as an argument in our constructor.
- await this.jtf.RunAsync(async delegate
- {
- // Because we're in the jtc collection, anyone waiting on MyMethodAsync
- // will automatically lend us use of the main thread if they have it
- // to avoid deadlocks.
- // It does NOT matter whether we use jtf or another JoinableTaskFactory instance
- // at this point.
- await anyOldJTF.SwitchToMainThreadAsync();
-
- // do some work
- // Unblock others
- this.UnlockProgress();
- });
- }
-}
-```
-
-Notice how the public API of the class does not need to expose any `JoinableTask`-related types.
-It's an implementation detail of the class.
-
-This works fine when the class itself fully controls the work to complete the `TaskCompletionSource`.
-When _other_ classes also do work (independently of work started within `SomeClass`), the placement and access to the `JoinableTaskFactory` that is associated with the `JoinableTaskCollection` may need to be elevated so that other classes can access it as well so that *all* the work required to complete the `TaskCompletionSource` will be tracked.
-
-### Task chaining or other means to ensure sequential execution
-
-Task chaining is another technique that can lead to deadlocks.
-Task chaining is where a single `Task` is kept in a field and used to call `Task.ContinueWith` to append another Task, and the resulting Task is then assigned to the field, like this:
-
-```cs
-class TaskChainingExample
-{
- private readonly object lockObject = new();
- private Task lastTask = Task.CompletedTask;
-
- internal Task AddWorkToEnd(Funk work)
- {
- lock (this.lockObject)
- {
- return this.lastTask = this.lastTask.ContinueWith(_ => work()).Unwrap();
- }
- }
-}
-```
-
-(Note: The above example has several *other* issues that would require more code to address, but it illustrates the idea of task chaining.)
-
-The deadlock risk with task chaining is that again, the chain of tasks come together to form a kind of private queue which the `JoinableTaskFactory` has no visibility into.
-When a task is not at the front of the queue but its owner blocks the main thread for its completion, and if any other task ahead of it in the queue needs the main thread, a deadlock will result.
-
-For this reason (and several others), task chaining is *not* recommended.
-Instead, you can achieve a thread-safe queue that executes work sequentially by utilizing the `ReentrantSemaphore` class.
-
-Fixing the above example would translate to this (allowing for a variety of reentrancy modes):
-
-```cs
-class SequentialExecutingQueueExample
-{
- private readonly ReentrantSemaphore semaphore = ReentrantSemaphore.Create(initialCount: 1, joinableTaskContext, ReentrancyMode.Stack);
-
- internal Task AddWorkToEnd(Func work)
- {
- return semaphore.ExecuteAsync(work);
- }
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD003.html).
diff --git a/doc/analyzers/VSTHRD004.md b/doc/analyzers/VSTHRD004.md
index 39d3bbdf6..8c937964f 100644
--- a/doc/analyzers/VSTHRD004.md
+++ b/doc/analyzers/VSTHRD004.md
@@ -1,43 +1 @@
-# VSTHRD004 Await SwitchToMainThreadAsync
-
-Calls to `JoinableTaskFactory.SwitchToMainThreadAsync` must be awaited
-or it is a no-op.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-void MyMethod()
-{
- joinableTaskFactory.SwitchToMainThreadAsync();
- UIThreadBoundWork();
-}
-```
-
-## Solution
-
-Add `await` in front of the call to `JoinableTaskFactory.SwitchToMainThreadAsync`.
-
-This requires an async context. Here, we fix the problem by making the outer method async:
-
-```csharp
-async Task MyMethodAsync()
-{
- await joinableTaskFactory.SwitchToMainThreadAsync();
- UIThreadBoundWork();
-}
-```
-
-
-Alternatively if found in a synchronous method that cannot be made async,
-this failure can be fixed by lifting the code into a delegate passed to `JoinableTaskFactory.Run`:
-
-```csharp
-void MyMethod()
-{
- joinableTaskFactory.Run(async delegate
- {
- await joinableTaskFactory.SwitchToMainThreadAsync();
- UIThreadBoundWork();
- });
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD004.html).
diff --git a/doc/analyzers/VSTHRD010.md b/doc/analyzers/VSTHRD010.md
index bfb01c11d..15a5d10b3 100644
--- a/doc/analyzers/VSTHRD010.md
+++ b/doc/analyzers/VSTHRD010.md
@@ -1,61 +1 @@
-# VSTHRD010 Invoke single-threaded types on Main thread
-
-Acquiring, casting, or invoking single-threaded objects should be done after ensuring
-that your code is running on the main thread.
-
-This analyzer can be configured to:
-1. Recognize the objects that are single-threaded that are unique to your app or library.
-2. Recognize synchronous methods that verify the caller is already on the main thread.
-3. Recognize methods that switch to the main thread when the caller awaits them.
- Calls to `JoinableTaskFactory.SwitchToMainThreadAsync` methods are pre-configured.
-
-See our [configuration](configuration.md) topic to learn more about customizing this analyzer.
-
-This analyzer also recognizes requirements to use the main thread transitively within your solution.
-For example, if method `A()` invokes a type that we know from configuration requires the main thread,
-and `B()` calls `A()`, then the `B` method also needs the UI thread transitively.
-This analyzer flags `B()` as needing to call a method that throws if not already on the main thread
-only when `A()` is written to call such a method.
-
-**NOTE:** This analyzer requires [full solution analysis](fsa.md).
-
-## Examples of patterns that are flagged by this analyzer
-
-This example is based on the configuration available from the Visual Studio SDK
-that defines `IVs*` interfaces as requiring the main thread.
-
-```csharp
-private void CallVS()
-{
- IVsSolution sln = GetIVsSolution();
- sln.SetProperty(); // This analyzer will report warning on this invocation.
-}
-```
-
-## Solution
-
-First ensure you are running on the main thread before interacting with single-threaded objects.
-Either throw when you are not on the appropriate thread, or explicitly switch to the
-main thread.
-
-This solution example is based on the configuration available from the Visual Studio SDK
-that defines `ThreadHelper.ThrowIfNotOnUIThread()` as one which throws if the caller
-is not already on the main thread.
-
-```csharp
-private void CallVS()
-{
- ThreadHelper.ThrowIfNotOnUIThread();
- IVsSolution sln = GetIVsSolution();
- sln.SetProperty(); // This analyzer will not report warning on this invocation.
-}
-
-private async Task CallVSAsync()
-{
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- IVsSolution sln = GetIVsSolution();
- sln.SetProperty(); // This analyzer will not report warning on this invocation.
-}
-```
-
-Refer to [Asynchronous and multithreaded programming within VS using the JoinableTaskFactory](https://devblogs.microsoft.com/premier-developer/asynchronous-and-multithreaded-programming-within-vs-using-the-joinabletaskfactory/) for more info.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD010.html).
diff --git a/doc/analyzers/VSTHRD011.md b/doc/analyzers/VSTHRD011.md
index 40b7e94fe..8bd47dc63 100644
--- a/doc/analyzers/VSTHRD011.md
+++ b/doc/analyzers/VSTHRD011.md
@@ -1,57 +1 @@
-# VSTHRD011 Use `AsyncLazy`
-
-The `Lazy` type executes the value factory just once and
-the value factory inherits the context of the first one to request the
-`Lazy.Value` property's value. This can lead to deadlocks when
-the value factory attempts to switch to the main thread.
-
-## Examples of patterns that are flagged by this analyzer
-
-### Using `Lazy` where `T` is `Task`
-
-When `T` is `Task` (because the value factory is an async method),
-if the first caller had no access to the main thread, and the value factory
-requires it, it will block. If later a second caller calls the `Value` property
-and that second caller is blocking the UI thread for its result, it will deadlock.
-
-```csharp
-var lazy = new Lazy>(async delegate // analyzer flags this line
-{
- await Task.Yield();
- return 3;
-});
-
-int value = await lazy.Value;
-```
-
-### Using synchronously blocking methods in `Lazy` value factories
-
-When the value factory passed to the `Lazy` constructor calls synchronously
-blocking methods such as `JoinableTaskFactory.Run`, only the first caller
-can help any required transition to the main thread.
-
-```csharp
-var lazy = new Lazy(delegate
-{
- return joinableTaskFactory.Run(async delegate { // analyzer flags this line
- int result = await SomeAsyncMethod();
- return result + 3;
- });
-});
-
-int value = lazy.Value;
-```
-
-## Solution
-
-Use `AsyncLazy` with an async value factory:
-
-```csharp
-var lazy = new AsyncLazy(async delegate
-{
- await Task.Yield();
- return 3;
-});
-
-int value = await lazy.GetValueAsync();
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD011.html).
diff --git a/doc/analyzers/VSTHRD012.md b/doc/analyzers/VSTHRD012.md
index 31710a1cb..7c2578ea7 100644
--- a/doc/analyzers/VSTHRD012.md
+++ b/doc/analyzers/VSTHRD012.md
@@ -1,33 +1 @@
-# VSTHRD012 Provide `JoinableTaskFactory` where allowed
-
-When constructing types or calling methods that accept a `JoinableTaskFactory`
-or `JoinableTaskContext`, take the opportunity to supply one if your application
-has a main thread with a single threaded `SynchronizationContext` such as WPF or WinForms.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-void F() {
- var o = new AsyncLazy(() => Task.FromResult(1)); // analyzer flags this line
-}
-```
-
-## Solution
-
-Call the overload that accepts a `JoinableTaskFactory` or `JoinableTaskContext` instance:
-
-```csharp
-void F() {
- var o = new AsyncLazy(() => Task.FromResult(1), this.JoinableTaskFactory);
-}
-```
-
-## Suppression
-
-You can suppress the diagnostic by explicitly specifying `null` for the argument:
-
-```csharp
-void F() {
- var o = new AsyncLazy(() => Task.FromResult(1), null);
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD012.html).
diff --git a/doc/analyzers/VSTHRD100.md b/doc/analyzers/VSTHRD100.md
index b1a7278d5..25d170bfe 100644
--- a/doc/analyzers/VSTHRD100.md
+++ b/doc/analyzers/VSTHRD100.md
@@ -1,56 +1 @@
-# VSTHRD100 Avoid `async void` methods
-
-Methods with `async void` signatures make it impossible for their caller to track
-the entire asynchronous operation and handle exceptions that may be thrown by that method.
-If the method throws an exception, it crashes the process.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-async void DoSomethingAsync()
-{
- await SomethingElseAsync();
-}
-```
-
-## Solution
-
-Change the method to return `Task` instead of `void`.
-
-```csharp
-async Task DoSomethingAsync()
-{
- await SomethingElseAsync();
-}
-```
-
-A code fix is offered that automatically changes the return type of the method.
-
-### Event handlers
-
-For event handlers, avoid `async void` by using `RunAsync`:
-```csharp
-obj.Event += (s, e) => joinableTaskFactory.RunAsync(() => OnEventAsync(s, e));
-}
-
-private async Task OnEventAsync(object sender, EventArgs e)
-{
- // async code here.
-}
-```
-
-When using method group syntax as an argument, you can define the method with the required signature, without the `async` modifier, and define an anonymous delegate or lambda within the method, like this:
-
-```cs
-var menuItem = new MenuCommand(HandleEvent, commandId);
-
-private void HandleEvent(object sender, EventArgs e)
-{
- _ = joinableTaskFactory.RunAsync(async () =>
- {
- // async code
- });
-}
-```
-
-Refer to [Async/Await - Best Practices in Asynchronous Programming](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) for more info.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD100.html).
diff --git a/doc/analyzers/VSTHRD101.md b/doc/analyzers/VSTHRD101.md
index 10f9e411d..1c842e532 100644
--- a/doc/analyzers/VSTHRD101.md
+++ b/doc/analyzers/VSTHRD101.md
@@ -1,71 +1 @@
-# VSTHRD101 Avoid unsupported async delegates
-
-C# allows you to define async delegates or lambdas and use them in contexts that accept
-void-returning delegates, thus creating an `async void` method such as is forbidden by
-[VSTHRD100](VSTHRD100.md), but is much harder to catch when simply looking at the code
-because for the same syntax, the C# compiler will create an `async Func` delegate
-or an `async void` delegate based on the type expected by the method being invoked.
-
-This analyzer helps prevent inadvertent creation of `async void` delegates.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-void StartWatching(ObservableCollection oc)
-{
- // This delegate becomes an "async void" method to match the EventHandler delegate type.
- oc.CollectionChanged += async () =>
- {
- await Task.Yield();
- };
-}
-
-void StartWatching(ObservableCollection oc)
-{
- // This delegate becomes an "async void" method to match the Action delegate type.
- Callback(async () =>
- {
- await Task.Yield();
- });
-}
-
-void Callback(Action action)
-{
- // out of scope of sample
-}
-```
-
-## Solution
-
-1. Wrap the asynchronous behavior in another method that accepts a `Func` delegate.
-1. Change the receiving method's expected delegate type to one that returns a `Task` or `Task`.
-1. Implement the delegate synchronously.
-
-```csharp
-void StartWatching(ObservableCollection oc)
-{
- oc.CollectionChanged += () =>
- {
- // The outer delegate is synchronous, but kicks off async work via a method that accepts an async delegate.
- joinableTaskFactory.RunAsync(async delegate {
- await Task.Yield();
- });
- };
-}
-
-void StartWatching(ObservableCollection oc)
-{
- // This delegate becomes an "async Task" method to match the Func delegate type.
- Callback(async () =>
- {
- await Task.Yield();
- });
-}
-
-void Callback(Func action)
-{
- // out of scope of sample
-}
-```
-
-Refer to [Async/Await - Best Practices in Asynchronous Programming](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) for more info.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD101.html).
diff --git a/doc/analyzers/VSTHRD102.md b/doc/analyzers/VSTHRD102.md
index 60ba1cb71..4d83049f2 100644
--- a/doc/analyzers/VSTHRD102.md
+++ b/doc/analyzers/VSTHRD102.md
@@ -1,56 +1 @@
-# VSTHRD102 Implement internal logic asynchronously
-
-Internal or private methods may be invoked by public methods that are asynchronous.
-If the internal method has an opportunity to do work asynchronously, it should do so
-in order that async public members can truly be async.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-public void PublicMethod()
-{
- DoWork();
-}
-
-public async Task PublicMethodAsync()
-{
- DoWork();
- await Task.Yield();
-}
-
-internal void DoWork()
-{
- joinableTaskFactory.Run(async delegate // Analyzer will flag this line
- {
- await DoSomethingAsync();
- });
-}
-```
-
-Note how `DoWork()` synchronously blocks for both `PublicMethod()` and `PublicMethodAsync()`.
-
-## Solution
-
-Remove the synchronously blocking behavior and make the method async.
-
-```csharp
-public void PublicMethod()
-{
- joinableTaskFactory.Run(() => PublicMethodAsync());
-}
-
-public async Task PublicMethodAsync()
-{
- await DoWorkAsync();
- await Task.Yield();
-}
-
-internal async Task DoWorkAsync()
-{
- await DoSomethingAsync();
-}
-```
-
-Note how `DoWorkAsync()` now allows `PublicMethodAsync()` to do its work asynchronously
-while `PublicMethod()` continues to synchronously block, giving your external caller the option
-as to whether to do work asynchronously or synchronously.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD102.html).
diff --git a/doc/analyzers/VSTHRD103.md b/doc/analyzers/VSTHRD103.md
index 348032057..6a94121a9 100644
--- a/doc/analyzers/VSTHRD103.md
+++ b/doc/analyzers/VSTHRD103.md
@@ -1,29 +1 @@
-# VSTHRD103 Call async methods when in an async method
-
-In a method which is already asynchronous, calls to other methods should
-be to their async versions, where they exist.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-Task DoAsync()
-{
- file.Read(buffer, 0, 10);
-}
-```
-
-All methods where an Async-suffixed equivalent exists will produce this warning
-when called from a `Task`-returning method.
-In addition, calling `Task.Wait()`, `Task.Result` or `Task.GetAwaiter().GetResult()`
-will produce this warning.
-
-## Solution
-
-Await the async version of the method:
-
-```csharp
-async Task DoAsync()
-{
- await file.ReadAsync(buffer, 0, 10);
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD103.html).
diff --git a/doc/analyzers/VSTHRD104.md b/doc/analyzers/VSTHRD104.md
index aabbe5722..41e426712 100644
--- a/doc/analyzers/VSTHRD104.md
+++ b/doc/analyzers/VSTHRD104.md
@@ -1,36 +1 @@
-# VSTHRD104 Offer async option
-
-When a publicly accessible method uses `JoinableTaskFactory.Run`, there should be
-another way to access the async behavior without synchronously blocking the thread
-so that an async caller can be async throughout.
-
-This rule encourages this pattern by recognizing when some method *Foo* exists and
-calls `JoinableTaskFactory.Run` that there is also a method *FooAsync*.
-The recommended pattern then is for *Foo* to call *FooAsync* from the delegate
-passed to `JoinableTaskFactory.Run` so that the implementation only need be written once.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-public void Foo() {
- this.joinableTaskFactory.Run(async delegate {
- await Task.Yield();
- });
-}
-```
-
-## Solution
-
-Add a FooAsync method, and (optionally) call it from the Foo method:
-
-```csharp
-public void Foo() {
- this.joinableTaskFactory.Run(async delegate {
- await FooAsync();
- });
-}
-
-public async Task FooAsync() {
- await Task.Yield();
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD104.html).
diff --git a/doc/analyzers/VSTHRD105.md b/doc/analyzers/VSTHRD105.md
index 06c3e34a4..62c8b7a7e 100644
--- a/doc/analyzers/VSTHRD105.md
+++ b/doc/analyzers/VSTHRD105.md
@@ -1,83 +1 @@
-# VSTHRD105 Avoid method overloads that assume `TaskScheduler.Current`
-
-Certain methods in the .NET Framework have overloads that allow specifying or omitting
-a `TaskScheduler` instance. Always specify one explicitly to avoid the assumed `TaskScheduler.Current`
-value, whose behavior is defined by your caller and may vary at runtime.
-
-The "current" `TaskScheduler` is defined by the one that is executing the currently running code.
-But when your code is executing without having been scheduled by a `TaskScheduler` (as is the case with most code),
-then the `TaskScheduler.Current` property returns `TaskScheduler.Default` which schedules tasks on the thread pool.
-This leads many to incorrectly assume that task scheduling methods such as `StartNew` and `ContinueWith` default
-to using the thread pool when in fact their default behavior varies by your caller.
-
-This variability in behavior leads to bugs when, for example, `TaskScheduler.Current` returns a `TaskScheduler`
-that executes tasks on the application's main thread and/or only executes one task at once, such as one obtained
-from the `TaskScheduler.FromCurrentSynchronizationContext()` method.
-Such a circumstance often leads to deadlocks or responsiveness issues in the application.
-
-Always explicitly specifying `TaskScheduler.Default` (or other if appropriate) ensures your code will schedule
-tasks in a predictable, consistent way.
-
-No diagnostic is produced by this analyzer when `TaskFactory.StartNew` is invoked on a private instance
-of `TaskFactory`, since it may in fact have a safe default for `TaskScheduler`.
-
-Similar rules: [CA2008 (DoNotCreateTasksWithoutPassingATaskSchedulerAnalyzer)](https://github.com/dotnet/roslyn-analyzers/blob/32d8f1e397439035f0ecb5f61a9e672225f0ecdb/src/Microsoft.NetCore.Analyzers/Core/Tasks/DoNotCreateTasksWithoutPassingATaskScheduler.cs)
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-private void FirstMethod()
-{
- TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
- Task.Factory.StartNew(
- () =>
- {
- this.AnotherMethod();
- },
- System.Threading.CancellationToken.None,
- TaskCreationOptions.None,
- uiScheduler);
-}
-
-private void AnotherMethod()
-{
- // TaskScheduler.Current is assumed here, which is determined by our caller.
- var nestedTask = Task.Factory.StartNew( // analyzer flags this line
- () =>
- {
- // Ooops, we're still on the UI thread when called by FirstMethod.
- // But we might be on the thread pool if someone else called us.
- });
-}
-```
-
-## Solution
-
-Specify a `TaskScheduler` explicitly to suppress the warning:
-
-```csharp
-private void FirstMethod()
-{
- TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
- Task.Factory.StartNew(
- () =>
- {
- this.AnotherMethod();
- },
- CancellationToken.None,
- TaskCreationOptions.None,
- uiScheduler);
-}
-
-private void AnotherMethod()
-{
- var nestedTask = Task.Factory.StartNew(
- () =>
- {
- // Ah, now we're reliably running on the thread pool. :)
- },
- CancellationToken.None,
- TaskCreationOptions.None,
- TaskScheduler.Default); // Specify TaskScheduler explicitly here.
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD105.html).
diff --git a/doc/analyzers/VSTHRD106.md b/doc/analyzers/VSTHRD106.md
index f47fc46ce..8a34ed3aa 100644
--- a/doc/analyzers/VSTHRD106.md
+++ b/doc/analyzers/VSTHRD106.md
@@ -1,31 +1 @@
-# VSTHRD106 Use `InvokeAsync` to raise async events
-
-Asynchronous events (those typed as `AsyncEventHandler`) must be raised carefully to ensure
-all event handlers are invoked and awaited on.
-
-Although C# lets you invoke event handlers naturally, it has no awareness of async event handlers
-and thus will not let you correctly await on their invocation nor invoke them sequentially.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-public AsyncEventHandler Clicked;
-
-async Task OnClicked() {
- await Clicked(this, EventArgs.Empty); // only awaits the first event handler.
-}
-```
-
-## Solution
-
-Use the `InvokeAsync` extension method defined in the `TplExtensions` class and await its result.
-This will ensure each event handler completes before invoking the next event handler in the list,
-similar to the default behavior for raising synchronous events.
-
-```csharp
-public AsyncEventHandler Clicked;
-
-async Task OnClicked() {
- await Clicked.InvokeAsync(this, EventArgs.Empty); // await for the completion of all handlers.
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD106.html).
diff --git a/doc/analyzers/VSTHRD107.md b/doc/analyzers/VSTHRD107.md
index 8694cf2ea..76dab4581 100644
--- a/doc/analyzers/VSTHRD107.md
+++ b/doc/analyzers/VSTHRD107.md
@@ -1,27 +1 @@
-# VSTHRD107 Await Task within using expression
-
-The C# `using` statement requires that the used expression implement `IDisposable`.
-Because `Task` implements `IDisposable`, one may accidentally omit an `await` operator
-and `Dispose` of the `Task` instead of the `T` result itself when `T` derives from `IDisposable`.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-AsyncSemaphore lck;
-using (lck.EnterAsync())
-{
- // ...
-}
-```
-
-## Solution
-
-Add the `await` operator within the `using` expression.
-
-```csharp
-AsyncSemaphore lck;
-using (await lck.EnterAsync())
-{
- // ...
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD107.html).
diff --git a/doc/analyzers/VSTHRD108.md b/doc/analyzers/VSTHRD108.md
index c2b986e2a..9771719f3 100644
--- a/doc/analyzers/VSTHRD108.md
+++ b/doc/analyzers/VSTHRD108.md
@@ -1,46 +1 @@
-# VSTHRD108 Assert thread affinity unconditionally
-
-When a method has thread affinity and throws if called from the wrong thread, it should do so without regard to any other condition. This helps ensure the caller will notice early during development that they are calling from the wrong thread. Extra conditions can hide the problem till end users discover an application failure.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-private int? age;
-
-public int GetAge()
-{
- if (!this.age.HasValue)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- this.age = DoExpensiveUIThreadWork();
- }
-
- return this.age.Value;
-}
-```
-
-The problem here is that although the UI thread is only strictly required when the field is actually initialized, callers generally cannot predict whether they will be the first or a subsequent caller. If they call from a background thread and tend to be a subsequent caller, no exception will be thrown. But under some conditions in the app when they happen to be the first caller, they'll fail at runtime because they're calling from the background thread.
-
-## Solution
-
-Move the code that throws when not on the UI thread outside the conditional block.
-
-```csharp
-private int? age;
-
-public int GetAge()
-{
- ThreadHelper.ThrowIfNotOnUIThread();
- if (!this.age.HasValue)
- {
- this.age = DoExpensiveUIThreadWork();
- }
-
- return this.age.Value;
-}
-```
-
-## Configuration
-
-This analyzer is configurable via the `vs-threading.MainThreadAssertingMethods.txt` file.
-See our [configuration](configuration.md) topic for more information.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD108.html).
diff --git a/doc/analyzers/VSTHRD109.md b/doc/analyzers/VSTHRD109.md
index 7e28198b7..d0792db4a 100644
--- a/doc/analyzers/VSTHRD109.md
+++ b/doc/analyzers/VSTHRD109.md
@@ -1,29 +1 @@
-# VSTHRD109 Switch instead of assert in async methods
-
-Methods that are or can be async should switch to the main thread when necessary
-rather than throw an exception if invoked from a different thread.
-This allows callers to invoke any async method from any thread
-without having to concern themselves with the threading requirements of a method that
-can support its own threading requirements by switching.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-async Task FooAsync() {
- ThreadHelper.ThrowIfNotOnUIThread();
- DoStuff();
- await DoMoreStuff();
-}
-```
-
-## Solution
-
-Use `await SwitchToMainThreadAsync()` instead:
-
-```csharp
-async Task FooAsync() {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- DoStuff();
- await DoMoreStuff();
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD109.html).
diff --git a/doc/analyzers/VSTHRD110.md b/doc/analyzers/VSTHRD110.md
index 7aa764f52..42bb17ab1 100644
--- a/doc/analyzers/VSTHRD110.md
+++ b/doc/analyzers/VSTHRD110.md
@@ -1,75 +1 @@
-# VSTHRD110 Observe result of async calls
-
-Tasks returned from async methods should be awaited, or assigned to a variable for observation later.
-Methods that return `Task`s often complete and report their work via the `Task` they return, and simply
-invoking the method does not guarantee that its work is complete nor successful. Using the `await` keyword
-just before the method call causes execution of the calling method to effectively suspend until the called
-method has completed and rethrows any exception thrown by the method.
-
-When a `Task` or `Task` is returned and is not awaited or redirected in some other way,
-within the context of a synchronous method, a warning is reported.
-
-This rule does *not* apply to calls made within async methods, since [CS4014][CS4014] already reports these.
-
-## Examples of patterns that are flagged by this analyzer
-
-```csharp
-void Foo() {
- DoStuffAsync();
-}
-
-async Task DoStuffAsync() { /* ... */ }
-```
-
-## Solution
-
-Convert the method to be async and await the expression:
-
-```csharp
-async Task FooAsync() {
- await DoStuffAsync();
-}
-
-async Task DoStuffAsync() { /* ... */ }
-```
-
-When the calling method's signature cannot be changed, wrap the method body in a `JoinableTaskFactory.Run` delegate instead:
-
-```csharp
-void Foo() {
- jtf.Run(async delegate {
- await DoStuffAsync();
- });
-}
-
-async Task DoStuffAsync() { /* ... */ }
-```
-
-One other option is to assign the result of the method call to a field or local variable, presumably to track it later:
-
-```csharp
-void Foo() {
- Task watchThis = DoStuffAsync();
-}
-
-async Task DoStuffAsync() { /* ... */ }
-```
-
-When tracking the `Task` with a field, remember that to await it later without risk of deadlocking,
-wrap it in a `JoinableTask` using `JoinableTaskFactory.RunAsync`, per [the 3rd rule](../threading_rules.md#Rule3).
-
-```csharp
-JoinableTask watchThis;
-
-void Foo() {
- this.watchThis = jtf.RunAsync(() => DoStuffAsync());
-}
-
-async Task WaitForFooToFinishAsync() {
- await this.watchThis;
-}
-
-async Task DoStuffAsync() { /* ... */ }
-```
-
-[CS4014]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs4014
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD110.html).
diff --git a/doc/analyzers/VSTHRD111.md b/doc/analyzers/VSTHRD111.md
index 0495e84f0..7cd14fe7a 100644
--- a/doc/analyzers/VSTHRD111.md
+++ b/doc/analyzers/VSTHRD111.md
@@ -1,38 +1 @@
-# VSTHRD111 Use `.ConfigureAwait(bool)`
-
-Some code bases, particularly libraries with no affinity to an app's UI thread, are advised to use `.ConfigureAwait(false)` for each and every _await_ because it can avoid deadlocks after those calls start on an application's UI thread and the app later decides to synchronously block the UI thread waiting for those tasks to finish. Using `.ConfigureAwait(false)` also allows continuations to switch to a background thread even when no synchronous blocking would cause a deadlock, which makes for a more responsive application and possibly higher throughput of async operations.
-
-Note that this scenario can also be solved using the `JoinableTaskFactory`, but many class libraries may not wish to depend on the application proffers an instance of that type to the library. Where JoinableTaskFactory _does_ apply, use of `.ConfigureAwait(false)` is _not_ recommended. See [this topic](https://github.com/Microsoft/vs-threading/blob/main/doc/cookbook_vs.md#should-i-await-a-task-with-configureawaitfalse) for more on when `.ConfigureAwait(false)` and `.ConfigureAwait(true)` are appropriate.
-
-**This analyzer's diagnostics are *hidden* by default**. You should enable the rule for libraries that use to require this await suffix.
-
-## Examples of patterns that are flagged by this analyzer
-
-Any await on `Task` or `ValueTask` without the `.ConfigureAwait(bool)` method called on it will be flagged.
-
-```csharp
-async Task FooAsync() {
- await DoStuffAsync(); // This line is flagged
- await DoMoreStuffAsync(); // This line is flagged
-}
-
-async Task DoStuffAsync() { /* ... */ }
-async ValueTask DoMoreStuffAsync() { /* ... */ }
-```
-
-## Solution
-
-Add `.ConfigureAwait(false)` or `.ConfigureAwait(true)` to the awaited `Task` or `ValueTask`.
-
-```csharp
-async Task FooAsync() {
- await DoStuffAsync().ConfigureAwait(true);
- await DoMoreStuffAsync().ConfigureAwait(false);
-}
-
-async Task DoStuffAsync() { /* ... */ }
-async ValueTask DoMoreStuffAsync() { /* ... */ }
-```
-
-Code fixes are offered for for this diagnostic to add either `.ConfigureAwait(false)` or `.ConfigureAwait(true)`
-to an awaited expression.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD111.html).
diff --git a/doc/analyzers/VSTHRD112.md b/doc/analyzers/VSTHRD112.md
index b9e3ae45c..edc8f6772 100644
--- a/doc/analyzers/VSTHRD112.md
+++ b/doc/analyzers/VSTHRD112.md
@@ -1,66 +1 @@
-# VSTHRD112 Implement `System.IAsyncDisposable`
-
-The `Microsoft.VisualStudio.Threading.IAsyncDisposable` interface is obsolete now that the
-`System.IAsyncDisposable` interface has been defined for .NET Standard 2.0 and .NET Framework 4.6.1
-by the [`Microsoft.Bcl.AsyncInterfaces` NuGet package](https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces).
-
-New classes looking to support async disposable should use `System.IAsyncDisposable` instead of `Microsoft.VisualStudio.Threading.IAsyncDisposable`.
-Existing classes that already implement `Microsoft.VisualStudio.Threading.IAsyncDisposable` should *also* implement `System.IAsyncDisposable` so the async disposal option will be recognized by code that only checks for presence of the new interface.
-
-## Examples of patterns that are flagged by this analyzer
-
-This class only implements `Microsoft.VisualStudio.Threading.IAsyncDisposable` and will produce the VSTHRD112 diagnostic:
-
-```cs
-using Microsoft.VisualStudio.Threading;
-
-class SomeClass : IAsyncDisposable
-{
- public Task DisposeAsync()
- {
- }
-}
-```
-
-## Solution
-
-Implement `System.IAsyncDisposable` in addition to (or instead of) `Microsoft.VisualStudio.Threading.IAsyncDisposable`.
-Add a package reference to `Microsoft.Bcl.AsyncInterfaces` if the compiler cannot find `System.IAsyncDisposable`.
-
-In this example, only `System.IAsyncDisposable` is supported, which is acceptable:
-
-```cs
-using System;
-
-class SomeClass : IAsyncDisposable
-{
- public ValueTask DisposeAsync()
- {
- }
-}
-```
-
-In this next example, both interfaces are supported:
-
-```cs
-class SomeClass : System.IAsyncDisposable, Microsoft.VisualStudio.Threading.IAsyncDisposable
-{
- Task Microsoft.VisualStudio.Threading.IAsyncDisposable.DisposeAsync()
- {
- // Simply forward the call to the other DisposeAsync overload.
- System.IAsyncDisposable self = this;
- return self.DisposeAsync().AsTask();
- }
-
- ValueTask System.IAsyncDisposable.DisposeAsync()
- {
- // Interesting dispose logic here.
- }
-}
-```
-
-In the above example both `DisposeAsync` methods are explicit interface implementations.
-Promoting one of the methods to be `public` is typically advised.
-If one of these methods was already public and the class itself is public or protected, keep the same method public to avoid an API binary breaking change.
-
-An automated code fix may be offered for VSTHRD112 diagnostics.
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD112.html).
diff --git a/doc/analyzers/VSTHRD113.md b/doc/analyzers/VSTHRD113.md
index 8d4dee677..547e22219 100644
--- a/doc/analyzers/VSTHRD113.md
+++ b/doc/analyzers/VSTHRD113.md
@@ -1,37 +1 @@
-# VSTHRD113 Check for `System.IAsyncDisposable`
-
-The `Microsoft.VisualStudio.Threading.IAsyncDisposable` interface is obsolete now that the
-`System.IAsyncDisposable` interface has been defined for .NET Standard 2.0 and .NET Framework 4.6.1
-by the [`Microsoft.Bcl.AsyncInterfaces` NuGet package](https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces).
-
-Existing code that tests for the `Microsoft.VisualStudio.Threading.IAsyncDisposable` interface on some object should also check for `System.IAsyncDisposable` and behave similarly in either case.
-New code should consider only supporting the new `System.IAsyncDisposable` interface.
-
-## Examples of patterns that are flagged by this analyzer
-
-The following code only checks for the obsolete interface and is flagged by this diagnostic:
-
-```cs
-using Microsoft.VisualStudio.Threading;
-
-if (obj is IAsyncDisposable asyncDisposable)
-{
- await asyncDisposable.DisposeAsync();
-}
-```
-
-## Solution
-
-Fix this by adding a code branch for the new interface that behaves similarly
-within the same containing code block:
-
-```cs
-if (obj is Microsoft.VisualStudio.Threading.IAsyncDisposable vsThreadingAsyncDisposable)
-{
- await vsThreadingAsyncDisposable.DisposeAsync();
-}
-else if (obj is System.IAsyncDisposable bclAsyncDisposable)
-{
- await bclAsyncDisposable.DisposeAsync();
-}
-```
+This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD113.html).
diff --git a/doc/analyzers/VSTHRD114.md b/doc/analyzers/VSTHRD114.md
index c20c3fb53..d7822f8ca 100644
--- a/doc/analyzers/VSTHRD114.md
+++ b/doc/analyzers/VSTHRD114.md
@@ -1,31 +1 @@
-# VSTHRD114 Avoid returning a null Task
-
-Returning `null` from a non-async `Task`/`Task` method will cause a `NullReferenceException` at runtime. This problem can be avoided by returning `Task.CompletedTask`, `Task.FromResult(null)` or `Task.FromResult(default(T))` instead.
-
-## Examples of patterns that are flagged by this analyzer
-
-Any non-async `Task` returning method with an explicit `return null;` will be flagged.
-
-```csharp
-Task DoAsync() {
- return null;
-}
-
-Task