11/*
2- * Copyright (c) 2022, 2022 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2022, 2025 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2828import java .util .Set ;
2929import java .util .concurrent .ConcurrentHashMap ;
3030
31- import org .graalvm .collections .EconomicMap ;
32- import org .graalvm .collections .MapCursor ;
33-
3431import com .oracle .svm .core .BuildPhaseProvider ;
3532import com .oracle .svm .core .feature .AutomaticallyRegisteredFeature ;
3633import com .oracle .svm .core .feature .InternalFeature ;
@@ -76,31 +73,46 @@ private Object replaceHostedWithRuntime(Object obj) {
7673 return obj ;
7774 }
7875
76+ /**
77+ * This method makes sure that the content of all modified {@link HostedImageHeapMap}s and
78+ * {@link HostedImageHeapList}s is properly propagated to their runtime counterparts. As both
79+ * the number of these collections and their individual sizes are theoretically unbounded, we
80+ * use <i>parallel streams</i> to divide the load across all cores.
81+ * <p>
82+ * We split the process into two stages. First, the content of each modified collection is
83+ * propagated from the hosted to the runtime version. Then, the modified runtime collections are
84+ * rescanned. The split is done to prevent concurrent modifications of the hosted collections
85+ * during the execution of this method, as they may be updated indirectly during the heap
86+ * scanning.
87+ */
7988 @ Override
8089 public void duringAnalysis (DuringAnalysisAccess a ) {
8190 DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl ) a ;
8291 if (ImageLayerBuildingSupport .buildingExtensionLayer ()) {
8392 allMaps .addAll (LayeredHostedImageHeapMapCollector .singleton ().getPreviousLayerReachableMaps ());
8493 }
85- for ( var hostedImageHeapMap : allMaps ) {
86- if ( needsUpdate (hostedImageHeapMap )) {
87- update (hostedImageHeapMap );
88- access . rescanObject ( hostedImageHeapMap .getCurrentLayerMap () );
89- access . requireAnalysisIteration ( );
94+ Set < Object > objectsToRescan = ConcurrentHashMap . newKeySet ();
95+ allMaps . parallelStream (). forEach (hostedImageHeapMap -> {
96+ if (hostedImageHeapMap . needsUpdate ()) {
97+ hostedImageHeapMap .update ( );
98+ objectsToRescan . add ( hostedImageHeapMap . getCurrentLayerMap () );
9099 }
91- }
92- for ( var hostedImageHeapList : allLists ) {
100+ });
101+ allLists . parallelStream (). forEach ( hostedImageHeapList -> {
93102 if (hostedImageHeapList .needsUpdate ()) {
94103 hostedImageHeapList .update ();
95- access .rescanObject (hostedImageHeapList .runtimeList );
96- access .requireAnalysisIteration ();
104+ objectsToRescan .add (hostedImageHeapList .runtimeList );
97105 }
106+ });
107+ if (!objectsToRescan .isEmpty ()) {
108+ objectsToRescan .parallelStream ().forEach (access ::rescanObject );
109+ access .requireAnalysisIteration ();
98110 }
99111 }
100112
101113 public boolean needsUpdate () {
102114 for (var hostedImageHeapMap : allMaps ) {
103- if (needsUpdate (hostedImageHeapMap )) {
115+ if (hostedImageHeapMap . needsUpdate ()) {
104116 return true ;
105117 }
106118 }
@@ -115,7 +127,7 @@ public boolean needsUpdate() {
115127 @ Override
116128 public void afterImageWrite (AfterImageWriteAccess access ) {
117129 for (var hostedImageHeapMap : allMaps ) {
118- if (needsUpdate (hostedImageHeapMap )) {
130+ if (hostedImageHeapMap . needsUpdate ()) {
119131 throw VMError .shouldNotReachHere ("ImageHeapMap modified after static analysis:%n%s%n%s" ,
120132 hostedImageHeapMap , hostedImageHeapMap .getCurrentLayerMap ());
121133 }
@@ -128,25 +140,4 @@ public void afterImageWrite(AfterImageWriteAccess access) {
128140 }
129141 }
130142 }
131-
132- private static boolean needsUpdate (HostedImageHeapMap <?, ?> hostedMap ) {
133- EconomicMap <Object , Object > runtimeMap = hostedMap .getCurrentLayerMap ();
134- if (hostedMap .size () != runtimeMap .size ()) {
135- return true ;
136- }
137- MapCursor <?, ?> hostedEntry = hostedMap .getEntries ();
138- while (hostedEntry .advance ()) {
139- Object hostedValue = hostedEntry .getValue ();
140- Object runtimeValue = runtimeMap .get (hostedEntry .getKey ());
141- if (hostedValue != runtimeValue ) {
142- return true ;
143- }
144- }
145- return false ;
146- }
147-
148- private static void update (HostedImageHeapMap <?, ?> hostedMap ) {
149- hostedMap .getCurrentLayerMap ().clear ();
150- hostedMap .getCurrentLayerMap ().putAll (hostedMap );
151- }
152143}
0 commit comments