Skip to content

Instantly share code, notes, and snippets.

@tocsoft
Created November 14, 2016 13:48
Show Gist options
  • Save tocsoft/afc9b5be41722f978ff0414d33afb1f1 to your computer and use it in GitHub Desktop.
Save tocsoft/afc9b5be41722f978ff0414d33afb1f1 to your computer and use it in GitHub Desktop.
VS extension threading helpers
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