Skip to content

Commit bda22e4

Browse files
author
Lifeng Lu
committed
Try to clean up dependent item earlier inside thread pool.
1 parent d9d54cf commit bda22e4

File tree

2 files changed

+38
-24
lines changed

2 files changed

+38
-24
lines changed

src/Microsoft.VisualStudio.Threading/JoinableTask.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,18 @@ internal void Post(SendOrPostCallback d, object? state, bool mainThreadAffinitiz
711711
eventsNeedNotify = new List<AsyncManualResetEvent>(tasksNeedNotify.Count);
712712
foreach (JoinableTask? taskToNotify in tasksNeedNotify)
713713
{
714+
if (mainThreadQueueUpdated && taskToNotify != this && taskToNotify.pendingEventCount == 0 && taskToNotify.PotentialUnreachableDependents != null)
715+
{
716+
// It is not essential to clean up potential unreachable dependent items before triggering the UI thread,
717+
// because dependencies may change, and invalidate this work. However, we try to do this work in the background thread to make it less likely
718+
// doing the expensive work on the UI thread.
719+
if (JoinableTaskDependencyGraph.CleanUpPotentialUnreachableDependentItems(taskToNotify, out HashSet<IJoinableTaskDependent>? reachableNodes) &&
720+
!reachableNodes!.Contains(this))
721+
{
722+
continue;
723+
}
724+
}
725+
714726
if (taskToNotify.pendingEventSource is null || taskToNotify == this)
715727
{
716728
taskToNotify.pendingEventSource = this.WeakSelf;

src/Microsoft.VisualStudio.Threading/JoinableTaskDependencyGraph.cs

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Microsoft.VisualStudio.Threading
66
using System;
77
using System.Collections.Generic;
88
using System.Diagnostics;
9+
using System.Diagnostics.CodeAnalysis;
910
using System.Linq;
1011
using System.Threading;
1112

@@ -47,7 +48,7 @@ internal static bool HasMainThreadSynchronousTaskWaiting(IJoinableTaskDependent
4748
{
4849
lock (taskItem.JoinableTaskContext.SyncContextLock)
4950
{
50-
return taskItem.GetJoinableTaskDependentData().HasMainThreadSynchronousTaskWaiting();
51+
return taskItem.GetJoinableTaskDependentData().HasMainThreadSynchronousTaskWaiting(taskItem);
5152
}
5253
}
5354
}
@@ -223,7 +224,8 @@ internal static HashSet<JoinableTask> GetDependentTasksFromCandidates(IEnumerabl
223224
/// Computes dependency graph to clean up all potential unreachable dependents items.
224225
/// </summary>
225226
/// <param name="syncTask">A thread blocking sychornizing task.</param>
226-
internal static void CleanUpPotentialUnreachableDependentItems(JoinableTask syncTask)
227+
/// <returns>True if it removes any unreachable items.</returns>
228+
internal static bool CleanUpPotentialUnreachableDependentItems(JoinableTask syncTask, [MaybeNullWhen(false)] out HashSet<IJoinableTaskDependent>? allReachableNodes)
227229
{
228230
Requires.NotNull(syncTask, nameof(syncTask));
229231

@@ -240,9 +242,23 @@ internal static void CleanUpPotentialUnreachableDependentItems(JoinableTask sync
240242

241243
JoinableTaskDependentData.ComputeSelfAndDescendentOrJoinedJobsAndRemainTasks(syncTaskItem, reachableNodes, unreachableItems);
242244

245+
syncTask.PotentialUnreachableDependents = null;
246+
allReachableNodes = reachableNodes;
247+
243248
// force to remove all invalid items
244-
JoinableTaskDependentData.RemoveUnreachableDependentItems(syncTask, unreachableItems, reachableNodes);
249+
if (unreachableItems.Count > 0)
250+
{
251+
JoinableTaskDependentData.RemoveUnreachableDependentItems(syncTask, unreachableItems, reachableNodes);
252+
253+
return true;
254+
}
255+
}
256+
else
257+
{
258+
allReachableNodes = null;
245259
}
260+
261+
return false;
246262
}
247263

248264
/// <summary>
@@ -564,10 +580,8 @@ internal bool HasDirectDependency(IJoinableTaskDependent dependency)
564580
/// Gets a value indicating whether the main thread is waiting for the task's completion
565581
/// This method is expected to be used with the JTF lock.
566582
/// </summary>
567-
internal bool HasMainThreadSynchronousTaskWaiting()
583+
internal bool HasMainThreadSynchronousTaskWaiting(IJoinableTaskDependent taskItem)
568584
{
569-
var hasDoneCleanUp = false;
570-
571585
DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking;
572586
while (existingTaskTracking is object)
573587
{
@@ -577,10 +591,12 @@ internal bool HasMainThreadSynchronousTaskWaiting()
577591
if (existingTaskTracking.SynchronousTask.PotentialUnreachableDependents != null)
578592
{
579593
// This might remove the current tracking item from the linked list, so we capture next node first.
580-
CleanUpPotentialUnreachableDependentItems(existingTaskTracking.SynchronousTask);
581-
582-
// We need check it again after the cleanup work has finished.
583-
hasDoneCleanUp = true;
594+
if (!CleanUpPotentialUnreachableDependentItems(existingTaskTracking.SynchronousTask, out HashSet<IJoinableTaskDependent>? allReachableNodes) ||
595+
allReachableNodes!.Contains(taskItem))
596+
{
597+
// this task is still a dependenting task
598+
return true;
599+
}
584600
}
585601
else
586602
{
@@ -591,20 +607,6 @@ internal bool HasMainThreadSynchronousTaskWaiting()
591607
existingTaskTracking = nextTrackingTask;
592608
}
593609

594-
if (hasDoneCleanUp)
595-
{
596-
existingTaskTracking = this.dependingSynchronousTaskTracking;
597-
while (existingTaskTracking is object)
598-
{
599-
if ((existingTaskTracking.SynchronousTask.State & JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread)
600-
{
601-
return true;
602-
}
603-
604-
existingTaskTracking = existingTaskTracking.Next;
605-
}
606-
}
607-
608610
return false;
609611
}
610612

0 commit comments

Comments
 (0)