From c55a1e3821d45ffeef9442f69347a2fe71da840d Mon Sep 17 00:00:00 2001 From: Lifeng Lu Date: Wed, 5 Nov 2025 13:41:46 -0800 Subject: [PATCH 1/3] Do not track nested factory when there is no main thread Generally, post to context would not be used as SwitchToMainThread is no-op in this context. Even it is being called under certain corner cases, it would just queue the same work item to the thread pool multiple times, so better to prevent the allocation in the first place. --- .../JoinableTaskFactory.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs index 0a0520f89..55b9bd62e 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs @@ -1017,20 +1017,23 @@ internal RunFramework(JoinableTaskFactory factory, JoinableTask joinable) { JoinableTaskDependencyGraph.AddDependency(this.previousJoinable, joinable); - // By definition we inherit the nesting factories of our immediate nesting task. - ListOfOftenOne nestingFactories = this.previousJoinable.NestingFactories; - - // And we may add our immediate nesting parent's factory to the list of - // ancestors if it isn't already in the list. - if (this.previousJoinable.Factory != this.factory) + if (!factory.Context.IsNoOpContext) { - if (!nestingFactories.Contains(this.previousJoinable.Factory)) + // By definition we inherit the nesting factories of our immediate nesting task. + ListOfOftenOne nestingFactories = this.previousJoinable.NestingFactories; + + // And we may add our immediate nesting parent's factory to the list of + // ancestors if it isn't already in the list. + if (this.previousJoinable.Factory != this.factory) { - nestingFactories.Add(this.previousJoinable.Factory); + if (!nestingFactories.Contains(this.previousJoinable.Factory)) + { + nestingFactories.Add(this.previousJoinable.Factory); + } } - } - this.joinable.NestingFactories = nestingFactories; + this.joinable.NestingFactories = nestingFactories; + } } if (joinable.GetTokenizedParent() is JoinableTask tokenizedParent) From 06c475eba1f145a79113381731b17bd2d3ea1559 Mon Sep 17 00:00:00 2001 From: Lifeng Lu Date: Wed, 5 Nov 2025 13:49:28 -0800 Subject: [PATCH 2/3] Do not turn on SynchronouslyBlockingMainThread flag on when the JTF is used in no main thread mode. Because IsOnMainThread is just to check the thread id, this can be turned on randomly when the code is running in the initial thread pool thread. It would turn on expensive dependency tracking graph, which is not what we expect to do. --- src/Microsoft.VisualStudio.Threading/JoinableTask.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTask.cs b/src/Microsoft.VisualStudio.Threading/JoinableTask.cs index e82404d4c..0b7e060f1 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTask.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTask.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; using System.Threading; using System.Threading.Tasks; using JoinRelease = Microsoft.VisualStudio.Threading.JoinableTaskCollection.JoinRelease; @@ -147,7 +146,7 @@ internal JoinableTask(JoinableTaskFactory owner, bool synchronouslyBlocking, str this.state |= JoinableTaskFlags.StartedSynchronously | JoinableTaskFlags.CompletingSynchronously; } - if (owner.Context.IsOnMainThread) + if (owner.Context.IsOnMainThread && !this.JoinableTaskContext.IsNoOpContext) { this.state |= JoinableTaskFlags.StartedOnMainThread; if (synchronouslyBlocking) @@ -977,7 +976,7 @@ internal void CompleteOnCurrentThread() { bool onMainThread = false; JoinableTaskFlags additionalFlags = JoinableTaskFlags.CompletingSynchronously; - if (this.JoinableTaskContext.IsOnMainThread) + if (this.JoinableTaskContext.IsOnMainThread && !this.JoinableTaskContext.IsNoOpContext) { additionalFlags |= JoinableTaskFlags.SynchronouslyBlockingMainThread; onMainThread = true; From d97b47723b4f39687b371dfd8dd6c581e828d70e Mon Sep 17 00:00:00 2001 From: Lifeng Lu Date: Wed, 5 Nov 2025 14:02:21 -0800 Subject: [PATCH 3/3] stop creating main thread synchronization context when running in no main thread mode. --- src/Microsoft.VisualStudio.Threading/JoinableTask.cs | 2 +- src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTask.cs b/src/Microsoft.VisualStudio.Threading/JoinableTask.cs index 0b7e060f1..7490f3672 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTask.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTask.cs @@ -320,7 +320,7 @@ internal SynchronizationContext? ApplicableJobSyncContext { get { - if (this.JoinableTaskContext.IsOnMainThread) + if (this.JoinableTaskContext.IsOnMainThread && !this.JoinableTaskContext.IsNoOpContext) { if (this.mainThreadJobSyncContext is null) { diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs index 55b9bd62e..3417adb3a 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs @@ -28,7 +28,7 @@ public partial class JoinableTaskFactory /// private readonly JoinableTaskContext owner; - private readonly SynchronizationContext mainThreadJobSyncContext; + private readonly SynchronizationContext? mainThreadJobSyncContext; /// /// The collection to add all created tasks to. May be . @@ -71,7 +71,7 @@ internal JoinableTaskFactory(JoinableTaskContext owner, JoinableTaskCollection? this.owner = owner; this.jobCollection = collection; - this.mainThreadJobSyncContext = new JoinableTaskSynchronizationContext(this); + this.mainThreadJobSyncContext = owner.IsNoOpContext ? null : new JoinableTaskSynchronizationContext(this); } ///