@@ -18,6 +18,11 @@ public class NonBridge
1818 public object Link ;
1919}
2020
21+ public class NonBridge2 : NonBridge
22+ {
23+ public object Link2 ;
24+ }
25+
2126class Driver {
2227 const int OBJ_COUNT = 200 * 1000 ;
2328 const int LINK_COUNT = 2 ;
@@ -207,6 +212,61 @@ static void Spider () {
207212 c . Links . Add ( last_level ) ;
208213 }
209214
215+ /*
216+ * Simulates a graph with two nested cycles that is produces by
217+ * the async state machine when `async Task M()` method gets its
218+ * continuation rooted by an Action held by RunnableImplementor
219+ * (ie. the task continuation is hooked through the SynchronizationContext
220+ * implentation and rooted only by Android bridge objects).
221+ */
222+ static void NestedCycles ( )
223+ {
224+ Bridge runnableImplementor = new Bridge ( ) ;
225+ Bridge byteArrayOutputStream = new Bridge ( ) ;
226+ NonBridge2 action = new NonBridge2 ( ) ;
227+ NonBridge displayClass = new NonBridge ( ) ;
228+ NonBridge2 asyncStateMachineBox = new NonBridge2 ( ) ;
229+ NonBridge2 asyncStreamWriter = new NonBridge2 ( ) ;
230+
231+ runnableImplementor . Links . Add ( action ) ;
232+ action . Link = displayClass ;
233+ action . Link2 = asyncStateMachineBox ;
234+ displayClass . Link = action ;
235+ asyncStateMachineBox . Link = asyncStreamWriter ;
236+ asyncStateMachineBox . Link2 = action ;
237+ asyncStreamWriter . Link = byteArrayOutputStream ;
238+ asyncStreamWriter . Link2 = asyncStateMachineBox ;
239+ }
240+
241+ /*
242+ * Simulates a graph where a heavy node has its fanout components
243+ * represented by cycles with back-references to the heavy node and
244+ * references to the same bridge objects.
245+ * This enters a pathological path in the SCC contraction where the
246+ * links to the bridge objects need to be correctly deduplicated. The
247+ * deduplication causes the heavy node to no longer be heavy.
248+ */
249+ static void FauxHeavyNodeWithCycles ( )
250+ {
251+ Bridge fanout = new Bridge ( ) ;
252+
253+ // Need enough edges for the node to be considered heavy by bridgeless_color_is_heavy
254+ NonBridge [ ] fauxHeavyNode = new NonBridge [ 100 ] ;
255+ for ( int i = 0 ; i < fauxHeavyNode . Length ; i ++ ) {
256+ NonBridge2 cycle = new NonBridge2 ( ) ;
257+ cycle . Link = fanout ;
258+ cycle . Link2 = fauxHeavyNode ;
259+ fauxHeavyNode [ i ] = cycle ;
260+ }
261+
262+ // Need at least HEAVY_REFS_MIN + 1 fan-in nodes
263+ Bridge [ ] faninNodes = new Bridge [ 3 ] ;
264+ for ( int i = 0 ; i < faninNodes . Length ; i ++ ) {
265+ faninNodes [ i ] = new Bridge ( ) ;
266+ faninNodes [ i ] . Links . Add ( fauxHeavyNode ) ;
267+ }
268+ }
269+
210270 static void RunTest ( ThreadStart setup )
211271 {
212272 var t = new Thread ( setup ) ;
@@ -231,6 +291,8 @@ static int Main ()
231291 RunTest ( SetupDeadList ) ;
232292 RunTest ( SetupSelfLinks ) ;
233293 RunTest ( Spider ) ;
294+ RunTest ( NestedCycles ) ;
295+ RunTest ( FauxHeavyNodeWithCycles ) ;
234296
235297 for ( int i = 0 ; i < 0 ; ++ i ) {
236298 GC . Collect ( ) ;
0 commit comments