Skip to content

Commit fe17c2c

Browse files
authored
[mono][sgen] Make color visible to client permanent (#121247)
A color (SCC) that isn't containing any bridge objects is made visible to client if xrefs_in * xrefs_out is greater than 60. Later on in bridge processing, we need to build the callback to pass for .net android. During this stage, we reduce from the full set of SCCs to SCCs that should be visible to client (containing bridge objects or satisfying the above condition). If an SCC has an xref to a color that is not visible to client, we need to do a recursive traversal to find all neighbors that are visible to client. The problem is that this process can end up making an SCC no longer visible to client, leading to inconsistencies in the computation. Consider a color(C1) that has a neighbor that is not visible to client(C2). In this final stage, we compute the neighbors of C1 by traversing recursively through the neighbors of C2. If C2 ends up pointing to colors that were already neighbors of C1, then, following this computation, C1 would end up with fewer xrefs_out, making the color no longer visible to client. This make future checks incorrect, resulting in building incorrect graph for client. This scenario seems rare in practice, we should have gotten way more reports otherwise. We fix this by pinning the visible_to_client property for a color once it first satisfies it, so it will no longer matter how many actual xrefs the color has. Fixes assertions like: ``` * Assertion at /home/vbrezae/Xamarin/repos/runtime/src/mono/mono/metadata/sgen-tarjan-bridge.c:1151, condition `color_visible_to_client (cd)' not met ```
1 parent 3345865 commit fe17c2c

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

src/coreclr/gc/gcbridge.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ struct ColorData
186186
// Count of colors that list this color in their otherColors
187187
unsigned incomingColors : INCOMING_COLORS_BITS;
188188
unsigned visited : 1;
189+
// ColorVisibleToClient for a ColorData* can change over the course of bridge processing which
190+
// is problematic. We fix this by setting this flag when a color is detected as visible to client.
191+
// Once the flag is set, the color is pinned to being visible to client, even though it could lose
192+
// some xrefs, making it not satisfy the BridgelessColorIsHeavy condition.
193+
unsigned visibleToClient : 1;
189194
};
190195

191196
// Represents one managed object. Equivalent of new/old bridge "HashEntry"
@@ -233,7 +238,19 @@ static bool BridgelessColorIsHeavy(ColorData* data)
233238
// Should color be made visible to client?
234239
static bool ColorVisibleToClient(ColorData* data)
235240
{
236-
return DynPtrArraySize(&data->bridges) || BridgelessColorIsHeavy(data);
241+
if (data->visibleToClient)
242+
return true;
243+
244+
if (DynPtrArraySize(&data->bridges) || BridgelessColorIsHeavy(data))
245+
{
246+
data->visibleToClient = true;
247+
return true;
248+
}
249+
else
250+
{
251+
return false;
252+
}
253+
237254
}
238255

239256
// Stacks of ScanData objects used for tarjan algorithm.
@@ -1186,9 +1203,9 @@ static MarkCrossReferencesArgs* BuildSccCallbackData()
11861203
ColorData* cd;
11871204
for (cd = &cur->data[0]; cd < cur->nextData; cd++)
11881205
{
1189-
int bridges = DynPtrArraySize(&cd->bridges);
1190-
if (!(bridges || BridgelessColorIsHeavy(cd)))
1206+
if (!ColorVisibleToClient(cd))
11911207
continue;
1208+
int bridges = DynPtrArraySize(&cd->bridges);
11921209

11931210
apiSccs[apiIndex].Count = bridges;
11941211
uintptr_t *contexts = new (nothrow) uintptr_t[bridges];

src/mono/mono/metadata/sgen-tarjan-bridge.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ typedef struct {
9696
// Count of colors that list this color in their other_colors
9797
unsigned incoming_colors : INCOMING_COLORS_BITS;
9898
unsigned visited : 1;
99+
// color_visible_to_client for a ColorData* can change over the course of bridge processing which
100+
// is problematic. We fix this by setting this flag when a color is detected as visible to client.
101+
// Once the flag is set, the color is pinned to being visible to client, even though it could lose
102+
// some xrefs, making it not satisfy the bridgeless_color_is_heavy condition.
103+
unsigned visible_to_client : 1;
99104
} ColorData;
100105

101106
// Represents one managed object. Equivalent of new/old bridge "HashEntry"
@@ -140,8 +145,17 @@ bridgeless_color_is_heavy (ColorData *data) {
140145

141146
// Should color be made visible to client?
142147
static gboolean
143-
color_visible_to_client (ColorData *data) {
144-
return dyn_array_ptr_size (&data->bridges) || bridgeless_color_is_heavy (data);
148+
color_visible_to_client (ColorData *data)
149+
{
150+
if (data->visible_to_client)
151+
return TRUE;
152+
153+
if (dyn_array_ptr_size (&data->bridges) || bridgeless_color_is_heavy (data)) {
154+
data->visible_to_client = TRUE;
155+
return TRUE;
156+
} else {
157+
return FALSE;
158+
}
145159
}
146160

147161
// Stacks of ScanData objects used for tarjan algorithm.
@@ -1105,9 +1119,9 @@ processing_build_callback_data (int generation)
11051119
for (cur = root_color_bucket; cur; cur = cur->next) {
11061120
ColorData *cd;
11071121
for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1108-
int bridges = dyn_array_ptr_size (&cd->bridges);
1109-
if (!(bridges || bridgeless_color_is_heavy (cd)))
1122+
if (!color_visible_to_client (cd))
11101123
continue;
1124+
int bridges = dyn_array_ptr_size (&cd->bridges);
11111125

11121126
api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
11131127
api_sccs [api_index]->is_alive = FALSE;

src/tests/GC/Features/Bridge/Bridge.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ public class Bridge1 : BridgeBase
8989
}
9090
}
9191

92+
[InlineArray(14)]
93+
public struct InlineData
94+
{
95+
public object obj;
96+
}
97+
9298
// 128 size
9399
public class Bridge14 : BridgeBase
94100
{
@@ -107,7 +113,7 @@ public class NonBridge2 : NonBridge
107113

108114
public class NonBridge14
109115
{
110-
public object a,b,c,d,e,f,g,h,i,j,k,l,m,n;
116+
public InlineData Data;
111117
}
112118

113119

@@ -403,6 +409,27 @@ static void SetupFragmentation<TBridge, TNonBridge>()
403409
}
404410
}
405411

412+
public static void BridgelessHeavyColorChanging()
413+
{
414+
Bridge1[] left = new Bridge1[8];
415+
for (int i = 0; i < 8; i++)
416+
left[i] = new Bridge1();
417+
Bridge[] right = new Bridge[7];
418+
for (int i = 0; i < 7; i++)
419+
right[i] = new Bridge();
420+
NonBridge2 right7 = new NonBridge2();
421+
422+
NonBridge14 mid = new NonBridge14();
423+
424+
for (int i = 0; i < 8; i++)
425+
left[i].Link = mid;
426+
for (int i = 0; i < 7; i++)
427+
mid.Data[i] = right[i];
428+
mid.Data[7] = right7;
429+
right7.Link = right[6];
430+
right7.Link2 = right[5];
431+
}
432+
406433
public static int Main(string[] args)
407434
{
408435
TestLinkedList();
@@ -417,6 +444,7 @@ public static int Main(string[] args)
417444
RunGraphTest(NestedCycles);
418445
RunGraphTest(FauxHeavyNodeWithCycles);
419446
RunGraphTest(Spider);
447+
RunGraphTest(BridgelessHeavyColorChanging);
420448
return 100;
421449
}
422450
}

0 commit comments

Comments
 (0)