Created
November 14, 2016 13:48
-
-
Save tocsoft/afc9b5be41722f978ff0414d33afb1f1 to your computer and use it in GitHub Desktop.
VS extension threading helpers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Threads : IThreads | |
{ | |
public bool DisableThreadSwitching { get; set; } = false; | |
/// <summary> | |
/// await this and continuing code will run on a background thread | |
/// </summary> | |
/// <returns></returns> | |
public IThreadSwitcher SwitchToBackgroundThread() | |
{ | |
if (DisableThreadSwitching) | |
{ | |
return new NoOpThreadSwitcher(); | |
} | |
return new TaskSchedulerThreadSwitcher(TaskScheduler.Default); | |
} | |
/// <summary> | |
/// await this and continuing code will run on UI thread | |
/// </summary> | |
/// <returns></returns> | |
public IThreadSwitcher SwitchToUIThread() | |
{ | |
if (!DisableThreadSwitching) | |
{ | |
if (Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory != null) | |
{ | |
return new MainThreadAwaitableThreadSwitcher(Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory); | |
} | |
} | |
return new NoOpThreadSwitcher(); | |
} | |
/// <summary> | |
/// minimum api for calling 'await' on a class | |
/// </summary> | |
public interface IThreadSwitcher | |
{ | |
ICompletable GetAwaiter(); | |
} | |
/// <summary> | |
/// the minimum api required for returning from a GetAwaiter | |
/// </summary> | |
public interface ICompletable : INotifyCompletion | |
{ | |
bool IsCompleted { get; } | |
void GetResult(); | |
} | |
/// <summary> | |
/// forces awaiting onto the Main VS thread wraps VS ThreadHelper so it can be switched out with a noop version for testing | |
/// </summary> | |
public struct MainThreadAwaitableThreadSwitcher : IThreadSwitcher | |
{ | |
private readonly JoinableTaskFactory taskFactory; | |
public MainThreadAwaitableThreadSwitcher(JoinableTaskFactory taskFactory) | |
{ | |
this.taskFactory = taskFactory; | |
} | |
public ICompletable GetAwaiter() | |
{ | |
var awaitable = taskFactory.SwitchToMainThreadAsync(); | |
return new MainThreadAwaitableAwaiter(awaitable.GetAwaiter()); | |
} | |
public struct MainThreadAwaitableAwaiter : ICompletable | |
{ | |
private readonly JoinableTaskFactory.MainThreadAwaiter awaiter; | |
public MainThreadAwaitableAwaiter(JoinableTaskFactory.MainThreadAwaiter awaiter) | |
{ | |
this.awaiter = awaiter; | |
} | |
public bool IsCompleted | |
{ | |
get | |
{ | |
return awaiter.IsCompleted; | |
} | |
} | |
public void OnCompleted(Action continuation) | |
{ | |
awaiter.OnCompleted(continuation); | |
} | |
public void GetResult() | |
{ | |
awaiter.GetResult(); | |
} | |
} | |
} | |
/// <summary> | |
/// Fakes a thread switch, continues int sync on current thread (used for unit testing to force a sync workflow) | |
/// </summary> | |
public struct NoOpThreadSwitcher : IThreadSwitcher | |
{ | |
public ICompletable GetAwaiter() | |
{ | |
return new NoOpThreadAwaiter(); | |
} | |
public struct NoOpThreadAwaiter : ICompletable | |
{ | |
public bool IsCompleted | |
{ | |
get | |
{ | |
return true; | |
} | |
} | |
public void OnCompleted(Action continuation) | |
{ | |
continuation(); | |
} | |
public void GetResult() | |
{ | |
} | |
} | |
} | |
/// <summary> | |
/// Forces the awaiting code to continue on the TaskScheduler | |
/// </summary> | |
public struct TaskSchedulerThreadSwitcher : IThreadSwitcher | |
{ | |
private TaskScheduler scheduler; | |
public TaskSchedulerThreadSwitcher(TaskScheduler scheduler) | |
{ | |
this.scheduler = scheduler; | |
} | |
public ICompletable GetAwaiter() | |
{ | |
return new TaskSchedulerAwaiter(scheduler); | |
} | |
public struct TaskSchedulerAwaiter : ICompletable | |
{ | |
private readonly TaskScheduler scheduler; | |
public bool IsCompleted | |
{ | |
get | |
{ | |
bool isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; | |
return (scheduler == TaskScheduler.Default & isThreadPoolThread) || (scheduler == TaskScheduler.Current && TaskScheduler.Current != TaskScheduler.Default); | |
} | |
} | |
public TaskSchedulerAwaiter(TaskScheduler scheduler) | |
{ | |
this.scheduler = scheduler; | |
} | |
public void OnCompleted(Action continuation) | |
{ | |
Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.None, scheduler); | |
} | |
public void GetResult() | |
{ | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment