最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - JCStress test results that I can't understand - Stack Overflow

programmeradmin9浏览0评论

Can somebody help me understand why I'm getting FORBIDDEN results in this JCStress? The scenario is simple.

We have one actor that records a result of some operation then changes the state (which is volatile). We also have two actors that are exactly the same - they record the state and the result of the operation specifically in that order. The test doesn't give any forbidden results with single reading actor.

public class RequestResultTest {

    @JCStressTest
    @Outcome(id = "1, 12345", expect = Expect.ACCEPTABLE)
    @Outcome(id = "0, 0", expect = Expect.ACCEPTABLE)
    @Outcome(id = "0, 12345", expect = Expect.ACCEPTABLE)
    @Outcome(id = "1, 0", expect = Expect.FORBIDDEN, desc = "State is finished, but not result recorded")
    @State
    public static class SingleWriterMultiReaderTest {

        private final SomeResult underTest;

        public SingleWriterMultiReaderTest() {
            this.underTest = new SomeResult();
        }

        @Actor
        public void finish() {
            underTest.finish(12345L);
        }

        @Actor
        public void getStateAndResult1(IJ_Result result) {
            int state = underTest.getState();
            long longResult = underTest.getValue();

            result.r1 = state;
            result.r2 = longResult;
        }

        @Actor
        public void getStateAndResult2(IJ_Result result) {
            int state = underTest.getState();
            long longResult = underTest.getValue();

            result.r1 = state;
            result.r2 = longResult;
        }
    }

    public static final class SomeResult {

        public static final int RUNNING = 0;
        public static final int FINISHED = 1;

        private volatile int state;
        private long value;

        public SomeResult() {
            this.value = 0L;
            this.state = RUNNING;
        }

        void finish(long value) {
            this.value = value;
            this.state = FINISHED;
        }

        public long getValue() {
            return value;
        }

        public int getState() {
            return state;
        }
    }
}

Here is one of the outputs.

  Compilation: split
    finish: Interpreter
    getStateAndResult1: C1
    getStateAndResult2: C2

  JVM args: [-Dfile.encoding=UTF-8, -XX:-UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=1773949580]
  Fork: #1

    RESULT      SAMPLES     FREQ      EXPECT  DESCRIPTION
      0, 0  142,382,593   97,91%  Acceptable  
  0, 12345      116,937    0,08%  Acceptable  
      1, 0        7,940   <0,01%   Forbidden  State is finished, but not result recorded
  1, 12345    2,908,741    2,00%  Acceptable  

Can somebody help me understand why I'm getting FORBIDDEN results in this JCStress? The scenario is simple.

We have one actor that records a result of some operation then changes the state (which is volatile). We also have two actors that are exactly the same - they record the state and the result of the operation specifically in that order. The test doesn't give any forbidden results with single reading actor.

public class RequestResultTest {

    @JCStressTest
    @Outcome(id = "1, 12345", expect = Expect.ACCEPTABLE)
    @Outcome(id = "0, 0", expect = Expect.ACCEPTABLE)
    @Outcome(id = "0, 12345", expect = Expect.ACCEPTABLE)
    @Outcome(id = "1, 0", expect = Expect.FORBIDDEN, desc = "State is finished, but not result recorded")
    @State
    public static class SingleWriterMultiReaderTest {

        private final SomeResult underTest;

        public SingleWriterMultiReaderTest() {
            this.underTest = new SomeResult();
        }

        @Actor
        public void finish() {
            underTest.finish(12345L);
        }

        @Actor
        public void getStateAndResult1(IJ_Result result) {
            int state = underTest.getState();
            long longResult = underTest.getValue();

            result.r1 = state;
            result.r2 = longResult;
        }

        @Actor
        public void getStateAndResult2(IJ_Result result) {
            int state = underTest.getState();
            long longResult = underTest.getValue();

            result.r1 = state;
            result.r2 = longResult;
        }
    }

    public static final class SomeResult {

        public static final int RUNNING = 0;
        public static final int FINISHED = 1;

        private volatile int state;
        private long value;

        public SomeResult() {
            this.value = 0L;
            this.state = RUNNING;
        }

        void finish(long value) {
            this.value = value;
            this.state = FINISHED;
        }

        public long getValue() {
            return value;
        }

        public int getState() {
            return state;
        }
    }
}

Here is one of the outputs.

  Compilation: split
    finish: Interpreter
    getStateAndResult1: C1
    getStateAndResult2: C2

  JVM args: [-Dfile.encoding=UTF-8, -XX:-UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=1773949580]
  Fork: #1

    RESULT      SAMPLES     FREQ      EXPECT  DESCRIPTION
      0, 0  142,382,593   97,91%  Acceptable  
  0, 12345      116,937    0,08%  Acceptable  
      1, 0        7,940   <0,01%   Forbidden  State is finished, but not result recorded
  1, 12345    2,908,741    2,00%  Acceptable  
Share Improve this question asked Feb 5 at 9:14 alphashockalphashock 618 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

The second actor 'getStateAndResult2' is the cause. If you remove it, it will run without problems.

The cause of the unexpected behavior is that the same result instance is shared between the getStateAndResult1 and getStateAndResult2 actors which causes a race condition.

Check this version that is part of JCstress for causality testing:

https://github.com/openjdk/jcstress/blob/master/jcstress-samples/src/main/java/org/openjdk/jcstress/samples/jmm/basic/BasicJMM_06_Causality.java

I made a slightly modified version that demonstrates that the instance is shared:

package com.example.jcstress;

import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.II_Result;
import org.openjdk.jcstress.infra.results.IJ_Result;

import java.util.concurrent.atomic.AtomicReference;

public class RequestResultTest {

    @JCStressTest
    @Outcome(id = "1, 12345", expect = Expect.ACCEPTABLE)
    @Outcome(id = "0, 0", expect = Expect.ACCEPTABLE)
    @Outcome(id = "0, 12345", expect = Expect.ACCEPTABLE)
    @Outcome(id = "1, 0", expect = Expect.FORBIDDEN, desc = "State is finished, but not result recorded")
    @State
    public static class SingleWriterMultiReaderTest {

        private volatile int state;
        private long value;

        private volatile IJ_Result result1;
        private volatile IJ_Result result2;

        public SingleWriterMultiReaderTest() {
            this.value = 0L;
            this.state = 0;

        }

        @Actor
        public void actor1() {
            this.value = 12345L;
            this.state = 1;
        }

        @Actor
        public void actor2(IJ_Result result) {
            result1 = result;

            if(result2 == result){
                throw new RuntimeException();
            }

            result.r1 = state;
            result.r2 = value;
        }

        @Actor
        public void actor3(IJ_Result result) {
            result2 = result;

            if(result1 == result){
                throw new RuntimeException();
            }

            result.r1 = state;
            result.r2 = value;
        }
    }
}
发布评论

评论列表(0)

  1. 暂无评论