Table of Contents

Class JoinableTaskFactory

Namespace
Microsoft.VisualStudio.Threading
Assembly
Microsoft.VisualStudio.Threading.dll

A factory for starting asynchronous tasks that can mitigate deadlocks when the tasks require the Main thread of an application and the Main thread may itself be blocking on the completion of a task.

public class JoinableTaskFactory
Inheritance
JoinableTaskFactory
Derived
Inherited Members

Remarks

For more complete comments please see the JoinableTaskContext.

Constructors

JoinableTaskFactory(JoinableTaskCollection)

Initializes a new instance of the JoinableTaskFactory class that adds all generated jobs to the specified collection.

public JoinableTaskFactory(JoinableTaskCollection collection)

Parameters

collection JoinableTaskCollection

The collection that all tasks created by this factory will belong to till they complete.

JoinableTaskFactory(JoinableTaskContext)

Initializes a new instance of the JoinableTaskFactory class.

public JoinableTaskFactory(JoinableTaskContext owner)

Parameters

owner JoinableTaskContext

The context for the tasks created by this factory.

Properties

Context

Gets the joinable task context to which this factory belongs.

public JoinableTaskContext Context { get; }

Property Value

JoinableTaskContext

HangDetectionTimeout

Gets or sets the timeout after which no activity while synchronously blocking suggests a hang has occurred.

protected TimeSpan HangDetectionTimeout { get; set; }

Property Value

TimeSpan

UnderlyingSynchronizationContext

Gets the underlying SynchronizationContext that controls the main thread in the host.

protected SynchronizationContext? UnderlyingSynchronizationContext { get; }

Property Value

SynchronizationContext

Methods

Add(JoinableTask)

Adds the specified joinable task to the applicable collection.

protected void Add(JoinableTask joinable)

Parameters

joinable JoinableTask

IsWaitingOnLongRunningTask()

Check whether the current joinableTask is waiting on a long running task.

protected bool IsWaitingOnLongRunningTask()

Returns

bool

Return true if the current synchronous task on the thread is waiting on a long running task.

OnTransitionedToMainThread(JoinableTask, bool)

Raised whenever a joinable task has completed a transition to the main thread.

protected virtual void OnTransitionedToMainThread(JoinableTask joinableTask, bool canceled)

Parameters

joinableTask JoinableTask

The task whose request to transition to the main thread has completed.

canceled bool

A value indicating whether the transition was cancelled before it was fulfilled.

Remarks

This event is usually raised on the main thread, but can be on another thread when canceled is true.

OnTransitioningToMainThread(JoinableTask)

Raised when a joinable task has requested a transition to the main thread.

protected virtual void OnTransitioningToMainThread(JoinableTask joinableTask)

Parameters

joinableTask JoinableTask

The task requesting the transition to the main thread.

Remarks

This event may be raised on any thread, including the main thread.

PostToUnderlyingSynchronizationContext(SendOrPostCallback, object)

Posts a message to the specified underlying SynchronizationContext for processing when the main thread is freely available.

protected virtual void PostToUnderlyingSynchronizationContext(SendOrPostCallback callback, object state)

Parameters

callback SendOrPostCallback

The callback to invoke.

state object

State to pass to the callback.

Run(Func<Task>)

Runs the specified asynchronous method to completion while synchronously blocking the calling thread.

public void Run(Func<Task> asyncMethod)

Parameters

asyncMethod Func<Task>

The asynchronous method to execute.

Remarks

Any exception thrown by the delegate is rethrown in its original type to the caller of this method.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

// On threadpool or Main thread, this method will block
// the calling thread until all async operations in the
// delegate complete.
joinableTaskFactory.Run(async delegate {
    // still on the threadpool or Main thread as before.
    await OperationAsync();
    // still on the threadpool or Main thread as before.
    await Task.Run(async delegate {
         // Now we're on a threadpool thread.
         await Task.Yield();
         // still on a threadpool thread.
    });
    // Now back on the Main thread (or threadpool thread if that's where we started).
});

Run(Func<Task>, JoinableTaskCreationOptions)

Runs the specified asynchronous method to completion while synchronously blocking the calling thread.

public void Run(Func<Task> asyncMethod, JoinableTaskCreationOptions creationOptions)

Parameters

asyncMethod Func<Task>

The asynchronous method to execute.

creationOptions JoinableTaskCreationOptions

The JoinableTaskCreationOptions used to customize the task's behavior.

Remarks

Any exception thrown by the delegate is rethrown in its original type to the caller of this method.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

// On threadpool or Main thread, this method will block
// the calling thread until all async operations in the
// delegate complete.
joinableTaskFactory.Run(async delegate {
    // still on the threadpool or Main thread as before.
    await OperationAsync();
    // still on the threadpool or Main thread as before.
    await Task.Run(async delegate {
         // Now we're on a threadpool thread.
         await Task.Yield();
         // still on a threadpool thread.
    });
    // Now back on the Main thread (or threadpool thread if that's where we started).
});

RunAsync(Func<Task>)

Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method requires the main thread while the main thread is blocked waiting for the async method's completion.

public JoinableTask RunAsync(Func<Task> asyncMethod)

Parameters

asyncMethod Func<Task>

The method that, when executed, will begin the async operation.

Returns

JoinableTask

An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary.

Remarks

Exceptions thrown by the delegate are captured by the returned JoinableTask.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

RunAsync(Func<Task>, JoinableTaskCreationOptions)

Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method requires the main thread while the main thread is blocked waiting for the async method's completion.

public JoinableTask RunAsync(Func<Task> asyncMethod, JoinableTaskCreationOptions creationOptions)

Parameters

asyncMethod Func<Task>

The method that, when executed, will begin the async operation.

creationOptions JoinableTaskCreationOptions

The JoinableTaskCreationOptions used to customize the task's behavior.

Returns

JoinableTask

An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary.

Remarks

Exceptions thrown by the delegate are captured by the returned JoinableTask.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

RunAsync(Func<Task>, string?, JoinableTaskCreationOptions)

Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method requires the main thread while the main thread is blocked waiting for the async method's completion.

public JoinableTask RunAsync(Func<Task> asyncMethod, string? parentToken, JoinableTaskCreationOptions creationOptions)

Parameters

asyncMethod Func<Task>

The method that, when executed, will begin the async operation.

parentToken string

An optional token that identifies one or more JoinableTask instances, typically in other processes, that serve as 'parents' to this one.

creationOptions JoinableTaskCreationOptions

The JoinableTaskCreationOptions used to customize the task's behavior.

Returns

JoinableTask

An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary.

Remarks

Exceptions thrown by the delegate are captured by the returned JoinableTask.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

RunAsync<T>(Func<Task<T>>)

Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method requires the main thread while the main thread is blocked waiting for the async method's completion.

public JoinableTask<T> RunAsync<T>(Func<Task<T>> asyncMethod)

Parameters

asyncMethod Func<Task<T>>

The method that, when executed, will begin the async operation.

Returns

JoinableTask<T>

An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary.

Type Parameters

T

The type of value returned by the asynchronous operation.

Remarks

Exceptions thrown by the delegate are captured by the returned JoinableTask.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

RunAsync<T>(Func<Task<T>>, JoinableTaskCreationOptions)

Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method requires the main thread while the main thread is blocked waiting for the async method's completion.

public JoinableTask<T> RunAsync<T>(Func<Task<T>> asyncMethod, JoinableTaskCreationOptions creationOptions)

Parameters

asyncMethod Func<Task<T>>

The method that, when executed, will begin the async operation.

creationOptions JoinableTaskCreationOptions

The JoinableTaskCreationOptions used to customize the task's behavior.

Returns

JoinableTask<T>

An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary.

Type Parameters

T

The type of value returned by the asynchronous operation.

Remarks

Exceptions thrown by the delegate are captured by the returned JoinableTask.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

RunAsync<T>(Func<Task<T>>, string?, JoinableTaskCreationOptions)

Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method requires the main thread while the main thread is blocked waiting for the async method's completion.

public JoinableTask<T> RunAsync<T>(Func<Task<T>> asyncMethod, string? parentToken, JoinableTaskCreationOptions creationOptions)

Parameters

asyncMethod Func<Task<T>>

The method that, when executed, will begin the async operation.

parentToken string

An optional token that identifies one or more JoinableTask instances, typically in other processes, that serve as 'parents' to this one.

creationOptions JoinableTaskCreationOptions

The JoinableTaskCreationOptions used to customize the task's behavior.

Returns

JoinableTask<T>

An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary.

Type Parameters

T

The type of value returned by the asynchronous operation.

Remarks

Exceptions thrown by the delegate are captured by the returned JoinableTask.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

Run<T>(Func<Task<T>>)

Runs the specified asynchronous method to completion while synchronously blocking the calling thread.

public T Run<T>(Func<Task<T>> asyncMethod)

Parameters

asyncMethod Func<Task<T>>

The asynchronous method to execute.

Returns

T

The result of the Task returned by asyncMethod.

Type Parameters

T

The type of value returned by the asynchronous operation.

Remarks

Any exception thrown by the delegate is rethrown in its original type to the caller of this method.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

// On threadpool or Main thread, this method will block
// the calling thread until all async operations in the
// delegate complete.
joinableTaskFactory.Run(async delegate {
    // still on the threadpool or Main thread as before.
    await OperationAsync();
    // still on the threadpool or Main thread as before.
    await Task.Run(async delegate {
         // Now we're on a threadpool thread.
         await Task.Yield();
         // still on a threadpool thread.
    });
    // Now back on the Main thread (or threadpool thread if that's where we started).
});

Run<T>(Func<Task<T>>, JoinableTaskCreationOptions)

Runs the specified asynchronous method to completion while synchronously blocking the calling thread.

public T Run<T>(Func<Task<T>> asyncMethod, JoinableTaskCreationOptions creationOptions)

Parameters

asyncMethod Func<Task<T>>

The asynchronous method to execute.

creationOptions JoinableTaskCreationOptions

The JoinableTaskCreationOptions used to customize the task's behavior.

Returns

T

The result of the Task returned by asyncMethod.

Type Parameters

T

The type of value returned by the asynchronous operation.

Remarks

Any exception thrown by the delegate is rethrown in its original type to the caller of this method.

When the delegate resumes from a yielding await, the default behavior is to resume in its original context as an ordinary async method execution would. For example, if the caller was on the main thread, execution resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread.

// On threadpool or Main thread, this method will block
// the calling thread until all async operations in the
// delegate complete.
joinableTaskFactory.Run(async delegate {
    // still on the threadpool or Main thread as before.
    await OperationAsync();
    // still on the threadpool or Main thread as before.
    await Task.Run(async delegate {
         // Now we're on a threadpool thread.
         await Task.Yield();
         // still on a threadpool thread.
    });
    // Now back on the Main thread (or threadpool thread if that's where we started).
});

SwitchToMainThreadAsync(bool, CancellationToken)

Gets an awaitable whose continuations execute on the synchronization context that this instance was initialized with, in such a way as to mitigate both deadlocks and reentrancy.

public JoinableTaskFactory.MainThreadAwaitable SwitchToMainThreadAsync(bool alwaysYield, CancellationToken cancellationToken = default)

Parameters

alwaysYield bool

A value indicating whether the caller should yield even if already executing on the main thread.

cancellationToken CancellationToken

A token whose cancellation will immediately schedule the continuation on a threadpool thread and will cause the continuation to throw OperationCanceledException, even if the caller is already on the main thread.

Returns

JoinableTaskFactory.MainThreadAwaitable

An awaitable.

Remarks

private async Task SomeOperationAsync()
{
    // This first part can be on the caller's thread, whatever that is.
    DoSomething();

    // Now switch to the Main thread to talk to some STA object.
    // Supposing it is also important to *not* do this step on our caller's callstack,
    // be sure we yield even if we're on the UI thread.
    await this.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true);
    STAService.DoSomething();
}

Exceptions

OperationCanceledException

Thrown back at the awaiting caller if cancellationToken is canceled, even if the caller is already on the main thread.

SwitchToMainThreadAsync(CancellationToken)

Gets an awaitable whose continuations execute on the main thread, in such a way as to mitigate both deadlocks and reentrancy.

public JoinableTaskFactory.MainThreadAwaitable SwitchToMainThreadAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

A token whose cancellation will immediately schedule the continuation on a threadpool thread and will cause the continuation to throw OperationCanceledException, even if the caller is already on the main thread.

Returns

JoinableTaskFactory.MainThreadAwaitable

An awaitable.

Remarks

private async Task SomeOperationAsync() {
    // on the caller's thread.
    await DoAsync();

    // Now switch to a threadpool thread explicitly.
    await TaskScheduler.Default;

    // Now switch to the Main thread to talk to some STA object.
    await this.JobContext.SwitchToMainThreadAsync();
    STAService.DoSomething();
}

When the owning JoinableTaskContext is created with a null SynchronizationContext, this method has no effect and the caller will continue execution on its original thread.

Exceptions

OperationCanceledException

Thrown back at the awaiting caller if cancellationToken is canceled, even if the caller is already on the main thread.

WaitSynchronously(Task)

Synchronously blocks the calling thread for the completion of the specified task. If running on the main thread, any applicable message pump is suppressed while the thread sleeps.

protected virtual void WaitSynchronously(Task task)

Parameters

task Task

The task whose completion is being waited on.

Remarks

Implementations should take care that exceptions from faulted or canceled tasks not be thrown back to the caller.

WaitSynchronouslyCore(Task)

Synchronously blocks the calling thread for the completion of the specified task.

protected virtual void WaitSynchronouslyCore(Task task)

Parameters

task Task

The task whose completion is being waited on.

Remarks

Implementations should take care that exceptions from faulted or canceled tasks not be thrown back to the caller.