Skip to content

Commit 1318218

Browse files
committed
WIP: add jcstres-based concurrency tests
1 parent ab88031 commit 1318218

18 files changed

Lines changed: 358 additions & 8 deletions

File tree

bom-testing/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies {
1616
api("org.jmockit:jmockit-coverage:1.23")
1717
api("org.jmockit:jmockit:1.50")
1818
api("org.mockito:mockito-core:5.18.0")
19+
api("org.openjdk.jcstress:jcstress-core:0.16")
1920
api("org.testcontainers:junit-jupiter:1.21.3")
2021
}
2122
}

boot/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@ plugins {
33
id("build-logic.test-junit5")
44
id("build-logic.test-jmockit")
55
id("build-logic.kotlin")
6+
kotlin("kapt")
67
}
78

89
dependencies {
910
testImplementation("ch.qos.logback:logback-classic")
1011
testImplementation("io.mockk:mockk")
12+
testImplementation("org.openjdk.jcstress:jcstress-core")
13+
testAnnotationProcessor(platform(projects.bomTesting))
14+
testAnnotationProcessor("org.openjdk.jcstress:jcstress-core")
15+
testRuntimeOnly(projects.jcstressJupiterEngine)
16+
kaptTest(platform(projects.bomTesting))
17+
kaptTest("org.openjdk.jcstress:jcstress-core")
1118
}
19+

boot/src/main/java/org/qubership/profiler/agent/LocalBuffer.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ public class LocalBuffer {
99

1010
public final long[] data = new long[SIZE];
1111
public final Object[] value = new Object[SIZE];
12-
public long startTime;
13-
public int count;
14-
public int first;
12+
// volatile since we want atomic values as it can be updated by both Dumper and mutator threads
13+
public volatile long startTime;
14+
// volatile as it is updated by mutator (log...) and read by Dumper thread
15+
public volatile int count;
16+
// volatile as it might be updated by both Dumper (stealData) and mutator (buffer.reset()) threads
17+
public volatile int first;
1518
public boolean corrupted;
1619
// Contains the total amount of heap consumed by the large events stored in the buffer
1720
private long largeEventsVolume;

boot/src/main/java/org/qubership/profiler/agent/Profiler.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,10 @@ public static void exchangeBuffer(LocalBuffer buffer,
178178
LocalBuffer newBuffer = state.buffer;
179179
long[] data = newBuffer.data;
180180

181-
if (newBuffer.count > 0) {
182-
System.arraycopy(data, 0, data, 1, newBuffer.count);
183-
}
184-
data[0] = methodAndTime;
181+
int count = newBuffer.count;
182+
data[count] = methodAndTime;
185183

186-
newBuffer.count++;
184+
newBuffer.count = count + 1;
187185
}
188186

189187
public static MetricsConfiguration getMetricConfigByName(String callType) {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.qubership.profiler.agent
2+
3+
import org.junit.platform.commons.annotation.Testable
4+
import org.openjdk.jcstress.annotations.*
5+
import org.openjdk.jcstress.infra.results.IIL_Result
6+
import org.openjdk.jcstress.infra.results.IJL_Result
7+
import org.openjdk.jcstress.infra.results.IJ_Result
8+
import org.openjdk.jcstress.infra.results.IL_Result
9+
10+
@JCStressTest
11+
@Outcome(id = ["0, 0, null"], expect = Expect.ACCEPTABLE, desc = "Count field update was not visible")
12+
@Outcome(id = ["1, 1, value"], expect = Expect.ACCEPTABLE, desc = "Count field update and event value was visible")
13+
@Outcome(id = ["1, .*, null"], expect = Expect.FORBIDDEN, desc = "Event value should be visible if count is visible")
14+
@Outcome(id = ["1, 0, .*"], expect = Expect.FORBIDDEN, desc = "Event tag should be visible if count is visible")
15+
@State
16+
@Testable
17+
open class LocalBufferEventStealTest {
18+
19+
private val localBuffer = LocalBuffer()
20+
21+
@Actor
22+
fun writer() {
23+
localBuffer.event("value", 42)
24+
}
25+
26+
@Actor
27+
fun reader(r: IIL_Result) {
28+
val count = localBuffer.count
29+
r.r1 = count
30+
if (count > 0) {
31+
r.r2 = if (localBuffer.data[0] == 0L) 0 else 1;
32+
r.r3 = localBuffer.value[0]
33+
}
34+
}
35+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.qubership.profiler.agent
2+
3+
import org.junit.platform.commons.annotation.Testable
4+
import org.openjdk.jcstress.annotations.*
5+
import org.openjdk.jcstress.infra.results.IJ_Result
6+
7+
@JCStressTest
8+
@Outcome(id = ["0, 0"], expect = Expect.ACCEPTABLE, desc = "Count field update was not visible")
9+
@Outcome(id = ["1, 1311768467463790320"], expect = Expect.ACCEPTABLE, desc = "Count field update and enter event was visible")
10+
@Outcome(id = ["1, 0"], expect = Expect.FORBIDDEN, desc = "Method enter event should be visible if count is visible")
11+
@State
12+
@Testable
13+
open class LocalBufferInitEnterStealTest {
14+
15+
private val localBuffer = LocalBuffer()
16+
17+
@Actor
18+
fun writer() {
19+
localBuffer.initTimedEnter(0x1234_5678_9abc_def0L)
20+
}
21+
22+
@Actor
23+
fun reader(r: IJ_Result) {
24+
val count = localBuffer.count
25+
r.r1 = count
26+
if (count > 0) {
27+
r.r2 = localBuffer.data[0]
28+
}
29+
}
30+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.qubership.profiler.agent
2+
3+
import org.junit.platform.commons.annotation.Testable
4+
import org.openjdk.jcstress.annotations.*
5+
import org.openjdk.jcstress.infra.results.IJ_Result
6+
7+
@JCStressTest
8+
@Outcome(id = ["0, 0"], expect = Expect.ACCEPTABLE, desc = "Count field update was not visible or the buffer was reset")
9+
@Outcome(id = ["1, 1311768467463790320"], expect = Expect.ACCEPTABLE, desc = "Count field update and enter event was visible")
10+
@Outcome(id = ["1, 0"], expect = Expect.FORBIDDEN, desc = "Method enter event should be visible if count is visible")
11+
@State
12+
@Testable
13+
open class LocalBufferResetStealTest {
14+
15+
private val localBuffer = LocalBuffer()
16+
17+
@Actor
18+
fun writer() {
19+
localBuffer.initTimedEnter(0x1234_5678_9abc_def0L)
20+
localBuffer.reset()
21+
}
22+
23+
@Actor
24+
fun reader(r: IJ_Result) {
25+
val count = localBuffer.count
26+
r.r1 = count
27+
if (count > 0) {
28+
r.r2 = localBuffer.data[0]
29+
}
30+
}
31+
}

build-logic/jvm/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ dependencies {
66
implementation(project(":basics"))
77
implementation(project(":build-parameters"))
88
implementation(project(":verification"))
9+
api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:2.2.0")
10+
api("org.jetbrains.kotlin.kapt:org.jetbrains.kotlin.kapt.gradle.plugin:2.2.0")
911
implementation("com.github.vlsi.crlf:com.github.vlsi.crlf.gradle.plugin:2.0.0")
1012
implementation("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:2.0.0")
1113
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ plugins {
66
id("com.github.vlsi.ide")
77
id("com.github.vlsi.gradle-extensions")
88
id("jacoco")
9+
kotlin("jvm") apply false
910
}
1011

1112
ide {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
plugins {
2+
id("build-logic.java-library")
3+
}
4+
5+
dependencies {
6+
api(platform(projects.bomTesting))
7+
api("org.junit.jupiter:junit-jupiter-engine")
8+
api("org.openjdk.jcstress:jcstress-core")
9+
}

0 commit comments

Comments
 (0)