Skip to content

Commit 3783fe8

Browse files
retronymSethTisue
authored andcommitted
First attempt to write a jcstress test show List is not safely publishable
1 parent 5d34914 commit 3783fe8

File tree

7 files changed

+234
-0
lines changed

7 files changed

+234
-0
lines changed

test/jcstress/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.gz
2+
results

test/jcstress/build.sbt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
enablePlugins(JCStressPlugin)
2+
3+
scalaVersion := "2.13.0-pre-SNAPSHOT"
4+
5+
scalaHome := Some(baseDirectory.value / "../../build/pack")
6+
version in Jcstress := "0.4"
7+
resolvers in Global += Resolver.mavenLocal
8+
crossPaths := false
9+
10+
// workaround https://github.com/ktoso/sbt-jcstress/issues/12
11+
internalDependencyClasspath in Test := Nil
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=1.1.5

test/jcstress/project/plugins.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("pl.project13.scala" % "sbt-jcstress" % "0.2.0")
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package example
2+
3+
/*
4+
* Copyright (c) 2016, Red Hat Inc.
5+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6+
*
7+
* This code is free software; you can redistribute it and/or modify it
8+
* under the terms of the GNU General Public License version 2 only, as
9+
* published by the Free Software Foundation. Oracle designates this
10+
* particular file as subject to the "Classpath" exception as provided
11+
* by Oracle in the LICENSE file that accompanied this code.
12+
*
13+
* This code is distributed in the hope that it will be useful, but WITHOUT
14+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16+
* version 2 for more details (a copy is included in the LICENSE file that
17+
* accompanied this code).
18+
*
19+
* You should have received a copy of the GNU General Public License version
20+
* 2 along with this work; if not, write to the Free Software Foundation,
21+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22+
*
23+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
24+
* or visit www.oracle.com if you need additional information or have any
25+
* questions.
26+
*/
27+
28+
import java.util.concurrent.atomic._
29+
30+
import org.openjdk.jcstress.annotations.Outcome.Outcomes
31+
import org.openjdk.jcstress.annotations._
32+
import org.openjdk.jcstress.infra.results.II_Result
33+
34+
/*
35+
This is our first concurrency test. It is deliberately simplistic to show
36+
testing approaches, introduce JCStress APIs, etc.
37+
Suppose we want to see if the field increment is atomic. We can make test
38+
with two actors, both actors incrementing the field and recording what
39+
value they observed into the result object. As JCStress runs, it will
40+
invoke these methods on the objects holding the field once per each actor
41+
and instance, and record what results are coming from there.
42+
Done enough times, we will get the history of observed results, and that
43+
would tell us something about the concurrent behavior. For example, running
44+
this test would yield:
45+
[OK] o.o.j.t.JCStressSample_01_Simple
46+
(JVM args: [-server])
47+
Observed state Occurrences Expectation Interpretation
48+
1, 1 54,734,140 ACCEPTABLE Both threads came up with the same value: atomicity failure.
49+
1, 2 47,037,891 ACCEPTABLE actor1 incremented, then actor2.
50+
2, 1 53,204,629 ACCEPTABLE actor2 incremented, then actor1.
51+
How to run this test:
52+
$ java -jar jcstress-samples/target/jcstress.jar -t JCStress_APISample_01_Simple
53+
*/
54+
55+
// Mark the class as JCStress test.
56+
@JCStressTest
57+
@Description("Simple test, checking AtomicInteger")
58+
@Outcomes(Array(
59+
new Outcome(
60+
id = Array("1, 1"),
61+
expect = Expect.ACCEPTABLE_INTERESTING,
62+
desc = "Both actors came up with the same value: atomicity failure."),
63+
new Outcome(
64+
id = Array("1, 2"),
65+
expect = Expect.ACCEPTABLE,
66+
desc = "actor1 incremented, then actor2."),
67+
new Outcome(id = Array("2, 1"),
68+
expect = Expect.ACCEPTABLE, desc = "actor2 incremented, then actor1.")
69+
)
70+
)
71+
@State
72+
class APISample_01_Simple {
73+
74+
var v = new AtomicInteger(0)
75+
76+
@Actor
77+
def actor1(r: II_Result): Unit = {
78+
r.r1 = v.incrementAndGet() // record result from actor1 to field r1
79+
}
80+
81+
@Actor
82+
def actor2(r: II_Result): Unit = {
83+
r.r2 = v.incrementAndGet() // record result from actor1 to field r1
84+
}
85+
86+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package scala.collection.immutable
2+
3+
import org.openjdk.jcstress.annotations._
4+
import org.openjdk.jcstress.annotations.Outcome.Outcomes
5+
import org.openjdk.jcstress.infra.results._
6+
7+
@JCStressTest
8+
@Outcomes(Array(
9+
new Outcome(id = Array("-1, 0"), expect = Expect.ACCEPTABLE, desc = "Read before write"),
10+
new Outcome(id = Array("16, 0"), expect = Expect.ACCEPTABLE, desc = "Read after all writes")
11+
))
12+
@State
13+
class ListLikeStressTest {
14+
15+
var v: ListLike = _
16+
17+
@Actor
18+
def actor1(r: II_Result): Unit = {
19+
val list = new ListLike("a")
20+
var l = list
21+
l.tail = new ListLike("a")
22+
l = l.tail
23+
l.tail = new ListLike("a")
24+
l = l.tail
25+
l.tail = new ListLike("a")
26+
l = l.tail
27+
l.tail = new ListLike("a")
28+
l = l.tail
29+
l.tail = new ListLike("a")
30+
l = l.tail
31+
l.tail = new ListLike("a")
32+
l = l.tail
33+
l.tail = new ListLike("a")
34+
l = l.tail
35+
l.tail = new ListLike("a")
36+
l = l.tail
37+
l.tail = new ListLike("a")
38+
l = l.tail
39+
l.tail = new ListLike("a")
40+
l = l.tail
41+
l.tail = new ListLike("a")
42+
l = l.tail
43+
l.tail = new ListLike("a")
44+
l = l.tail
45+
l.tail = new ListLike("a")
46+
l = l.tail
47+
l.tail = new ListLike("a")
48+
l = l.tail
49+
l.tail = new ListLike("a")
50+
l = l.tail
51+
l.tail = NilLike
52+
//java.lang.invoke.VarHandle.releaseFence()
53+
v = list
54+
55+
}
56+
57+
@Actor
58+
def actor2(r: II_Result): Unit = {
59+
var l = v
60+
if (l eq null) {
61+
r.r1 = -1
62+
r.r2 = 0
63+
return
64+
}
65+
var len = 0
66+
var nulls = 0
67+
while (l ne NilLike) {
68+
if (l eq null) {r.r1 = len; r.r2 = nulls + 1; return}
69+
if (l.head eq null) nulls += 1
70+
assert(l ne l.tail)
71+
l = l.tail
72+
len += 1
73+
}
74+
r.r1 = len
75+
r.r2 = nulls
76+
77+
}
78+
79+
}
80+
81+
class ListLike(var head: AnyRef) {
82+
var tail: ListLike = _
83+
}
84+
85+
object NilLike extends ListLike(null)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package scala.collection.immutable
2+
3+
import org.openjdk.jcstress.annotations._
4+
import org.openjdk.jcstress.annotations.Outcome.Outcomes
5+
import org.openjdk.jcstress.infra.results.I_Result
6+
7+
@JCStressTest
8+
@Outcomes(Array(
9+
new Outcome(id = Array("0"), expect = Expect.ACCEPTABLE, desc = "Read before write"),
10+
new Outcome(id = Array("1"), expect = Expect.ACCEPTABLE, desc = "Read after all writes"),
11+
new Outcome(id = Array("2"), expect = Expect.FORBIDDEN, desc = "Interleaved read / write")
12+
))
13+
@State
14+
class ListStressTest {
15+
16+
var v: List[AnyRef] = _
17+
18+
var elem1: AnyRef = _
19+
var elem2: AnyRef = _
20+
var nil: AnyRef = _
21+
22+
@Actor
23+
def actor1(): Unit = {
24+
val builder = List.newBuilder[String]
25+
builder += "a"
26+
builder += "b"
27+
v = builder.result
28+
}
29+
30+
@Actor
31+
def actor2(): Unit = {
32+
val v1 = v
33+
if (v1 ne null) {
34+
elem1 = v1.head
35+
val tail = v1.tail
36+
elem2 = tail.head
37+
nil = tail.tail
38+
}
39+
}
40+
41+
@Arbiter
42+
def arbiter(r: I_Result): Unit = {
43+
r.r1 =
44+
if (elem2 == null && elem2 == null && nil == null) 0
45+
else if (elem1 == "a" && elem2 == "b" && nil == Nil) 1
46+
else 2
47+
}
48+
}

0 commit comments

Comments
 (0)