CompareAndSet fails intermittently for riscv
yangfei at iscas.ac.cn
yangfei at iscas.ac.cn
Wed Aug 17 02:11:18 UTC 2022
Hi,
> -----Original Messages-----
> From: "Aleksey Shipilev" <shade at redhat.com>
> Sent Time: 2022-08-16 15:54:41 (Tuesday)
> To: "wangyadong (E)" <yadonn.wang at huawei.com>, "Vladimir Kempik" <vladimir.kempik at gmail.com>, "riscv-port-dev at openjdk.org" <riscv-port-dev at openjdk.org>
> Cc:
> Subject: Re: CompareAndSet fails intermittently for riscv
>
> On 8/16/22 08:34, wangyadong (E) wrote:
> > Yes, we know about these test failures, and we've found that these failures also occur on our
> > "powerful" aarch64 servers. I think there is some uncertainty about the test itself, and the
> > probability of passing increased when you raise the test parameter of "weakAttempts".
> Yes, the test itself is flaky.
>
> Unfortunately, disabling the test completely is worse option, because it would miss the actual
> broken weak CASes.
>
> > We now run the weak-cas tests with a high "weakAttempts" on unmatched.
>
> How much you bump weakAttempts to?
>
> I also think we would be better with some backoff in the loop, so that we have more chance in
> succeeding.
I suspect another jtreg test: test/jdk/java/util/concurrent/atomic/Serial.java also have the same issue.
I once reduced this test into [1] and it looks that this will always fail on my unmatched board.
But it passes if we disable the intrinsic like:
$ java -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_weakCompareAndSetLongRelease Serial
Looking at the source code, I think this test also invokes the weakCompareAndSetLongRelease in some way.
In src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java:
public void accumulate(double x) {
Cell[] cs; long b, v, r; int m; Cell c;
if ((cs = cells) != null
|| ((r = doubleToRawLongBits
(function.applyAsDouble(longBitsToDouble(b = base), x))) != b
&& !casBase(b, r))) {
int index = getProbe();
boolean uncontended = true;
if (cs == null
|| (m = cs.length - 1) < 0
|| (c = cs[index & m]) == null
|| !(uncontended =
((r = doubleToRawLongBits
(function.applyAsDouble
(longBitsToDouble(v = c.value), x))) == v)
|| c.cas(v, r))) <========
doubleAccumulate(x, function, uncontended, index);
}
}
In src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java:
@jdk.internal.vm.annotation.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return VALUE.weakCompareAndSetRelease(this, cmp, val); <========
}
........
final void doubleAccumulate(double x, DoubleBinaryOperator fn,
boolean wasUncontended, int index) {
if (index == 0) {
ThreadLocalRandom.current(); // force initialization
index = getProbe();
wasUncontended = true;
}
for (boolean collide = false;;) { // True if last slot nonempty
Cell[] cs; Cell c; int n; long v;
if ((cs = cells) != null && (n = cs.length) > 0) {
if ((c = cs[(n - 1) & index]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(Double.doubleToRawLongBits(x));
if (cellsBusy == 0 && casCellsBusy()) {
try { // Recheck under lock
Cell[] rs; int m, j;
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & index] == null) {
rs[j] = r;
break;
}
} finally {
cellsBusy = 0;
}
continue; // Slot is now non-empty
}
}
collide = false;
}
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
else if (c.cas(v = c.value, apply(fn, v, x))) <========
break;
........
[1]
import java.util.concurrent.atomic.DoubleAccumulator;
import java.util.function.DoubleBinaryOperator;
import java.io.Serializable;
import java.io.IOException;
public class Serial {
public static void main(String[] args) {
for (int i = 0; i < 12000; i++) {
testDoubleAccumulator1(i);
testDoubleAccumulator2(i);
}
}
static DoubleBinaryOperator plus = (DoubleBinaryOperator & Serializable) (x, y) -> x + y;
static DoubleAccumulator a = new DoubleAccumulator(plus, 13.9d);
static DoubleAccumulator b = new DoubleAccumulator(plus, 13.9d);
static void testDoubleAccumulator1(int i) {
a.accumulate(17.5d);
b.accumulate(17.5d);
if (a.get() != b.get())
throw new RuntimeException("Unexpected value, iter: " + i);
}
static void testDoubleAccumulator2(int i) {
a.reset();
b.reset();
if (a.get() != b.get()) {
System.out.println("====> a.get() : " + a.get());
System.out.println("====> b.get() : " + b.get());
throw new RuntimeException("Unexpected value after reset, iter: " + i);
}
}
}</riscv-port-dev at openjdk.org></vladimir.kempik at gmail.com></yadonn.wang at huawei.com></shade at redhat.com>
More information about the riscv-port-dev
mailing list