24
24
*/
25
25
package com .oracle .svm .core .genscavenge ;
26
26
27
- import java .util .concurrent .atomic .AtomicBoolean ;
28
-
29
- import jdk .graal .compiler .word .Word ;
30
27
import org .graalvm .nativeimage .Platform ;
31
28
import org .graalvm .nativeimage .Platforms ;
32
29
import org .graalvm .word .UnsignedWord ;
36
33
import com .oracle .svm .core .heap .PhysicalMemory ;
37
34
import com .oracle .svm .core .heap .ReferenceAccess ;
38
35
import com .oracle .svm .core .jdk .UninterruptibleUtils ;
36
+ import com .oracle .svm .core .thread .JavaSpinLockUtils ;
39
37
import com .oracle .svm .core .thread .VMOperation ;
40
38
import com .oracle .svm .core .util .UnsignedUtils ;
41
39
import com .oracle .svm .core .util .VMError ;
42
40
43
41
import jdk .graal .compiler .api .replacements .Fold ;
44
- import jdk .graal .compiler .nodes .PauseNode ;
42
+ import jdk .graal .compiler .word .Word ;
43
+ import jdk .internal .misc .Unsafe ;
45
44
46
45
abstract class AbstractCollectionPolicy implements CollectionPolicy {
47
46
@@ -54,6 +53,9 @@ static int getMaxSurvivorSpaces(Integer userValue) {
54
53
return (userValue != null ) ? userValue : AbstractCollectionPolicy .MAX_TENURING_THRESHOLD ;
55
54
}
56
55
56
+ private static final Unsafe U = Unsafe .getUnsafe ();
57
+ private static final long SIZES_UPDATE_LOCK_OFFSET = U .objectFieldOffset (AbstractCollectionPolicy .class , "sizesUpdateLock" );
58
+
57
59
/*
58
60
* Constants that can be made options if desirable. These are -XX options in HotSpot, refer to
59
61
* their descriptions for details. The values are HotSpot defaults unless labeled otherwise.
@@ -78,8 +80,8 @@ static int getMaxSurvivorSpaces(Integer userValue) {
78
80
protected UnsignedWord oldSize ;
79
81
protected int tenuringThreshold ;
80
82
81
- protected volatile SizeParameters sizes ;
82
- private final AtomicBoolean sizesUpdateSpinLock = new AtomicBoolean () ;
83
+ protected volatile SizeParameters sizes = null ;
84
+ @ SuppressWarnings ( "unused" ) private volatile int sizesUpdateLock ;
83
85
84
86
protected AbstractCollectionPolicy (int initialNewRatio , int initialTenuringThreshold ) {
85
87
this .initialNewRatio = initialNewRatio ;
@@ -145,40 +147,49 @@ protected void guaranteeSizeParametersInitialized() {
145
147
146
148
@ Override
147
149
public void updateSizeParameters () {
148
- SizeParameters params = computeSizeParameters (sizes );
149
- SizeParameters previous = sizes ;
150
- if (previous != null && params .equal (previous )) {
150
+ /*
151
+ * Read the old object before computing the new values. Otherwise, we risk reusing an
152
+ * outdated SizeParameters object.
153
+ */
154
+ SizeParameters prevParams = sizes ;
155
+ SizeParameters newParams = computeSizeParameters (prevParams );
156
+ if (prevParams != null && newParams .equal (prevParams )) {
151
157
return ; // nothing to do
152
158
}
153
- while (!sizesUpdateSpinLock .compareAndSet (false , true )) {
154
- /*
155
- * We use a primitive spin lock because at this point, the current thread might be
156
- * unable to use a Java lock (e.g. no Thread object yet), and the critical section is
157
- * short, so we do not want to suspend and wake up threads for it.
158
- */
159
- PauseNode .pause ();
160
- }
159
+ updateSizeParameters0 (newParams , prevParams );
160
+ guaranteeSizeParametersInitialized (); // sanity
161
+ }
162
+
163
+ @ Uninterruptible (reason = "Holding the spin lock at a safepoint can result in deadlocks." )
164
+ private void updateSizeParameters0 (SizeParameters newParams , SizeParameters prevParams ) {
165
+ /*
166
+ * We use a primitive spin lock because at this point, the current thread might be unable to
167
+ * use a Java lock (e.g. no Thread object yet), and the critical section is short, so we do
168
+ * not want to suspend and wake up threads for it.
169
+ */
170
+ JavaSpinLockUtils .lockNoTransition (this , SIZES_UPDATE_LOCK_OFFSET );
161
171
try {
162
- updateSizeParametersLocked (params , previous );
172
+ if (sizes != prevParams ) {
173
+ /*
174
+ * Some other thread beat us and we cannot tell if our values or their values are
175
+ * newer, so back off - any newer values will be applied eventually.
176
+ */
177
+ return ;
178
+ }
179
+ updateSizeParametersLocked (newParams , prevParams );
163
180
} finally {
164
- sizesUpdateSpinLock . set ( false );
181
+ JavaSpinLockUtils . unlock ( this , SIZES_UPDATE_LOCK_OFFSET );
165
182
}
166
- guaranteeSizeParametersInitialized (); // sanity
167
183
}
168
184
169
- @ Uninterruptible (reason = "Must be atomic with regard to garbage collection." )
170
- private void updateSizeParametersLocked (SizeParameters params , SizeParameters previous ) {
171
- if (sizes != previous ) {
172
- // Some other thread beat us and we cannot tell if our values or their values are newer,
173
- // so back off -- any newer values will be applied eventually.
174
- return ;
175
- }
176
- sizes = params ;
185
+ @ Uninterruptible (reason = "Holding the spin lock at a safepoint can result in deadlocks. Updating the size parameters must be atomic with regard to garbage collection." )
186
+ private void updateSizeParametersLocked (SizeParameters newParams , SizeParameters prevParams ) {
187
+ sizes = newParams ;
177
188
178
- if (previous == null || gcCount () == 0 ) {
179
- survivorSize = params .initialSurvivorSize ;
180
- edenSize = params .initialEdenSize ;
181
- oldSize = params .initialOldSize ();
189
+ if (prevParams == null || gcCount () == 0 ) {
190
+ survivorSize = newParams .initialSurvivorSize ;
191
+ edenSize = newParams .initialEdenSize ;
192
+ oldSize = newParams .initialOldSize ();
182
193
promoSize = UnsignedUtils .min (edenSize , oldSize );
183
194
}
184
195
@@ -191,10 +202,10 @@ private void updateSizeParametersLocked(SizeParameters params, SizeParameters pr
191
202
* We assume that such changes happen very early on and values then adapt reasonably quick,
192
203
* but we must still ensure that computations can handle it (for example, no overflows).
193
204
*/
194
- survivorSize = UnsignedUtils .min (survivorSize , params .maxSurvivorSize ());
205
+ survivorSize = UnsignedUtils .min (survivorSize , newParams .maxSurvivorSize ());
195
206
edenSize = UnsignedUtils .min (edenSize , getMaximumEdenSize ());
196
- oldSize = UnsignedUtils .min (oldSize , params .maxOldSize ());
197
- promoSize = UnsignedUtils .min (promoSize , params .maxOldSize ());
207
+ oldSize = UnsignedUtils .min (oldSize , newParams .maxOldSize ());
208
+ promoSize = UnsignedUtils .min (promoSize , newParams .maxOldSize ());
198
209
}
199
210
200
211
@ Override
0 commit comments