@@ -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