Skip to content

Commit a97f324

Browse files
authored
Merge pull request #783 from microsoft/dev/lifengl/removeDependencyPerf
Try to reduce performance overhead when we remove a JTF dependency
2 parents c79ca83 + 3b98e80 commit a97f324

File tree

3 files changed

+230
-60
lines changed

3 files changed

+230
-60
lines changed

src/Microsoft.VisualStudio.Threading/JoinableTask.cs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,21 @@ internal WeakReference<JoinableTask> WeakSelf
380380
}
381381
}
382382

383+
/// <summary>
384+
/// Gets or sets potential unreachable dependent nodes.
385+
/// This is a special collection only used in synchronized task when there are other tasks which are marked to block it through ref-count code.
386+
/// However, it is possible the reference count is retained by loop-dependencies. This collection tracking those items,
387+
/// so the clean-up logic can run when it becomes necessary.
388+
/// </summary>
389+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
390+
internal HashSet<IJoinableTaskDependent>? PotentialUnreachableDependents { get; set; }
391+
392+
/// <summary>
393+
/// Gets a value indicating whether PotentialUnreachableDependents is empty.
394+
/// </summary>
395+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
396+
internal bool HasPotentialUnreachableDependents => this.PotentialUnreachableDependents is object && this.PotentialUnreachableDependents.Count != 0;
397+
383398
/// <summary>
384399
/// Gets the flags set on this task.
385400
/// </summary>
@@ -702,6 +717,18 @@ internal void Post(SendOrPostCallback d, object? state, bool mainThreadAffinitiz
702717
eventsNeedNotify = new List<AsyncManualResetEvent>(tasksNeedNotify.Count);
703718
foreach (JoinableTask? taskToNotify in tasksNeedNotify)
704719
{
720+
if (mainThreadQueueUpdated && taskToNotify != this && taskToNotify.pendingEventCount == 0 && taskToNotify.HasPotentialUnreachableDependents)
721+
{
722+
// It is not essential to clean up potential unreachable dependent items before triggering the UI thread,
723+
// because dependencies may change, and invalidate this work. However, we try to do this work in the background thread to make it less likely
724+
// doing the expensive work on the UI thread.
725+
if (JoinableTaskDependencyGraph.CleanUpPotentialUnreachableDependentItems(taskToNotify, out HashSet<IJoinableTaskDependent>? reachableNodes) &&
726+
!reachableNodes.Contains(this))
727+
{
728+
continue;
729+
}
730+
}
731+
705732
if (taskToNotify.pendingEventSource is null || taskToNotify == this)
706733
{
707734
taskToNotify.pendingEventSource = this.WeakSelf;
@@ -1078,7 +1105,9 @@ private bool TryDequeueSelfOrDependencies(bool onMainThread, ref HashSet<IJoinab
10781105

10791106
if (this.pendingEventSource is object)
10801107
{
1081-
if (this.pendingEventSource.TryGetTarget(out JoinableTask? pendingSource) && JoinableTaskDependencyGraph.IsDependingSynchronousTask(pendingSource, this))
1108+
if (this.pendingEventSource.TryGetTarget(out JoinableTask? pendingSource) &&
1109+
(pendingSource == this ||
1110+
(!this.HasPotentialUnreachableDependents && JoinableTaskDependencyGraph.IsDependingSynchronousTask(pendingSource, this))))
10821111
{
10831112
ExecutionQueue? queue = onMainThread ? pendingSource.mainThreadQueue : pendingSource.threadPoolQueue;
10841113
if (queue is object && !queue.IsCompleted && queue.TryDequeue(out work))
@@ -1105,8 +1134,25 @@ private bool TryDequeueSelfOrDependencies(bool onMainThread, ref HashSet<IJoinab
11051134
visited.Clear();
11061135
}
11071136

1108-
if (TryDequeueSelfOrDependencies(this, onMainThread, visited, out work))
1137+
bool foundWork = TryDequeueSelfOrDependencies(this, onMainThread, visited, out work);
1138+
1139+
HashSet<IJoinableTaskDependent>? visitedNodes = visited;
1140+
if (visitedNodes != null && this.HasPotentialUnreachableDependents)
11091141
{
1142+
// We walked the dependencies tree and use this information to update the PotentialUnreachableDependents list.
1143+
this.PotentialUnreachableDependents!.RemoveWhere(n => visitedNodes.Contains(n));
1144+
1145+
if (!foundWork && this.PotentialUnreachableDependents.Count > 0)
1146+
{
1147+
JoinableTaskDependencyGraph.RemoveUnreachableDependentItems(this, this.PotentialUnreachableDependents, visitedNodes);
1148+
this.PotentialUnreachableDependents.Clear();
1149+
}
1150+
}
1151+
1152+
if (foundWork)
1153+
{
1154+
Assumes.NotNull(work);
1155+
11101156
tryAgainAfter = null;
11111157
return true;
11121158
}

0 commit comments

Comments
 (0)