Skip to content

Commit b337599

Browse files
committed
8383810: Shenandoah: Simplify native CAS barriers
Reviewed-by: rkennke, xpeng, kdnilsen
1 parent d171f37 commit b337599

2 files changed

Lines changed: 173 additions & 24 deletions

File tree

src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -249,29 +249,35 @@ inline oop ShenandoahBarrierSet::oop_load(DecoratorSet decorators, T* addr) {
249249

250250
template <typename T>
251251
inline oop ShenandoahBarrierSet::oop_cmpxchg(DecoratorSet decorators, T* addr, oop compare_value, oop new_value) {
252-
oop res;
253-
oop expected = compare_value;
254-
do {
255-
compare_value = expected;
256-
res = RawAccess<>::oop_atomic_cmpxchg(addr, compare_value, new_value);
257-
expected = res;
258-
} while ((compare_value != expected) && (resolve_forwarded(compare_value) == resolve_forwarded(expected)));
252+
shenandoah_assert_not_in_cset_except(nullptr, compare_value, (compare_value == nullptr || ShenandoahHeap::heap()->cancelled_gc()));
253+
shenandoah_assert_not_in_cset_except(nullptr, new_value, (new_value == nullptr || ShenandoahHeap::heap()->cancelled_gc()));
259254

260-
// Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway,
261-
// because it must be the previous value.
262-
res = load_reference_barrier(decorators, res, static_cast<T*>(nullptr));
263-
satb_enqueue(res);
264-
return res;
255+
// Handle the previous value through SATB, as we are about to perform the store.
256+
oop prev = RawAccess<>::oop_load(addr);
257+
satb_enqueue(prev);
258+
259+
// Perform LRB on location to fix it up for this and all following accesses.
260+
// This guarantees there are no false negatives due to concurrent evacuation,
261+
// and the value loaded later by CAS is sanitized by some LRB, or is null.
262+
load_reference_barrier(decorators, prev, addr);
263+
264+
return RawAccess<>::oop_atomic_cmpxchg(addr, compare_value, new_value);
265265
}
266266

267267
template <typename T>
268268
inline oop ShenandoahBarrierSet::oop_xchg(DecoratorSet decorators, T* addr, oop new_value) {
269-
oop previous = RawAccess<>::oop_atomic_xchg(addr, new_value);
270-
// Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway,
271-
// because it must be the previous value.
272-
previous = load_reference_barrier<T>(decorators, previous, static_cast<T*>(nullptr));
273-
satb_enqueue(previous);
274-
return previous;
269+
shenandoah_assert_not_in_cset_except(nullptr, new_value, (new_value == nullptr || ShenandoahHeap::heap()->cancelled_gc()));
270+
271+
// Handle the previous value through SATB, as we are about to perform the store.
272+
oop prev = RawAccess<>::oop_load(addr);
273+
satb_enqueue(prev);
274+
275+
// Perform LRB on location to fix it up for this and all following accesses.
276+
// This is purely opportunistic: we would not have any false negatives here.
277+
// This guarantees the value loaded later by XCHG is sanitized by some LRB, or is null.
278+
load_reference_barrier(decorators, prev, addr);
279+
280+
return RawAccess<>::oop_atomic_xchg(addr, new_value);
275281
}
276282

277283
template <DecoratorSet decorators, typename BarrierSetT>
@@ -338,15 +344,17 @@ inline void ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_st
338344
template <DecoratorSet decorators, typename BarrierSetT>
339345
template <typename T>
340346
inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_atomic_cmpxchg_not_in_heap(T* addr, oop compare_value, oop new_value) {
341-
assert((decorators & (AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF)) == 0, "must be absent");
347+
assert((decorators & AS_NO_KEEPALIVE) == 0, "CAS only with keep-alive");
348+
assert((decorators & ON_STRONG_OOP_REF) != 0, "CAS only for strong refs");
342349
ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
343350
return bs->oop_cmpxchg(decorators, addr, compare_value, new_value);
344351
}
345352

346353
template <DecoratorSet decorators, typename BarrierSetT>
347354
template <typename T>
348355
inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_atomic_cmpxchg_in_heap(T* addr, oop compare_value, oop new_value) {
349-
assert((decorators & (AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF)) == 0, "must be absent");
356+
assert((decorators & AS_NO_KEEPALIVE) == 0, "CAS only with keep-alive");
357+
assert((decorators & ON_STRONG_OOP_REF) != 0, "CAS only for strong refs");
350358
ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
351359
oop result = bs->oop_cmpxchg(decorators, addr, compare_value, new_value);
352360
if (ShenandoahCardBarrier) {
@@ -357,9 +365,20 @@ inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_ato
357365

358366
template <DecoratorSet decorators, typename BarrierSetT>
359367
inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_atomic_cmpxchg_in_heap_at(oop base, ptrdiff_t offset, oop compare_value, oop new_value) {
360-
assert((decorators & AS_NO_KEEPALIVE) == 0, "must be absent");
368+
assert((decorators & AS_NO_KEEPALIVE) == 0, "CAS only with keep-alive");
369+
assert((decorators & (ON_STRONG_OOP_REF | ON_UNKNOWN_OOP_REF)) != 0, "CAS only for strong refs OR unknown refs (Unsafe)");
361370
ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
371+
372+
// Unsafe.compareAndExchange/Set come here with ON_UNKNOWN_OOP_REF set.
373+
// These are normally strong refs, but one can use Unsafe on Reference.referent.
374+
// We cannot deal with that case. If application does Unsafe operations on
375+
// Reference.referent field, this likely breaks weak reference semantics already.
376+
// We upgrade the access to strong in (sometimes futile) attempt to maintain heap
377+
// integrity, and assert in debug builds for better diagnostics.
362378
DecoratorSet resolved_decorators = AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength<decorators>(base, offset);
379+
assert((resolved_decorators & ON_STRONG_OOP_REF) != 0, "Application error: CAS on weak location");
380+
resolved_decorators = (resolved_decorators & ~ON_DECORATOR_MASK) | ON_STRONG_OOP_REF;
381+
363382
auto addr = AccessInternal::oop_field_addr<decorators>(base, offset);
364383
oop result = bs->oop_cmpxchg(resolved_decorators, addr, compare_value, new_value);
365384
if (ShenandoahCardBarrier) {
@@ -371,15 +390,17 @@ inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_ato
371390
template <DecoratorSet decorators, typename BarrierSetT>
372391
template <typename T>
373392
inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_atomic_xchg_not_in_heap(T* addr, oop new_value) {
374-
assert((decorators & (AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF)) == 0, "must be absent");
393+
assert((decorators & AS_NO_KEEPALIVE) == 0, "XCHG only with keep-alive");
394+
assert((decorators & ON_STRONG_OOP_REF) != 0, "XCHG only for strong refs");
375395
ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
376396
return bs->oop_xchg(decorators, addr, new_value);
377397
}
378398

379399
template <DecoratorSet decorators, typename BarrierSetT>
380400
template <typename T>
381401
inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_atomic_xchg_in_heap(T* addr, oop new_value) {
382-
assert((decorators & (AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF)) == 0, "must be absent");
402+
assert((decorators & AS_NO_KEEPALIVE) == 0, "XCHG only with keep-alive");
403+
assert((decorators & ON_STRONG_OOP_REF) != 0, "XCHG only for strong refs");
383404
ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
384405
oop result = bs->oop_xchg(decorators, addr, new_value);
385406
if (ShenandoahCardBarrier) {
@@ -390,9 +411,20 @@ inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_ato
390411

391412
template <DecoratorSet decorators, typename BarrierSetT>
392413
inline oop ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_atomic_xchg_in_heap_at(oop base, ptrdiff_t offset, oop new_value) {
393-
assert((decorators & AS_NO_KEEPALIVE) == 0, "must be absent");
414+
assert((decorators & AS_NO_KEEPALIVE) == 0, "XCHG only with keep-alive");
415+
assert((decorators & (ON_STRONG_OOP_REF | ON_UNKNOWN_OOP_REF)) != 0, "XCHG only for strong refs OR unknown refs (Unsafe)");
394416
ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
417+
418+
// Unsafe.getAndSet comes here with ON_UNKNOWN_OOP_REF set.
419+
// These are normally strong refs, but one can use Unsafe on Reference.referent.
420+
// We cannot deal with that case. If application does Unsafe operations on
421+
// Reference.referent field, this likely breaks weak reference semantics already.
422+
// We upgrade the access to strong in (sometimes futile) attempt to maintain heap
423+
// integrity, and assert in debug builds for better diagnostics.
395424
DecoratorSet resolved_decorators = AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength<decorators>(base, offset);
425+
assert((resolved_decorators & ON_STRONG_OOP_REF) != 0, "Application error: XCHG on weak location");
426+
resolved_decorators = (resolved_decorators & ~ON_DECORATOR_MASK) | ON_STRONG_OOP_REF;
427+
396428
auto addr = AccessInternal::oop_field_addr<decorators>(base, offset);
397429
oop result = bs->oop_xchg(resolved_decorators, addr, new_value);
398430
if (ShenandoahCardBarrier) {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @summary Test that weak CAS attempt on Reference.referent is handled properly
27+
* @requires vm.gc.Shenandoah
28+
* @library /test/lib
29+
* @modules java.base/jdk.internal.misc:+open
30+
*
31+
* @run driver TestWeakReferenceCAS
32+
*/
33+
34+
import java.util.*;
35+
import java.lang.ref.*;
36+
import java.lang.reflect.*;
37+
import jdk.internal.misc.Unsafe;
38+
39+
import jdk.test.lib.Platform;
40+
import jdk.test.lib.process.ProcessTools;
41+
import jdk.test.lib.process.OutputAnalyzer;
42+
43+
public class TestWeakReferenceCAS {
44+
45+
public static void main(String[] args) throws Exception {
46+
if (args.length == 0) {
47+
driver("CAS");
48+
driver("CAE");
49+
driver("GAS");
50+
} else {
51+
switch (args[0]) {
52+
case "CAS":
53+
Test.testCAS();
54+
break;
55+
case "CAE":
56+
Test.testCAE();
57+
break;
58+
case "GAS":
59+
Test.testGAS();
60+
break;
61+
default:
62+
throw new IllegalArgumentException("Unknown test mode: " + args[0]);
63+
}
64+
}
65+
}
66+
67+
private static void driver(String test) throws Exception {
68+
OutputAnalyzer output = ProcessTools.executeLimitedTestJava(
69+
"-Xmx128m",
70+
"--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
71+
"--add-opens", "java.base/java.lang.ref=ALL-UNNAMED",
72+
"-XX:+UseShenandoahGC",
73+
TestWeakReferenceCAS.class.getName(),
74+
test);
75+
if (Platform.isDebugBuild()) {
76+
output.shouldNotHaveExitValue(0);
77+
output.shouldContain("Application error:");
78+
} else {
79+
output.shouldHaveExitValue(0);
80+
}
81+
}
82+
83+
static class Test {
84+
static final Unsafe UNSAFE = Unsafe.getUnsafe();
85+
static final long OFFSET;
86+
87+
static {
88+
try {
89+
Field f = Reference.class.getDeclaredField("referent");
90+
OFFSET = UNSAFE.objectFieldOffset(f);
91+
} catch (Exception e) {
92+
throw new RuntimeException(e);
93+
}
94+
}
95+
96+
static void testCAS() {
97+
Object obj = new Object();
98+
Object next = new Object();
99+
WeakReference<Object> ref = new WeakReference<>(obj);
100+
UNSAFE.compareAndSetReference(ref, OFFSET, obj, next);
101+
}
102+
103+
static void testCAE() {
104+
Object obj = new Object();
105+
Object next = new Object();
106+
WeakReference<Object> ref = new WeakReference<>(obj);
107+
UNSAFE.compareAndExchangeReference(ref, OFFSET, obj, next);
108+
}
109+
110+
static void testGAS() {
111+
Object obj = new Object();
112+
Object next = new Object();
113+
WeakReference<Object> ref = new WeakReference<>(obj);
114+
UNSAFE.getAndSetReference(ref, OFFSET, next);
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)