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