diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs
index e409a2db7..fa3cb7339 100644
--- a/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs
+++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs
@@ -245,6 +245,15 @@ public bool IsWithinJoinableTask
get { return this.AmbientTask is object; }
}
+ ///
+ /// Gets a value indicating whether this instance is not associated with any main thread
+ /// (e.g. created with ).
+ ///
+ ///
+ /// This allows library code to skip some additional work in the environments that do not have a main thread.
+ ///
+ public bool IsNoOpContext => this.UnderlyingSynchronizationContext is null;
+
///
/// Gets a value indicating whether the main thread is blocked by any joinable task.
///
diff --git a/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
index 82ffde8aa..dbb58c1bb 100644
--- a/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
@@ -1,4 +1,5 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
+Microsoft.VisualStudio.Threading.JoinableTaskContext.IsNoOpContext.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt
index 82ffde8aa..dbb58c1bb 100644
--- a/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt
@@ -1,4 +1,5 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
+Microsoft.VisualStudio.Threading.JoinableTaskContext.IsNoOpContext.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt
index 82ffde8aa..dbb58c1bb 100644
--- a/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt
@@ -1,4 +1,5 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
+Microsoft.VisualStudio.Threading.JoinableTaskContext.IsNoOpContext.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
index 82ffde8aa..dbb58c1bb 100644
--- a/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
@@ -1,4 +1,5 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
+Microsoft.VisualStudio.Threading.JoinableTaskContext.IsNoOpContext.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file
diff --git a/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs
index dc36759f6..2951b647c 100644
--- a/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs
+++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs
@@ -727,6 +727,7 @@ public void Ctor_ExplicitNullSyncContext()
Thread mainThread = Thread.CurrentThread;
Assumes.NotNull(SynchronizationContext.Current);
JoinableTaskContext jtc = JoinableTaskContext.CreateNoOpContext();
+ Assert.True(jtc.IsNoOpContext);
await TaskScheduler.Default.SwitchTo(alwaysYield: true); // Get off the main thread.
Assert.NotSame(mainThread, Thread.CurrentThread);
@@ -745,6 +746,7 @@ public void Ctor_NullSyncContextArg_AmbientSyncContext()
Thread mainThread = Thread.CurrentThread;
Assumes.NotNull(SynchronizationContext.Current);
JoinableTaskContext jtc = new(null, null);
+ Assert.False(jtc.IsNoOpContext);
await TaskScheduler.Default.SwitchTo(alwaysYield: true); // Get off the main thread.
Assert.NotSame(mainThread, Thread.CurrentThread);
@@ -762,6 +764,7 @@ public void Ctor_Default()
Thread mainThread = Thread.CurrentThread;
Assumes.NotNull(SynchronizationContext.Current);
JoinableTaskContext jtc = new();
+ Assert.False(jtc.IsNoOpContext);
await TaskScheduler.Default.SwitchTo(alwaysYield: true); // Get off the main thread.
Assert.NotSame(mainThread, Thread.CurrentThread);
@@ -771,6 +774,28 @@ public void Ctor_Default()
});
}
+ [Fact]
+ public void Ctor_DefaultWithNoSyncContext()
+ {
+ this.SimulateUIThread(async delegate
+ {
+ await TaskScheduler.Default.SwitchTo(alwaysYield: true); // Get off the main thread.
+
+ Thread currentThread = Thread.CurrentThread;
+
+ Assumes.Null(SynchronizationContext.Current);
+ JoinableTaskContext jtc = new();
+ Assert.True(jtc.IsNoOpContext);
+
+ await TaskScheduler.Default.SwitchTo();
+ Assert.Same(currentThread, Thread.CurrentThread);
+
+ // Verify that switching to the main thread works.
+ await jtc.Factory.SwitchToMainThreadAsync(this.TimeoutToken);
+ Assert.Same(currentThread, Thread.CurrentThread);
+ });
+ }
+
protected override JoinableTaskContext CreateJoinableTaskContext()
{
return new JoinableTaskContextDerived();