// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
///
/// A customizable source of instances.
///
public class JoinableTaskContextNode
{
///
/// The inner JoinableTaskContext.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly JoinableTaskContext context;
///
/// A single joinable task factory that itself cannot be joined.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private JoinableTaskFactory? nonJoinableFactory;
///
/// Initializes a new instance of the class.
///
/// The inner JoinableTaskContext.
public JoinableTaskContextNode(JoinableTaskContext context)
{
Requires.NotNull(context, nameof(context));
this.context = context;
}
///
/// Gets the factory which creates joinable tasks
/// that do not belong to a joinable task collection.
///
public JoinableTaskFactory Factory
{
get
{
if (this.nonJoinableFactory is null)
{
JoinableTaskFactory? factory = this.CreateDefaultFactory();
Interlocked.CompareExchange(ref this.nonJoinableFactory, factory, null);
}
return this.nonJoinableFactory;
}
}
///
/// Gets the main thread that can be shared by tasks created by this context.
///
public Thread MainThread
{
get { return this.context.MainThread; }
}
///
/// Gets a value indicating whether the caller is executing on the main thread.
///
public bool IsOnMainThread => this.context.IsOnMainThread;
///
/// Gets the inner wrapped context.
///
public JoinableTaskContext Context
{
get { return this.context; }
}
///
/// Creates a joinable task factory that automatically adds all created tasks
/// to a collection that can be jointly joined.
///
/// The collection that all tasks should be added to.
public virtual JoinableTaskFactory CreateFactory(JoinableTaskCollection collection)
{
return this.context.CreateFactory(collection);
}
///
/// Creates a collection for in-flight joinable tasks.
///
/// A new joinable task collection.
public JoinableTaskCollection CreateCollection()
{
return this.context.CreateCollection();
}
///
/// Conceals any JoinableTask the caller is associated with until the returned value is disposed.
///
/// A value to dispose of to restore visibility into the caller's associated JoinableTask, if any.
///
/// In some cases asynchronous work may be spun off inside a delegate supplied to Run,
/// so that the work does not have privileges to re-enter the Main thread until the
/// call has returned and the UI thread is idle.
/// To prevent the asynchronous work from automatically being allowed to re-enter the Main thread,
/// wrap the code that calls the asynchronous task in a using block with a call to this method
/// as the expression.
///
///
/// this.JoinableTaskContext.RunSynchronously(async delegate {
/// using(this.JoinableTaskContext.SuppressRelevance()) {
/// var asyncOperation = Task.Run(async delegate {
/// // Some background work.
/// await this.JoinableTaskContext.SwitchToMainThreadAsync();
/// // Some Main thread work, that cannot begin until the outer RunSynchronously call has returned.
/// });
/// }
///
/// // Because the asyncOperation is not related to this Main thread work (it was suppressed),
/// // the following await *would* deadlock if it were uncommented.
/// ////await asyncOperation;
/// });
///
///
///
public JoinableTaskContext.RevertRelevance SuppressRelevance()
{
return this.context.SuppressRelevance();
}
///
/// Gets a value indicating whether the main thread is blocked for the caller's completion.
///
public bool IsMainThreadBlocked()
{
return this.context.IsMainThreadBlocked();
}
///
/// Invoked when a hang is suspected to have occurred involving the main thread.
///
/// Describes the hang in detail.
///
/// A single hang occurrence may invoke this method multiple times, with increasing
/// values in the values
/// in the parameter.
///
protected internal virtual void OnHangDetected(JoinableTaskContext.HangDetails details)
{
Requires.NotNull(details, nameof(details));
// Preserve backward compatibility by forwarding the call to the older overload.
this.OnHangDetected(details.HangDuration, details.NotificationCount, details.HangId);
}
///
/// Invoked when an earlier hang report is false alarm.
///
/// The duration of the total waiting time.
/// A GUID that uniquely identifies the earlier hang report.
protected internal virtual void OnFalseHangDetected(TimeSpan hangDuration, Guid hangId)
{
}
///
/// Invoked when a hang is suspected to have occurred involving the main thread.
///
/// The duration of the current hang.
/// The number of times this hang has been reported, including this one.
/// A random GUID that uniquely identifies this particular hang.
///
/// A single hang occurrence may invoke this method multiple times, with increasing
/// values in the parameter.
///
protected virtual void OnHangDetected(TimeSpan hangDuration, int notificationCount, Guid hangId)
{
}
///
/// Creates a factory without a .
///
///
/// Used for initializing the property.
///
protected virtual JoinableTaskFactory CreateDefaultFactory()
{
return this.context.CreateDefaultFactory();
}
///
/// Registers with the inner to receive hang notifications.
///
/// A value to dispose of to cancel hang notifications.
protected IDisposable RegisterOnHangDetected()
{
return this.context.RegisterHangNotifications(this);
}
}
}