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();