From 049feeefa8dfe2901c3f822941b2a070374ace76 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 9 Oct 2023 18:00:50 +0200 Subject: [PATCH 01/87] Setup jcstress submodule --- .gitignore | 2 - {jcstests => jcstress-tests}/Custom_LB.java | 0 jcstress/.gitignore | 1 + jcstress/pom.xml | 121 ++++++++++++++++++ .../main/java/org/sample/ConcurrencyTest.java | 54 ++++++++ litmus/.gitignore | 2 + 6 files changed, 178 insertions(+), 2 deletions(-) rename {jcstests => jcstress-tests}/Custom_LB.java (100%) create mode 100644 jcstress/.gitignore create mode 100644 jcstress/pom.xml create mode 100644 jcstress/src/main/java/org/sample/ConcurrencyTest.java create mode 100644 litmus/.gitignore diff --git a/.gitignore b/.gitignore index b741e1c..c3483a0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,3 @@ build/ temp/ *.hprof gitignored/ -litmus/src/nativeInterop/kaffinity.def -litmus/src/nativeInterop/kaffinity_gnu.o diff --git a/jcstests/Custom_LB.java b/jcstress-tests/Custom_LB.java similarity index 100% rename from jcstests/Custom_LB.java rename to jcstress-tests/Custom_LB.java diff --git a/jcstress/.gitignore b/jcstress/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/jcstress/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/jcstress/pom.xml b/jcstress/pom.xml new file mode 100644 index 0000000..2f4ed29 --- /dev/null +++ b/jcstress/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + org.sample + test + 1.0 + jar + + JCStress test sample + + + + + 3.2 + + + + + org.openjdk.jcstress + jcstress-core + ${jcstress.version} + + + + + UTF-8 + + + 0.16 + + + 1.8 + + + jcstress + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + ${javac.target} + ${javac.target} + ${javac.target} + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + main + package + + shade + + + ${uberjar.name} + + + org.openjdk.jcstress.Main + + + META-INF/TestList + + + + + + + + + + diff --git a/jcstress/src/main/java/org/sample/ConcurrencyTest.java b/jcstress/src/main/java/org/sample/ConcurrencyTest.java new file mode 100644 index 0000000..fa2664a --- /dev/null +++ b/jcstress/src/main/java/org/sample/ConcurrencyTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sample; + +import org.openjdk.jcstress.annotations.*; +import org.openjdk.jcstress.infra.results.II_Result; + +// See jcstress-samples or existing tests for API introduction and testing guidelines + +@JCStressTest +// Outline the outcomes here. The default outcome is provided, you need to remove it: +@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "Default outcome.") +@State +public class ConcurrencyTest { + + @Actor + public void actor1(II_Result r) { + // Put the code for first thread here + } + + @Actor + public void actor2(II_Result r) { + // Put the code for second thread here + } + +} diff --git a/litmus/.gitignore b/litmus/.gitignore new file mode 100644 index 0000000..04f9a31 --- /dev/null +++ b/litmus/.gitignore @@ -0,0 +1,2 @@ +src/nativeInterop/kaffinity.def +src/nativeInterop/kaffinity_gnu.o From 1b2645ca946720a6b959e485df17e23ba76b0b8a Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Tue, 10 Oct 2023 13:48:42 +0200 Subject: [PATCH 02/87] Struggling to call kotlin from jcstress maven project --- jcstress/.gitignore | 1 + jcstress/pom.xml | 28 +++++++++++++++++ .../src/main/java/org/sample/AbaTest.java | 15 +++++++++ .../main/java/org/sample/ConcurrencyTest.java | 4 ++- litmus/build.gradle.kts | 9 ++++++ .../kotlin/komem/litmus/LitmusTest.kt | 4 +-- litmus/src/jvmMain/java/AbaTest.java | 31 +++++++++++++++++++ .../kotlin/komem/litmus/NativeTest.kt | 2 +- 8 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 jcstress/src/main/java/org/sample/AbaTest.java create mode 100644 litmus/src/jvmMain/java/AbaTest.java diff --git a/jcstress/.gitignore b/jcstress/.gitignore index 2f7896d..4c72a9b 100644 --- a/jcstress/.gitignore +++ b/jcstress/.gitignore @@ -1 +1,2 @@ target/ +libs/ diff --git a/jcstress/pom.xml b/jcstress/pom.xml index 2f4ed29..012c16e 100644 --- a/jcstress/pom.xml +++ b/jcstress/pom.xml @@ -55,6 +55,11 @@ THE POSSIBILITY OF SUCH DAMAGE. jcstress-core ${jcstress.version} + + komem.litmus + litmuskt + 1.0 + @@ -115,6 +120,29 @@ THE POSSIBILITY OF SUCH DAMAGE. + + + org.apache.maven.plugins + maven-install-plugin + 3.1.1 + + komem.litmus + litmuskt + 1.0 + jar + ${basedir}/libs/litmus-jvm-1.0-SNAPSHOT.jar + true + + + + install-jar-lib + + install-file + + validate + + + diff --git a/jcstress/src/main/java/org/sample/AbaTest.java b/jcstress/src/main/java/org/sample/AbaTest.java new file mode 100644 index 0000000..6c05b03 --- /dev/null +++ b/jcstress/src/main/java/org/sample/AbaTest.java @@ -0,0 +1,15 @@ +package org.sample; + +import komem.litmus.LitmusTest; + +public class AbaTest { + + private List> fs; + private Function1 arbiter; + + public AbaTest(LitmusTest test) { + fs = test.getThreadFunctions(); + arbiter = test.getOutcomeFinalizer(); + } + +} diff --git a/jcstress/src/main/java/org/sample/ConcurrencyTest.java b/jcstress/src/main/java/org/sample/ConcurrencyTest.java index fa2664a..344e215 100644 --- a/jcstress/src/main/java/org/sample/ConcurrencyTest.java +++ b/jcstress/src/main/java/org/sample/ConcurrencyTest.java @@ -33,6 +33,8 @@ import org.openjdk.jcstress.annotations.*; import org.openjdk.jcstress.infra.results.II_Result; +import komem.litmus.AffinityMap; + // See jcstress-samples or existing tests for API introduction and testing guidelines @JCStressTest @@ -43,7 +45,7 @@ public class ConcurrencyTest { @Actor public void actor1(II_Result r) { - // Put the code for first thread here + } @Actor diff --git a/litmus/build.gradle.kts b/litmus/build.gradle.kts index 2f3ef02..0b1f60a 100644 --- a/litmus/build.gradle.kts +++ b/litmus/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.incremental.createDirectory plugins { kotlin("multiplatform") id("com.google.devtools.ksp") version "1.9.10-1.0.13" + `java-library` } group = "komem.litmus" @@ -34,6 +35,8 @@ kotlin { mainRun { mainClass.set("JvmMainKt") } + withSourcesJar() + withJava() } val affinitySupported = hostOs == "Linux" @@ -153,3 +156,9 @@ tasks.whenTaskAdded { tasks.matching { it.name.startsWith("compileKotlin") }.forEach { it.dependsOn(kspTask) } } } + +tasks.register("copyLibToJCStress") { + dependsOn("jvmJar") + from(buildDir.resolve("libs/litmus-jvm-$version.jar")) + into(projectDir.resolve("../jcstress/libs/")) +} diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusTest.kt b/litmus/src/commonMain/kotlin/komem/litmus/LitmusTest.kt index bd04a6a..7f8bad3 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/LitmusTest.kt +++ b/litmus/src/commonMain/kotlin/komem/litmus/LitmusTest.kt @@ -2,7 +2,7 @@ package komem.litmus data class LitmusTest( val stateProducer: () -> S, - val threadFunctions: List Any?>, + val threadFunctions: List Unit>, val outcomeFinalizer: (S.() -> LitmusOutcome), val outcomeSpec: LitmusOutcomeSpec ) { @@ -12,7 +12,7 @@ data class LitmusTest( class LitmusTestScope( private val stateProducer: () -> S ) { - private val threadFunctions = mutableListOf Any?>() + private val threadFunctions = mutableListOf Unit>() private lateinit var outcomeFinalizer: S.() -> LitmusOutcome private lateinit var outcomeSpec: LitmusOutcomeSpecScope diff --git a/litmus/src/jvmMain/java/AbaTest.java b/litmus/src/jvmMain/java/AbaTest.java new file mode 100644 index 0000000..3834d63 --- /dev/null +++ b/litmus/src/jvmMain/java/AbaTest.java @@ -0,0 +1,31 @@ +import komem.litmus.LitmusTest; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +import java.util.List; + +public class AbaTest { + + private static List> fs; + private static Function1 arbiter; + + public AbaTest(LitmusTest test) { + fs = test.getThreadFunctions(); + arbiter = test.getOutcomeFinalizer(); + } + +} + + +/* + +class LitmusJcsState { + private static (!!!) List> tfs = test.getThreadFunctions(); + + private S state = stateProducer(); + + public void actor0() { + tfs[0].invoke(state); + } +} + */ diff --git a/litmus/src/nativeTest/kotlin/komem/litmus/NativeTest.kt b/litmus/src/nativeTest/kotlin/komem/litmus/NativeTest.kt index afed7f8..1c05a4c 100644 --- a/litmus/src/nativeTest/kotlin/komem/litmus/NativeTest.kt +++ b/litmus/src/nativeTest/kotlin/komem/litmus/NativeTest.kt @@ -10,6 +10,6 @@ class NativeTest { val test = ATOM val runner = WorkerRunner val params = LitmusRunParams(1_000_000, 100, null, ::CinteropSpinBarrier) - runner.runTest(params, test).prettyPrint() + runner.runTest(params, test) } } From c5160dcc90336cf2b5ec165e574e65a572e52218 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Wed, 18 Oct 2023 13:41:56 +0200 Subject: [PATCH 03/87] Manually connect and run a test with jcstress --- gradle/wrapper/gradle-wrapper.properties | 2 +- jcstress/.gitignore | 3 ++ jcstress/pom.xml | 8 +++- .../src/main/java/org/sample/AbaTest.java | 15 ------- .../src/main/java/org/sample/LitmusSB.java | 43 +++++++++++++++++++ litmus/build.gradle.kts | 2 +- 6 files changed, 55 insertions(+), 18 deletions(-) delete mode 100644 jcstress/src/main/java/org/sample/AbaTest.java create mode 100644 jcstress/src/main/java/org/sample/LitmusSB.java diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fc..e411586 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/jcstress/.gitignore b/jcstress/.gitignore index 4c72a9b..7fd277f 100644 --- a/jcstress/.gitignore +++ b/jcstress/.gitignore @@ -1,2 +1,5 @@ target/ libs/ +results/ +*.bin.gz +test.iml diff --git a/jcstress/pom.xml b/jcstress/pom.xml index 012c16e..db85c2b 100644 --- a/jcstress/pom.xml +++ b/jcstress/pom.xml @@ -60,6 +60,11 @@ THE POSSIBILITY OF SUCH DAMAGE. litmuskt 1.0 + + org.jetbrains.kotlin + kotlin-stdlib + 1.9.10 + @@ -112,7 +117,8 @@ THE POSSIBILITY OF SUCH DAMAGE. implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> org.openjdk.jcstress.Main - + META-INF/TestList diff --git a/jcstress/src/main/java/org/sample/AbaTest.java b/jcstress/src/main/java/org/sample/AbaTest.java deleted file mode 100644 index 6c05b03..0000000 --- a/jcstress/src/main/java/org/sample/AbaTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sample; - -import komem.litmus.LitmusTest; - -public class AbaTest { - - private List> fs; - private Function1 arbiter; - - public AbaTest(LitmusTest test) { - fs = test.getThreadFunctions(); - arbiter = test.getOutcomeFinalizer(); - } - -} diff --git a/jcstress/src/main/java/org/sample/LitmusSB.java b/jcstress/src/main/java/org/sample/LitmusSB.java new file mode 100644 index 0000000..f467b4e --- /dev/null +++ b/jcstress/src/main/java/org/sample/LitmusSB.java @@ -0,0 +1,43 @@ +package org.sample; + +import komem.litmus.LitmusTest; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; +import komem.litmus.testsuite.ClassicTestsKt; +import org.openjdk.jcstress.annotations.*; +import org.openjdk.jcstress.infra.results.II_Result; + +import java.util.List; + +@JCStressTest +@State +public class LitmusSB { + + private static final LitmusTest sb = (LitmusTest) ClassicTestsKt.getSB(); + private static final Function1 fT0 = sb.getThreadFunctions().get(0); + private static final Function1 fT1 = sb.getThreadFunctions().get(1); + private static final Function1 fA = sb.getOutcomeFinalizer(); + + public LitmusSB() { + } + + public Object state = sb.getStateProducer().invoke(); + + @Actor + public void t1() { + fT0.invoke(state); + } + + @Actor + public void t2() { + fT1.invoke(state); + } + + @Arbiter + public void a(II_Result r) { + List result = (List) fA.invoke(state); + r.r1 = result.get(0); + r.r2 = result.get(1); + } + +} diff --git a/litmus/build.gradle.kts b/litmus/build.gradle.kts index 0b1f60a..4f04948 100644 --- a/litmus/build.gradle.kts +++ b/litmus/build.gradle.kts @@ -36,7 +36,7 @@ kotlin { mainClass.set("JvmMainKt") } withSourcesJar() - withJava() + jvmToolchain(8) } val affinitySupported = hostOs == "Linux" From 94af8b93a8738d77d7885a77ec50ba4fe05e087e Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 5 Nov 2023 21:39:12 +0100 Subject: [PATCH 04/87] Improve build files --- build.gradle.kts | 2 +- codegen/build.gradle.kts | 5 +- gradle.properties | 3 ++ litmus/build.gradle.kts | 114 ++++++++++++++++----------------------- 4 files changed, 52 insertions(+), 72 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 85d5923..6a659e6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version "1.9.10" apply false + kotlin("multiplatform") version "1.9.20" apply false } repositories { diff --git a/codegen/build.gradle.kts b/codegen/build.gradle.kts index ef3b11e..b32f804 100644 --- a/codegen/build.gradle.kts +++ b/codegen/build.gradle.kts @@ -2,16 +2,15 @@ plugins { kotlin("multiplatform") } -group = "com.example" +group = "komem.litmus" version = "1.0-SNAPSHOT" kotlin { jvm() sourceSets { - val jvmMain by getting { + jvmMain { dependencies { implementation("com.google.devtools.ksp:symbol-processing-api:1.9.10-1.0.13") -// implementation(project(":litmus")) } kotlin.srcDir("src/main/kotlin") resources.srcDir("src/main/resources") diff --git a/gradle.properties b/gradle.properties index 0e30cfb..3d6b7e8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,6 @@ kotlin.code.style=official +kotlin.native.ignoreDisabledTargets=true +kotlin.mpp.enableCInteropCommonization=true +mainClass=JvmMainKt # path to custom compiler dist #kotlin.native.home=./fresh-kn-compiler/kotlin-native-linux-x86_64-1.9.0-RC diff --git a/litmus/build.gradle.kts b/litmus/build.gradle.kts index 4f04948..90c4904 100644 --- a/litmus/build.gradle.kts +++ b/litmus/build.gradle.kts @@ -1,105 +1,83 @@ -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.incremental.createDirectory plugins { kotlin("multiplatform") - id("com.google.devtools.ksp") version "1.9.10-1.0.13" + id("com.google.devtools.ksp") version "1.9.20-1.0.13" `java-library` } group = "komem.litmus" version = "1.0-SNAPSHOT" -@OptIn(ExperimentalKotlinGradlePluginApi::class) kotlin { - targetHierarchy.default { - common { - withJvm() - withNative() - withLinux() - withMacos() - } - } - - val armEnabled = findProperty("arm") != null - val hostOs = System.getProperty("os.name") -// val isMingwX64 = hostOs.startsWith("Windows") - - val nativeTarget = when { - hostOs == "Mac OS X" -> if (armEnabled) macosArm64() else macosX64() - hostOs == "Linux" -> linuxX64() - else -> throw GradleException("Host OS is not supported") - } - val jvmTarget = jvm { - // executable by default - mainRun { - mainClass.set("JvmMainKt") - } + val nativeTargets = listOf( + linuxX64(), +// linuxArm64(), // 1) no machine currently available 2) CLI library does not support + macosX64(), + macosArm64(), + ) + + jvm { withSourcesJar() jvmToolchain(8) } + val hostOs = System.getProperty("os.name") val affinitySupported = hostOs == "Linux" - nativeTarget.apply { - compilations.getByName("main") { - cinterops { - val barrier by creating { - defFile(project.file("src/nativeInterop/barrier.def")) - headers(project.file("src/nativeInterop/barrier.h")) - } - if (affinitySupported) { - val affinity by creating { - defFile(project.file("src/nativeInterop/kaffinity.def")) - headers(project.file("src/nativeInterop/kaffinity.h")) + nativeTargets.forEach { target -> + target.apply { + compilations.getByName("main") { + cinterops { + val barrier by creating { + defFile(project.file("src/nativeInterop/barrier.def")) + headers(project.file("src/nativeInterop/barrier.h")) + } + if (affinitySupported) { + val affinity by creating { + defFile(project.file("src/nativeInterop/kaffinity.def")) + headers(project.file("src/nativeInterop/kaffinity.h")) + } } } + if (gradle.startParameter.taskNames.any { it.contains("bitcode") }) { + val tempDir = projectDir.resolve("temp/bitcode") + if (!tempDir.exists()) tempDir.createDirectory() + kotlinOptions.freeCompilerArgs = listOf("-Xtemporary-files-dir=${tempDir.absolutePath}") + } } - if (gradle.startParameter.taskNames.any { it.contains("bitcode") }) { - val tempDir = projectDir.resolve("temp/bitcode") - if (!tempDir.exists()) tempDir.createDirectory() - kotlinOptions.freeCompilerArgs = listOf("-Xtemporary-files-dir=${tempDir.absolutePath}") - } - } - binaries { - executable { - entryPoint = "main" + binaries { + executable { + entryPoint = "main" + } } } } sourceSets { - val commonMain by getting { + commonMain { dependencies { implementation("org.jetbrains.kotlinx:atomicfu:0.20.2") implementation("com.github.ajalt.clikt:clikt:4.2.1") } - kotlin.srcDir(buildDir.resolve("generated/ksp/metadata/commonMain/kotlin/")) // ksp + kotlin.srcDir(layout.buildDirectory.dir("generated/ksp/metadata/commonMain/kotlin/")) // ksp } - val commonTest by getting { + commonTest { dependencies { implementation("org.jetbrains.kotlin:kotlin-test:1.9.0") } } - val nativeMain by getting - val nativeTest by getting - - val jvmMain by getting - val jvmTest by getting - - when { - hostOs == "Mac OS X" -> { - val macosMain by getting { - dependsOn(commonMain) - kotlin.srcDirs("src/macosMain/kotlin") - } + jvmMain { + dependencies { + implementation(kotlin("reflect")) } + } - hostOs == "Linux" -> { - val linuxMain by getting { - dependsOn(commonMain) - kotlin.srcDirs("src/linuxMain/kotlin") - } - } + macosMain { + kotlin.srcDirs("src/macosMain/kotlin") + } + + linuxMain { + kotlin.srcDirs("src/linuxMain/kotlin") } } } @@ -159,6 +137,6 @@ tasks.whenTaskAdded { tasks.register("copyLibToJCStress") { dependsOn("jvmJar") - from(buildDir.resolve("libs/litmus-jvm-$version.jar")) + from(layout.buildDirectory.file("libs/litmus-jvm-$version.jar")) into(projectDir.resolve("../jcstress/libs/")) } From 965ed45e70e27c5b03304668fbb4083c24610aed Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 5 Nov 2023 21:39:52 +0100 Subject: [PATCH 05/87] Generate wrappers in runtime, call jcstress via ProcessBuilder --- jcstress/.gitignore | 1 + .../main/java/org/sample/ConcurrencyTest.java | 56 -------- .../src/main/java/org/sample/LitmusSB.java | 43 ------ litmus/src/jvmMain/java/AbaTest.java | 31 ----- litmus/src/jvmMain/kotlin/JvmMain.kt | 131 +++++++++++++++++- 5 files changed, 129 insertions(+), 133 deletions(-) delete mode 100644 jcstress/src/main/java/org/sample/ConcurrencyTest.java delete mode 100644 jcstress/src/main/java/org/sample/LitmusSB.java delete mode 100644 litmus/src/jvmMain/java/AbaTest.java diff --git a/jcstress/.gitignore b/jcstress/.gitignore index 7fd277f..3346914 100644 --- a/jcstress/.gitignore +++ b/jcstress/.gitignore @@ -3,3 +3,4 @@ libs/ results/ *.bin.gz test.iml +src/main/java/komem/litmus/ diff --git a/jcstress/src/main/java/org/sample/ConcurrencyTest.java b/jcstress/src/main/java/org/sample/ConcurrencyTest.java deleted file mode 100644 index 344e215..0000000 --- a/jcstress/src/main/java/org/sample/ConcurrencyTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017, Red Hat Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Oracle nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.sample; - -import org.openjdk.jcstress.annotations.*; -import org.openjdk.jcstress.infra.results.II_Result; - -import komem.litmus.AffinityMap; - -// See jcstress-samples or existing tests for API introduction and testing guidelines - -@JCStressTest -// Outline the outcomes here. The default outcome is provided, you need to remove it: -@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "Default outcome.") -@State -public class ConcurrencyTest { - - @Actor - public void actor1(II_Result r) { - - } - - @Actor - public void actor2(II_Result r) { - // Put the code for second thread here - } - -} diff --git a/jcstress/src/main/java/org/sample/LitmusSB.java b/jcstress/src/main/java/org/sample/LitmusSB.java deleted file mode 100644 index f467b4e..0000000 --- a/jcstress/src/main/java/org/sample/LitmusSB.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.sample; - -import komem.litmus.LitmusTest; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; -import komem.litmus.testsuite.ClassicTestsKt; -import org.openjdk.jcstress.annotations.*; -import org.openjdk.jcstress.infra.results.II_Result; - -import java.util.List; - -@JCStressTest -@State -public class LitmusSB { - - private static final LitmusTest sb = (LitmusTest) ClassicTestsKt.getSB(); - private static final Function1 fT0 = sb.getThreadFunctions().get(0); - private static final Function1 fT1 = sb.getThreadFunctions().get(1); - private static final Function1 fA = sb.getOutcomeFinalizer(); - - public LitmusSB() { - } - - public Object state = sb.getStateProducer().invoke(); - - @Actor - public void t1() { - fT0.invoke(state); - } - - @Actor - public void t2() { - fT1.invoke(state); - } - - @Arbiter - public void a(II_Result r) { - List result = (List) fA.invoke(state); - r.r1 = result.get(0); - r.r2 = result.get(1); - } - -} diff --git a/litmus/src/jvmMain/java/AbaTest.java b/litmus/src/jvmMain/java/AbaTest.java deleted file mode 100644 index 3834d63..0000000 --- a/litmus/src/jvmMain/java/AbaTest.java +++ /dev/null @@ -1,31 +0,0 @@ -import komem.litmus.LitmusTest; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; - -import java.util.List; - -public class AbaTest { - - private static List> fs; - private static Function1 arbiter; - - public AbaTest(LitmusTest test) { - fs = test.getThreadFunctions(); - arbiter = test.getOutcomeFinalizer(); - } - -} - - -/* - -class LitmusJcsState { - private static (!!!) List> tfs = test.getThreadFunctions(); - - private S state = stateProducer(); - - public void actor0() { - tfs[0].invoke(state); - } -} - */ diff --git a/litmus/src/jvmMain/kotlin/JvmMain.kt b/litmus/src/jvmMain/kotlin/JvmMain.kt index 3c1b685..e160ccf 100644 --- a/litmus/src/jvmMain/kotlin/JvmMain.kt +++ b/litmus/src/jvmMain/kotlin/JvmMain.kt @@ -1,4 +1,129 @@ -import komem.litmus.CliJvm -import komem.litmus.commonMain +import komem.litmus.LitmusAutoOutcome +import komem.litmus.LitmusTest +import komem.litmus.generated.LitmusTestRegistry +import kotlin.io.path.* +import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.full.superclasses +import kotlin.system.exitProcess -fun main(args: Array) = commonMain(args, CliJvm()) +fun main() { + val jcstressDirectory = Path("../jcstress") + + val tests = LitmusTestRegistry.all() + val test = tests[1] + + val javaClassName = test.name.replace('.', '_') + val targetFile = jcstressDirectory / "src/main/java/komem/litmus/$javaClassName.java" + targetFile.createParentDirectories() + val targetCode = generateWrapperCode(test, javaClassName) + + targetFile.writeText(targetCode) + val mvn = ProcessBuilder("mvn", "-f", jcstressDirectory.absolutePathString(), "verify") + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start() + mvn.waitFor() + + if (mvn.exitValue() != 0) { + exitProcess(mvn.exitValue()) + } + + val jcs = ProcessBuilder( + "java", + "-jar", + (jcstressDirectory / "target/jcstress.jar").absolutePathString(), + "-t", + javaClassName + ) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start() + jcs.waitFor() +} + +val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) + +private fun generateWrapperCode(test: LitmusTest<*>, javaClassName: String): String { + val stateClass = test.stateProducer()!!::class + require(stateClass.allSuperclasses.contains(LitmusAutoOutcome::class)) { + "to use JCStress, test state must extend some LitmusAutoOutcome (e.g. LitmusIIOutcome)" + } + + fun javaTestGetter(): String { + val parts = test.name.split(".") + val getter = "get" + parts.last() + "()" + val className = parts.dropLast(1).last() + "Kt" + val packages = parts.dropLast(2) + val packagesLine = if (packages.isEmpty()) "" else packages.joinToString(".", postfix = ".") + return "$packagesLine$className.$getter" + } + + fun javaThreadFunctionDecl(index: Int) = + "\tprivate static final Function1 fT$index = test.getThreadFunctions().get($index);" + + fun javaActorDecl(index: Int) = """ + @Actor + public void t$index() { + fT$index.invoke(state); + } + + """.trimEnd() + + fun javaArbiterDecl(): String { + val autoOutcomeClassList = stateClass.superclasses + .filter { it.isSubclassOf(LitmusAutoOutcome::class) } + require(autoOutcomeClassList.size == 1) { "test state should extend exactly one LitmusAutoOutcome" } + val autoOutcomeClass = autoOutcomeClassList.first() + val outcomeTypeName = autoOutcomeClass.simpleName!! + .removePrefix("Litmus") + .removeSuffix("Outcome") + val (varType, varCount) = when (outcomeTypeName) { + "II" -> "Integer" to 2 + "III" -> "Integer" to 3 + "IIII" -> "Integer" to 4 + else -> error("unknown AutoOutcome type $outcomeTypeName") + } + + val jcstressResultClassName = outcomeTypeName + "_Result" + return """ + @Arbiter + public void a($jcstressResultClassName r) { + List<$varType> result = (List<$varType>) fA.invoke(state); +${List(varCount) { " r.r${it + 1} = result.get($it);" }.joinToString("\n")} + } + + """.trimEnd() + } + + return """ +package komem.litmus; + +import komem.litmus.testsuite.*; + +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +import org.openjdk.jcstress.annotations.*; +import org.openjdk.jcstress.infra.results.*; + +import java.util.List; + +@JCStressTest +@State +@Outcome(expect = Expect.ACCEPTABLE) +public class $javaClassName { + + private static final LitmusTest test = (LitmusTest) ${javaTestGetter()}; +${List(test.threadCount) { javaThreadFunctionDecl(it) }.joinToString("\n")} + private static final Function1 fA = test.getOutcomeFinalizer(); + + public $javaClassName() { + } + + public Object state = test.getStateProducer().invoke(); +${List(test.threadCount) { javaActorDecl(it) }.joinToString("\n")} +${javaArbiterDecl()} +} + """.trimIndent() +} From 10d099622966884c8a0b1424c763dded5d9604cb Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 6 Nov 2023 17:03:29 +0100 Subject: [PATCH 06/87] Add @Outcome annotations, handle single values differently --- gradle.properties | 2 + .../kotlin/komem/litmus/LitmusAutoOutcome.kt | 6 ++ .../komem/litmus/testsuite/ClassicTests.kt | 8 +- litmus/src/jvmMain/kotlin/JvmMain.kt | 86 ++++++++++++++----- 4 files changed, 74 insertions(+), 28 deletions(-) diff --git a/gradle.properties b/gradle.properties index 3d6b7e8..b51bcc4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,8 @@ kotlin.code.style=official + kotlin.native.ignoreDisabledTargets=true kotlin.mpp.enableCInteropCommonization=true +# this one is required for jvmRun task; can also be passed as -DmainClass=JvmMainKt mainClass=JvmMainKt # path to custom compiler dist #kotlin.native.home=./fresh-kn-compiler/kotlin-native-linux-x86_64-1.9.0-RC diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt b/litmus/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt index 5ec8d9d..6ea6d23 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt +++ b/litmus/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt @@ -4,6 +4,12 @@ interface LitmusAutoOutcome { fun getOutcome(): LitmusOutcome } +open class LitmusIOutcome( + var r1: Int = 0, +) : LitmusAutoOutcome { + override fun getOutcome() = r1 // single values are handled differently +} + open class LitmusIIOutcome( var r1: Int = 0, var r2: Int = 0 diff --git a/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt b/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt index b22f51f..2c39419 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt +++ b/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt @@ -10,19 +10,15 @@ class IntHolderCtor { } val ATOM: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var x = 0 - var o = 0 } }) { thread { x = -1 // signed 0xFFFFFFFF } thread { - o = x - } - outcome { - o + r1 = x } spec { accept(0) diff --git a/litmus/src/jvmMain/kotlin/JvmMain.kt b/litmus/src/jvmMain/kotlin/JvmMain.kt index e160ccf..2c7cd31 100644 --- a/litmus/src/jvmMain/kotlin/JvmMain.kt +++ b/litmus/src/jvmMain/kotlin/JvmMain.kt @@ -1,7 +1,11 @@ import komem.litmus.LitmusAutoOutcome +import komem.litmus.LitmusOutcomeType import komem.litmus.LitmusTest import komem.litmus.generated.LitmusTestRegistry -import kotlin.io.path.* +import kotlin.io.path.Path +import kotlin.io.path.createParentDirectories +import kotlin.io.path.div +import kotlin.io.path.writeText import kotlin.reflect.full.allSuperclasses import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.superclasses @@ -11,7 +15,7 @@ fun main() { val jcstressDirectory = Path("../jcstress") val tests = LitmusTestRegistry.all() - val test = tests[1] + val test = tests[0] val javaClassName = test.name.replace('.', '_') val targetFile = jcstressDirectory / "src/main/java/komem/litmus/$javaClassName.java" @@ -19,7 +23,8 @@ fun main() { val targetCode = generateWrapperCode(test, javaClassName) targetFile.writeText(targetCode) - val mvn = ProcessBuilder("mvn", "-f", jcstressDirectory.absolutePathString(), "verify") + val mvn = ProcessBuilder("mvn", "verify") + .directory(jcstressDirectory.toFile()) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .redirectError(ProcessBuilder.Redirect.INHERIT) .start() @@ -32,10 +37,13 @@ fun main() { val jcs = ProcessBuilder( "java", "-jar", - (jcstressDirectory / "target/jcstress.jar").absolutePathString(), + "target/jcstress.jar", "-t", - javaClassName + javaClassName, + "-m", + "quick" ) + .directory(jcstressDirectory.toFile()) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .redirectError(ProcessBuilder.Redirect.INHERIT) .start() @@ -50,6 +58,20 @@ private fun generateWrapperCode(test: LitmusTest<*>, javaClassName: String): Str "to use JCStress, test state must extend some LitmusAutoOutcome (e.g. LitmusIIOutcome)" } + val autoOutcomeClassList = stateClass.superclasses + .filter { it.isSubclassOf(LitmusAutoOutcome::class) } + require(autoOutcomeClassList.size == 1) { "test state should extend exactly one LitmusAutoOutcome" } + val outcomeTypeName = autoOutcomeClassList.first().simpleName!! + .removePrefix("Litmus") + .removeSuffix("Outcome") + val (outcomeVarType, outcomeVarCount) = when (outcomeTypeName) { + "I" -> "Integer" to 1 + "II" -> "Integer" to 2 + "III" -> "Integer" to 3 + "IIII" -> "Integer" to 4 + else -> error("unknown AutoOutcome type $outcomeTypeName") + } + fun javaTestGetter(): String { val parts = test.name.split(".") val getter = "get" + parts.last() + "()" @@ -71,29 +93,46 @@ private fun generateWrapperCode(test: LitmusTest<*>, javaClassName: String): Str """.trimEnd() fun javaArbiterDecl(): String { - val autoOutcomeClassList = stateClass.superclasses - .filter { it.isSubclassOf(LitmusAutoOutcome::class) } - require(autoOutcomeClassList.size == 1) { "test state should extend exactly one LitmusAutoOutcome" } - val autoOutcomeClass = autoOutcomeClassList.first() - val outcomeTypeName = autoOutcomeClass.simpleName!! - .removePrefix("Litmus") - .removeSuffix("Outcome") - val (varType, varCount) = when (outcomeTypeName) { - "II" -> "Integer" to 2 - "III" -> "Integer" to 3 - "IIII" -> "Integer" to 4 - else -> error("unknown AutoOutcome type $outcomeTypeName") - } val jcstressResultClassName = outcomeTypeName + "_Result" - return """ + + return if (outcomeVarCount > 1) { + """ @Arbiter public void a($jcstressResultClassName r) { - List<$varType> result = (List<$varType>) fA.invoke(state); -${List(varCount) { " r.r${it + 1} = result.get($it);" }.joinToString("\n")} + List<$outcomeVarType> result = (List<$outcomeVarType>) fA.invoke(state); +${List(outcomeVarCount) { " r.r${it + 1} = result.get($it);" }.joinToString("\n")} } """.trimEnd() + } else { + // single values are handled differently + """ + @Arbiter + public void a($jcstressResultClassName r) { + r.r1 = ($outcomeVarType) fA.invoke(state); + } + + """.trimEnd() + } + } + + fun jcstressOutcomeDecls(): String { + val outcomes = test.outcomeSpec.accepted.associateWith { "ACCEPTABLE" } + + test.outcomeSpec.interesting.associateWith { "ACCEPTABLE_INTERESTING" } + + test.outcomeSpec.forbidden.associateWith { "FORBIDDEN" } + + // since only AutoOutcome is allowed, each outcome is a list (unless it's a single value) + return outcomes.map { (o, t) -> + val oId = if (outcomeVarCount > 1) (o as List<*>).joinToString(", ") else o.toString() + "@Outcome(id = \"$oId\", expect = $t)" + }.joinToString("\n") + } + + val jcstressDefaultOutcomeType = when (test.outcomeSpec.default) { + LitmusOutcomeType.ACCEPTED -> "ACCEPTABLE" + LitmusOutcomeType.FORBIDDEN -> "FORBIDDEN" + LitmusOutcomeType.INTERESTING -> "ACCEPTABLE_INTERESTING" } return """ @@ -107,11 +146,14 @@ import kotlin.jvm.functions.Function1; import org.openjdk.jcstress.annotations.*; import org.openjdk.jcstress.infra.results.*; +import static org.openjdk.jcstress.annotations.Expect.*; + import java.util.List; @JCStressTest @State -@Outcome(expect = Expect.ACCEPTABLE) +${jcstressOutcomeDecls()} +@Outcome(expect = $jcstressDefaultOutcomeType) public class $javaClassName { private static final LitmusTest test = (LitmusTest) ${javaTestGetter()}; From fc1f2539bcbdf93051a7fd020d1aadc5287240c0 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 6 Nov 2023 18:37:23 +0100 Subject: [PATCH 07/87] Use LitmusI..IOutcome in all tests --- .../komem/litmus/testsuite/ClassicTests.kt | 39 ++++++------------- litmus/src/jvmMain/kotlin/JvmMain.kt | 4 +- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt b/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt index 2c39419..040b4c7 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt +++ b/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt @@ -121,12 +121,11 @@ val MPVolatile: LitmusTest<*> = litmusTest({ } val MP_DRF: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var x = 0 @Volatile var y = 0 - var o = 0 } }) { thread { @@ -134,10 +133,7 @@ val MP_DRF: LitmusTest<*> = litmusTest({ y = 1 } thread { - o = if (y != 0) x else -1 - } - outcome { - o + r1 = if (y != 0) x else -1 } spec { accept(1) @@ -246,19 +242,15 @@ val IRIWVolatile: LitmusTest<*> = litmusTest({ } val UPUB: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var h: IntHolder? = null - var o = 0 } }) { thread { h = IntHolder(0) } thread { - o = h?.x ?: -1 - } - outcome { - o + r1 = h?.x ?: -1 } spec { accept(0) @@ -267,19 +259,15 @@ val UPUB: LitmusTest<*> = litmusTest({ } val UPUBCtor: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var h: IntHolderCtor? = null - var o = 0 } }) { thread { h = IntHolderCtor() } thread { - o = h?.x ?: -1 - } - outcome { - o + r1 = h?.x ?: -1 } spec { accept(1) @@ -288,23 +276,18 @@ val UPUBCtor: LitmusTest<*> = litmusTest({ } val LB_DEPS_OOTA: LitmusTest<*> = litmusTest({ - object { + object : LitmusIIOutcome() { var x = 0 var y = 0 - var a = 0 - var b = 0 } }) { thread { - a = x - y = a + r1 = x + y = r1 } thread { - b = y - x = b - } - outcome { - listOf(a, b) + r2 = y + x = r2 } spec { accept(0, 0) diff --git a/litmus/src/jvmMain/kotlin/JvmMain.kt b/litmus/src/jvmMain/kotlin/JvmMain.kt index 2c7cd31..f432dd7 100644 --- a/litmus/src/jvmMain/kotlin/JvmMain.kt +++ b/litmus/src/jvmMain/kotlin/JvmMain.kt @@ -2,6 +2,7 @@ import komem.litmus.LitmusAutoOutcome import komem.litmus.LitmusOutcomeType import komem.litmus.LitmusTest import komem.litmus.generated.LitmusTestRegistry +import komem.litmus.testsuite.UPUB import kotlin.io.path.Path import kotlin.io.path.createParentDirectories import kotlin.io.path.div @@ -14,8 +15,7 @@ import kotlin.system.exitProcess fun main() { val jcstressDirectory = Path("../jcstress") - val tests = LitmusTestRegistry.all() - val test = tests[0] + val test = UPUB val javaClassName = test.name.replace('.', '_') val targetFile = jcstressDirectory / "src/main/java/komem/litmus/$javaClassName.java" From 249ad8232f2b2ce75017c674dcf823b21f87d7d9 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Tue, 14 Nov 2023 20:59:45 +0100 Subject: [PATCH 08/87] No-op PthreadRunner --- .../kotlin/komem.litmus/CliNative.kt | 4 ++- .../kotlin/komem.litmus/PthreadRunner.kt | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/CliNative.kt b/litmus/src/nativeMain/kotlin/komem.litmus/CliNative.kt index 066c726..15181b7 100644 --- a/litmus/src/nativeMain/kotlin/komem.litmus/CliNative.kt +++ b/litmus/src/nativeMain/kotlin/komem.litmus/CliNative.kt @@ -6,7 +6,9 @@ import com.github.ajalt.clikt.parameters.types.choice import komem.litmus.barriers.CinteropSpinBarrier class CliNative : CliCommon() { - override val runner = WorkerRunner + override val runner by option("-r", "--runner") + .choice(mapOf("worker" to WorkerRunner, "pthread" to PthreadRunner)) + .default(WorkerRunner) private val affinityMapChoices = run { val schedulesMapped = mutableMapOf>("none" to listOf(null)) diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt new file mode 100644 index 0000000..c8014e6 --- /dev/null +++ b/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -0,0 +1,29 @@ +package komem.litmus + +import kotlinx.cinterop.* +import platform.posix.pthread_create + +fun sayHi() { + println("hi!") +} + +object PthreadRunner : LitmusRunner() { + @OptIn(ExperimentalForeignApi::class) + override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult { + + memScoped { + val p = alloc() // pthread_t = ULong + pthread_create( + __newthread = p.ptr, + __attr = null, + __start_routine = staticCFunction { + sayHi() + return@staticCFunction null + }, + __arg = null, + ) + } + + return emptyList() + } +} \ No newline at end of file From afe37718df91b2ec140f037df98dc98e40a18618 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Tue, 14 Nov 2023 23:09:39 +0100 Subject: [PATCH 09/87] Working PthreadRunner --- .../kotlin/komem.litmus/PthreadRunner.kt | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index c8014e6..30472eb 100644 --- a/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -1,29 +1,68 @@ package komem.litmus +import komem.litmus.barriers.Barrier import kotlinx.cinterop.* +import platform.posix.errno import platform.posix.pthread_create +import platform.posix.pthread_join +import platform.posix.strerror -fun sayHi() { - println("hi!") +class ThreadData( + val states: List, + val function: (Any?) -> Unit, + val syncPeriod: Int, + val barrier: Barrier, +) + +fun threadRoutine(data: ThreadData) = data.run { + for (i in states.indices) { + function(states[i]) + if (i % syncPeriod == 0) barrier.await() + } } +@OptIn(ExperimentalForeignApi::class) +private typealias PthreadVar = ULongVar + object PthreadRunner : LitmusRunner() { @OptIn(ExperimentalForeignApi::class) - override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult { + override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult = memScoped { + + val states = List(params.batchSize) { test.stateProducer() } + val barrier = params.barrierProducer(test.threadCount) - memScoped { - val p = alloc() // pthread_t = ULong - pthread_create( - __newthread = p.ptr, + fun startThread(index: Int): Pair> { + val function: (Any?) -> Unit = { state -> + // TODO: fix thread function signature + @Suppress("UNCHECKED_CAST") + test.threadFunctions[index].invoke(state as S) + } + val threadData = ThreadData(states, function, params.syncPeriod, barrier) + + val threadDataRef = StableRef.create(threadData) + val pthreadHandleVar = alloc() + val code = pthread_create( + __newthread = pthreadHandleVar.ptr, __attr = null, __start_routine = staticCFunction { - sayHi() + val data = it!!.asStableRef().get() + threadRoutine(data) return@staticCFunction null }, - __arg = null, + __arg = threadDataRef.asCPointer(), ) + if (code != 0) error("pthread_create failed; errno=${strerror(errno)}") + return pthreadHandleVar to threadDataRef + } + + // TODO: affinity + val (handleVars, refsToClear) = List(test.threadCount) { startThread(it) }.unzip() + for (handleVar in handleVars) { + pthread_join(handleVar.value, null) } - return emptyList() + for (ref in refsToClear) ref.dispose() + val outcomes = states.map { test.outcomeFinalizer(it) } + return@memScoped outcomes.calcStats(test.outcomeSpec) } -} \ No newline at end of file +} From 243dd50bf7ba31cc341d5c808c87efa5493e98a0 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Wed, 15 Nov 2023 00:40:38 +0100 Subject: [PATCH 10/87] Support affinity for PthreadRunner --- .../komem/litmus/AffinityBindingsImplPosix.kt | 37 ++++++++++--------- .../kotlin/komem.litmus/AffinityManager.kt | 6 ++- .../kotlin/komem.litmus/PthreadRunner.kt | 37 ++++++++++--------- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt b/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt index 1be1e48..27d989c 100644 --- a/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt +++ b/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt @@ -8,6 +8,7 @@ import platform.posix.cpu_set_t import platform.posix.errno import platform.posix.pthread_t import platform.posix.strerror +import kotlin.native.concurrent.ObsoleteWorkersApi import kotlin.native.concurrent.Worker @OptIn(ExperimentalForeignApi::class) @@ -18,23 +19,7 @@ private fun Int.callCheck() { } } -@OptIn(ExperimentalForeignApi::class) -private fun setAffinity(thread: pthread_t, cpus: Set): Unit = memScoped { - require(cpus.isNotEmpty()) - val set = alloc() - for (cpu in cpus) cpu_set(cpu, set.ptr) - set_affinity(thread, set.ptr).callCheck() -} - -@OptIn(ExperimentalForeignApi::class) -private fun getAffinity(thread: pthread_t): Set = memScoped { - val set = alloc() - get_affinity(thread, set.ptr).callCheck() - return (0..) { @@ -44,4 +29,22 @@ actual fun getAffinityManager(): AffinityManager? = object : AffinityManager { override fun getAffinity(w: Worker): Set { return getAffinity(w.platformThreadId) } + + @OptIn(ExperimentalForeignApi::class) + override fun setAffinity(thread: pthread_t, cpus: Set): Unit = memScoped { + require(cpus.isNotEmpty()) + val set = alloc() + cpu_zero(set.ptr) + for (cpu in cpus) cpu_set(cpu, set.ptr) + set_affinity(thread, set.ptr).callCheck() + } + + @OptIn(ExperimentalForeignApi::class) + override fun getAffinity(thread: pthread_t): Set = memScoped { + val set = alloc() + get_affinity(thread, set.ptr).callCheck() + return (0..) fun getAffinity(w: Worker): Set + fun setAffinity(thread: pthread_t, cpus: Set) + fun getAffinity(thread: pthread_t): Set + fun newShiftMap(shift: Int): AffinityMap = object : AffinityMap { private val cpus: List> @@ -55,4 +59,4 @@ interface AffinityManager { ) } -expect fun getAffinityManager(): AffinityManager? +expect fun getAffinityManager(): AffinityManager? \ No newline at end of file diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index 30472eb..4914b7a 100644 --- a/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/litmus/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -2,19 +2,16 @@ package komem.litmus import komem.litmus.barriers.Barrier import kotlinx.cinterop.* -import platform.posix.errno -import platform.posix.pthread_create -import platform.posix.pthread_join -import platform.posix.strerror +import platform.posix.* -class ThreadData( +private class ThreadData( val states: List, val function: (Any?) -> Unit, val syncPeriod: Int, val barrier: Barrier, ) -fun threadRoutine(data: ThreadData) = data.run { +private fun threadRoutine(data: ThreadData): Unit = with(data) { for (i in states.indices) { function(states[i]) if (i % syncPeriod == 0) barrier.await() @@ -22,27 +19,28 @@ fun threadRoutine(data: ThreadData) = data.run { } @OptIn(ExperimentalForeignApi::class) +// pthread_t = ULong private typealias PthreadVar = ULongVar object PthreadRunner : LitmusRunner() { @OptIn(ExperimentalForeignApi::class) override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult = memScoped { - + pthread_t val states = List(params.batchSize) { test.stateProducer() } val barrier = params.barrierProducer(test.threadCount) - fun startThread(index: Int): Pair> { + fun startThread(threadIndex: Int): Pair> { val function: (Any?) -> Unit = { state -> // TODO: fix thread function signature @Suppress("UNCHECKED_CAST") - test.threadFunctions[index].invoke(state as S) + test.threadFunctions[threadIndex].invoke(state as S) } val threadData = ThreadData(states, function, params.syncPeriod, barrier) val threadDataRef = StableRef.create(threadData) - val pthreadHandleVar = alloc() + val pthreadVar = alloc() val code = pthread_create( - __newthread = pthreadHandleVar.ptr, + __newthread = pthreadVar.ptr, __attr = null, __start_routine = staticCFunction { val data = it!!.asStableRef().get() @@ -51,14 +49,19 @@ object PthreadRunner : LitmusRunner() { }, __arg = threadDataRef.asCPointer(), ) - if (code != 0) error("pthread_create failed; errno=${strerror(errno)}") - return pthreadHandleVar to threadDataRef + if (code != 0) error("pthread_create failed; errno means: ${strerror(errno)?.toKString()}") + // TODO: I don't think there is a way to assign affinity before the thread starts (would be useful for MacOS) + getAffinityManager()?.let { am -> + val map = params.affinityMap?.allowedCores(threadIndex) ?: return@let + am.setAffinity(pthreadVar.value, map) + require(am.getAffinity(pthreadVar.value) == map) { "setting affinity failed" } + } + return pthreadVar to threadDataRef } - // TODO: affinity - val (handleVars, refsToClear) = List(test.threadCount) { startThread(it) }.unzip() - for (handleVar in handleVars) { - pthread_join(handleVar.value, null) + val (pthreadVars, refsToClear) = List(test.threadCount) { startThread(it) }.unzip() + for (pthreadVar in pthreadVars) { + pthread_join(pthreadVar.value, null) } for (ref in refsToClear) ref.dispose() From cb772fac4846013e8cf873e504c8332f6303ccba Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Wed, 15 Nov 2023 01:30:22 +0100 Subject: [PATCH 11/87] Split starting and joining test threads for async-style actual parallelism --- .../kotlin/komem/litmus/LitmusRunner.kt | 19 ++++++++++--- .../kotlin/komem/litmus/JvmThreadRunner.kt | 11 ++++---- .../komem/litmus/AffinityBindingsImplPosix.kt | 24 +++++----------- .../kotlin/komem.litmus/NativeUtils.kt | 12 ++++++++ .../kotlin/komem.litmus/PthreadRunner.kt | 28 +++++++++++-------- .../kotlin/komem.litmus/WorkerRunner.kt | 24 ++++++++-------- 6 files changed, 69 insertions(+), 49 deletions(-) diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/litmus/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index 8778061..9efe268 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/litmus/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -4,7 +4,18 @@ import kotlin.time.Duration import kotlin.time.TimeSource abstract class LitmusRunner { - abstract fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult + /** + * Starts threads for the test and returns a "join handle". These handles should block + * until the threads join and then collect and return the results. This is crucial for parallelism. + */ + abstract fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult + + /** + * Runs the test. Blocks the current thread until the test finishes. + */ + fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult { + return startTest(params, test)() + } // be extremely careful due to LTOutcome = Any? protected fun List.calcStats(outcomeSpec: LitmusOutcomeSpec): LitmusResult = this @@ -38,16 +49,16 @@ fun LitmusRunner.runTestParallel( params: LitmusRunParams, test: LitmusTest, ): LitmusResult { - val allOutcomes = List(instances) { instanceIndex -> + val allJoinHandles = List(instances) { instanceIndex -> val newAffinityMap = params.affinityMap?.let { oldMap -> AffinityMap { threadIndex -> oldMap.allowedCores(instanceIndex * test.threadCount + threadIndex) } } val newParams = params.copy(affinityMap = newAffinityMap) - runTest(newParams, test) + startTest(newParams, test) } - return allOutcomes.mergeResults() + return allJoinHandles.map { it() }.mergeResults() } fun LitmusRunner.runTestParallel( diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/litmus/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index 3188f4b..d6ea40b 100644 --- a/litmus/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/litmus/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -3,7 +3,7 @@ package komem.litmus // does not support affinity object JvmThreadRunner : LitmusRunner() { - override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult { + override fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { val states = List(params.batchSize) { test.stateProducer() } val barrier = params.barrierProducer(test.threadCount) @@ -20,10 +20,11 @@ object JvmThreadRunner : LitmusRunner() { } } threads.forEach { it.start() } - threads.forEach { it.join() } // await all threads - val outcomes = states.map { it.outcomeFinalizer() } - assert(outcomes.size == params.batchSize) - return outcomes.calcStats(test.outcomeSpec) + return { + threads.forEach { it.join() } + val outcomes = states.map { it.outcomeFinalizer() } + outcomes.calcStats(test.outcomeSpec) + } } } diff --git a/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt b/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt index 27d989c..c1698c5 100644 --- a/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt +++ b/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt @@ -1,26 +1,16 @@ -@file:OptIn(kotlin.native.concurrent.ObsoleteWorkersApi::class) - package komem.litmus import kaffinity.* -import kotlinx.cinterop.* +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.alloc +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.ptr import platform.posix.cpu_set_t -import platform.posix.errno import platform.posix.pthread_t -import platform.posix.strerror import kotlin.native.concurrent.ObsoleteWorkersApi import kotlin.native.concurrent.Worker -@OptIn(ExperimentalForeignApi::class) -private fun Int.callCheck() { - if (this != 0) { - val err = strerror(errno)!!.toKString() - throw IllegalStateException("C call error: $err") - } -} - -@ObsoleteWorkersApi -@OptIn(ExperimentalStdlibApi::class) +@OptIn(ExperimentalStdlibApi::class, ObsoleteWorkersApi::class) actual fun getAffinityManager(): AffinityManager? = object : AffinityManager { override fun setAffinity(w: Worker, cpus: Set) { setAffinity(w.platformThreadId, cpus) @@ -36,13 +26,13 @@ actual fun getAffinityManager(): AffinityManager? = object : AffinityManager { val set = alloc() cpu_zero(set.ptr) for (cpu in cpus) cpu_set(cpu, set.ptr) - set_affinity(thread, set.ptr).callCheck() + set_affinity(thread, set.ptr).syscallCheck() } @OptIn(ExperimentalForeignApi::class) override fun getAffinity(thread: pthread_t): Set = memScoped { val set = alloc() - get_affinity(thread, set.ptr).callCheck() + get_affinity(thread, set.ptr).syscallCheck() return (0.., @@ -24,8 +27,7 @@ private typealias PthreadVar = ULongVar object PthreadRunner : LitmusRunner() { @OptIn(ExperimentalForeignApi::class) - override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult = memScoped { - pthread_t + override fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { val states = List(params.batchSize) { test.stateProducer() } val barrier = params.barrierProducer(test.threadCount) @@ -38,7 +40,7 @@ object PthreadRunner : LitmusRunner() { val threadData = ThreadData(states, function, params.syncPeriod, barrier) val threadDataRef = StableRef.create(threadData) - val pthreadVar = alloc() + val pthreadVar = nativeHeap.alloc() val code = pthread_create( __newthread = pthreadVar.ptr, __attr = null, @@ -59,13 +61,17 @@ object PthreadRunner : LitmusRunner() { return pthreadVar to threadDataRef } - val (pthreadVars, refsToClear) = List(test.threadCount) { startThread(it) }.unzip() - for (pthreadVar in pthreadVars) { - pthread_join(pthreadVar.value, null) - } + val threads = List(test.threadCount) { startThread(it) } + + return { + for ((pthreadVar, threadDataRef) in threads) { + pthread_join(pthreadVar.value, null).syscallCheck() - for (ref in refsToClear) ref.dispose() - val outcomes = states.map { test.outcomeFinalizer(it) } - return@memScoped outcomes.calcStats(test.outcomeSpec) + nativeHeap.free(pthreadVar) + threadDataRef.dispose() + } + val outcomes = states.map { test.outcomeFinalizer(it) } + outcomes.calcStats(test.outcomeSpec) + } } } diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/litmus/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index b0373ed..091002c 100644 --- a/litmus/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/litmus/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -1,18 +1,17 @@ package komem.litmus import komem.litmus.barriers.Barrier -import kotlin.experimental.ExperimentalNativeApi import kotlin.native.concurrent.ObsoleteWorkersApi import kotlin.native.concurrent.TransferMode import kotlin.native.concurrent.Worker object WorkerRunner : LitmusRunner() { - @OptIn(ObsoleteWorkersApi::class, ExperimentalNativeApi::class) - override fun runTest( + @OptIn(ObsoleteWorkersApi::class) + override fun startTest( params: LitmusRunParams, test: LitmusTest, - ): LitmusResult { + ): () -> LitmusResult { data class WorkerContext( val states: List, @@ -26,7 +25,7 @@ object WorkerRunner : LitmusRunner() { val outcomeFinalizer = test.outcomeFinalizer val workers = List(test.threadCount) { Worker.start() } - workers.mapIndexed { threadIndex, worker -> + val futures = workers.mapIndexed { threadIndex, worker -> params.affinityMap?.let { affinityMap -> getAffinityManager()?.run { val cpuSet = affinityMap.allowedCores(threadIndex) @@ -49,12 +48,13 @@ object WorkerRunner : LitmusRunner() { states[i].threadFunction() } } - worker.requestTermination() - }.forEach { it.result } // await all workers - - val outcomes = states.map { it.outcomeFinalizer() } - assert(outcomes.size == params.batchSize) - - return outcomes.calcStats(test.outcomeSpec) + } + + return { + futures.forEach { it.result } // await all results + workers.forEach { it.requestTermination().result } // waits for all workers to stop + val outcomes = states.map { it.outcomeFinalizer() } + outcomes.calcStats(test.outcomeSpec) + } } } From 9661840300cf1ddc647c3e9438b9643e36073d4f Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Tue, 21 Nov 2023 16:22:48 +0100 Subject: [PATCH 12/87] Refactor into a common JVM CLI, currently broken due to a cyclic dependency --- .../kotlin/komem/litmus/CliCommon.kt | 32 ++-- litmus/src/jvmMain/kotlin/JvmMain.kt | 172 +----------------- .../src/jvmMain/kotlin/komem/litmus/CliJvm.kt | 53 +++++- .../kotlin/komem/litmus/jcstress/Codegen.kt | 140 ++++++++++++++ .../komem/litmus/jcstress/JCStressRunner.kt | 53 ++++++ 5 files changed, 267 insertions(+), 183 deletions(-) create mode 100644 litmus/src/jvmMain/kotlin/komem/litmus/jcstress/Codegen.kt create mode 100644 litmus/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/CliCommon.kt b/litmus/src/commonMain/kotlin/komem/litmus/CliCommon.kt index d49a8b7..7a37b98 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/CliCommon.kt +++ b/litmus/src/commonMain/kotlin/komem/litmus/CliCommon.kt @@ -1,7 +1,10 @@ package komem.litmus import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.arguments.* +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.arguments.check +import com.github.ajalt.clikt.parameters.arguments.multiple +import com.github.ajalt.clikt.parameters.arguments.transformAll import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.int import komem.litmus.barriers.BarrierProducer @@ -11,13 +14,18 @@ import kotlin.time.Duration abstract class CliCommon : CliktCommand( name = "litmuskt" ) { - private val batchSizeSchedule by option("-b", "--batchSize") - .int().varargValues().default(listOf(1_000_000)) + companion object { + const val DEFAULT_BATCH_SIZE = 1_000_000 + const val DEFAULT_SYNC_EVERY = 100 + } + + protected open val batchSizeSchedule by option("-b", "--batchSize") + .int().varargValues().default(listOf(DEFAULT_BATCH_SIZE)) - private val syncEverySchedule by option("-s", "--syncEvery") - .int().varargValues().default(listOf(100)) + protected open val syncEverySchedule by option("-s", "--syncEvery") + .int().varargValues().default(listOf(DEFAULT_SYNC_EVERY)) - private val tests by argument("tests") + protected open val tests by argument("tests") .multiple(required = true) .transformAll { args -> val regexes = args.map { @@ -31,15 +39,15 @@ abstract class CliCommon : CliktCommand( } .check("no tests were selected") { it.isNotEmpty() } - private val PARALLELISM_DISABLED = Int.MAX_VALUE - 1 - private val PARALLELISM_AUTO = Int.MAX_VALUE - 2 - private val parallelism by option("-p", "--parallelism") + protected val PARALLELISM_DISABLED = Int.MAX_VALUE - 1 + protected val PARALLELISM_AUTO = Int.MAX_VALUE - 2 + protected open val parallelism by option("-p", "--parallelism") .int().optionalValue(PARALLELISM_AUTO).default(PARALLELISM_DISABLED) - .check("value must be in range 2..100") { - it in 2..100 || it == PARALLELISM_DISABLED || it == PARALLELISM_AUTO + .check("value must be in range 2..1000") { + it in 2..1000 || it == PARALLELISM_DISABLED || it == PARALLELISM_AUTO } - private val duration by option("-d", "--duration") + protected open val duration by option("-d", "--duration") .convert { Duration.parse(it) } .check("value must be positive") { it.isPositive() } diff --git a/litmus/src/jvmMain/kotlin/JvmMain.kt b/litmus/src/jvmMain/kotlin/JvmMain.kt index f432dd7..8397aa7 100644 --- a/litmus/src/jvmMain/kotlin/JvmMain.kt +++ b/litmus/src/jvmMain/kotlin/JvmMain.kt @@ -1,171 +1,3 @@ -import komem.litmus.LitmusAutoOutcome -import komem.litmus.LitmusOutcomeType -import komem.litmus.LitmusTest -import komem.litmus.generated.LitmusTestRegistry -import komem.litmus.testsuite.UPUB -import kotlin.io.path.Path -import kotlin.io.path.createParentDirectories -import kotlin.io.path.div -import kotlin.io.path.writeText -import kotlin.reflect.full.allSuperclasses -import kotlin.reflect.full.isSubclassOf -import kotlin.reflect.full.superclasses -import kotlin.system.exitProcess +import komem.litmus.CliJvm -fun main() { - val jcstressDirectory = Path("../jcstress") - - val test = UPUB - - val javaClassName = test.name.replace('.', '_') - val targetFile = jcstressDirectory / "src/main/java/komem/litmus/$javaClassName.java" - targetFile.createParentDirectories() - val targetCode = generateWrapperCode(test, javaClassName) - - targetFile.writeText(targetCode) - val mvn = ProcessBuilder("mvn", "verify") - .directory(jcstressDirectory.toFile()) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .redirectError(ProcessBuilder.Redirect.INHERIT) - .start() - mvn.waitFor() - - if (mvn.exitValue() != 0) { - exitProcess(mvn.exitValue()) - } - - val jcs = ProcessBuilder( - "java", - "-jar", - "target/jcstress.jar", - "-t", - javaClassName, - "-m", - "quick" - ) - .directory(jcstressDirectory.toFile()) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .redirectError(ProcessBuilder.Redirect.INHERIT) - .start() - jcs.waitFor() -} - -val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) - -private fun generateWrapperCode(test: LitmusTest<*>, javaClassName: String): String { - val stateClass = test.stateProducer()!!::class - require(stateClass.allSuperclasses.contains(LitmusAutoOutcome::class)) { - "to use JCStress, test state must extend some LitmusAutoOutcome (e.g. LitmusIIOutcome)" - } - - val autoOutcomeClassList = stateClass.superclasses - .filter { it.isSubclassOf(LitmusAutoOutcome::class) } - require(autoOutcomeClassList.size == 1) { "test state should extend exactly one LitmusAutoOutcome" } - val outcomeTypeName = autoOutcomeClassList.first().simpleName!! - .removePrefix("Litmus") - .removeSuffix("Outcome") - val (outcomeVarType, outcomeVarCount) = when (outcomeTypeName) { - "I" -> "Integer" to 1 - "II" -> "Integer" to 2 - "III" -> "Integer" to 3 - "IIII" -> "Integer" to 4 - else -> error("unknown AutoOutcome type $outcomeTypeName") - } - - fun javaTestGetter(): String { - val parts = test.name.split(".") - val getter = "get" + parts.last() + "()" - val className = parts.dropLast(1).last() + "Kt" - val packages = parts.dropLast(2) - val packagesLine = if (packages.isEmpty()) "" else packages.joinToString(".", postfix = ".") - return "$packagesLine$className.$getter" - } - - fun javaThreadFunctionDecl(index: Int) = - "\tprivate static final Function1 fT$index = test.getThreadFunctions().get($index);" - - fun javaActorDecl(index: Int) = """ - @Actor - public void t$index() { - fT$index.invoke(state); - } - - """.trimEnd() - - fun javaArbiterDecl(): String { - - val jcstressResultClassName = outcomeTypeName + "_Result" - - return if (outcomeVarCount > 1) { - """ - @Arbiter - public void a($jcstressResultClassName r) { - List<$outcomeVarType> result = (List<$outcomeVarType>) fA.invoke(state); -${List(outcomeVarCount) { " r.r${it + 1} = result.get($it);" }.joinToString("\n")} - } - - """.trimEnd() - } else { - // single values are handled differently - """ - @Arbiter - public void a($jcstressResultClassName r) { - r.r1 = ($outcomeVarType) fA.invoke(state); - } - - """.trimEnd() - } - } - - fun jcstressOutcomeDecls(): String { - val outcomes = test.outcomeSpec.accepted.associateWith { "ACCEPTABLE" } + - test.outcomeSpec.interesting.associateWith { "ACCEPTABLE_INTERESTING" } + - test.outcomeSpec.forbidden.associateWith { "FORBIDDEN" } - - // since only AutoOutcome is allowed, each outcome is a list (unless it's a single value) - return outcomes.map { (o, t) -> - val oId = if (outcomeVarCount > 1) (o as List<*>).joinToString(", ") else o.toString() - "@Outcome(id = \"$oId\", expect = $t)" - }.joinToString("\n") - } - - val jcstressDefaultOutcomeType = when (test.outcomeSpec.default) { - LitmusOutcomeType.ACCEPTED -> "ACCEPTABLE" - LitmusOutcomeType.FORBIDDEN -> "FORBIDDEN" - LitmusOutcomeType.INTERESTING -> "ACCEPTABLE_INTERESTING" - } - - return """ -package komem.litmus; - -import komem.litmus.testsuite.*; - -import kotlin.Unit; -import kotlin.jvm.functions.Function1; - -import org.openjdk.jcstress.annotations.*; -import org.openjdk.jcstress.infra.results.*; - -import static org.openjdk.jcstress.annotations.Expect.*; - -import java.util.List; - -@JCStressTest -@State -${jcstressOutcomeDecls()} -@Outcome(expect = $jcstressDefaultOutcomeType) -public class $javaClassName { - - private static final LitmusTest test = (LitmusTest) ${javaTestGetter()}; -${List(test.threadCount) { javaThreadFunctionDecl(it) }.joinToString("\n")} - private static final Function1 fA = test.getOutcomeFinalizer(); - - public $javaClassName() { - } - - public Object state = test.getStateProducer().invoke(); -${List(test.threadCount) { javaActorDecl(it) }.joinToString("\n")} -${javaArbiterDecl()} -} - """.trimIndent() -} +fun main(args: Array) = CliJvm().main(args) diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/CliJvm.kt b/litmus/src/jvmMain/kotlin/komem/litmus/CliJvm.kt index 056c2e7..961535c 100644 --- a/litmus/src/jvmMain/kotlin/komem/litmus/CliJvm.kt +++ b/litmus/src/jvmMain/kotlin/komem/litmus/CliJvm.kt @@ -1,9 +1,60 @@ package komem.litmus +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required +import com.github.ajalt.clikt.parameters.types.choice +import com.github.ajalt.clikt.parameters.types.path import komem.litmus.barriers.JvmSpinBarrier +import komem.litmus.jcstress.JCStressRunner class CliJvm : CliCommon() { - override val runner = JvmThreadRunner + override val runner by option("-r", "--runner") + .choice(mapOf("thread" to JvmThreadRunner, "jcstress" to JCStressRunner)) + .required() override val barrierProducer = ::JvmSpinBarrier override val affinityMapSchedule = listOf(null) + + private val jcstressDirectory by option("-j", "--jcsdir") + .path(canBeFile = false) + private val allowJCStressReruns by option("--allow-jcs-reruns") + .flag() + // TODO: freeJCStressArgs + + override fun run() = if (runner == JCStressRunner) jcstressRun() else super.run() + + private fun jcstressRun() { + val paramsList = variateRunParams( + batchSizeSchedule = batchSizeSchedule, + affinityMapSchedule = affinityMapSchedule, + syncPeriodSchedule = syncEverySchedule, + barrierSchedule = listOf(barrierProducer), + ).toList() + when (paramsList.size) { + 0 -> { + echo("parameters list is empty; ensure no empty lists are used", err = true) + return + } + + 1 -> {} // ok + else -> { + if (!allowJCStressReruns) { + echo( + "you likely don't want to run JCStress multiple times;" + + " if you're sure, enable --allow-jcs-reruns", + err = true + ) + return + } + } + } + + for (params in paramsList) { + val nonDefaultParams = if ( + params.batchSize == DEFAULT_BATCH_SIZE && + params.syncPeriod == DEFAULT_SYNC_EVERY + ) null else params // jcstress defaults are different + JCStressRunner.runJCStress(nonDefaultParams, tests, jcstressDirectory) + } + } } diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/Codegen.kt b/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/Codegen.kt new file mode 100644 index 0000000..aafd533 --- /dev/null +++ b/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/Codegen.kt @@ -0,0 +1,140 @@ +package komem.litmus.jcstress + +import komem.litmus.LitmusAutoOutcome +import komem.litmus.LitmusOutcomeType +import komem.litmus.LitmusTest +import komem.litmus.generated.LitmusTestRegistry +import java.nio.file.Path +import kotlin.io.path.createParentDirectories +import kotlin.io.path.div +import kotlin.io.path.writeText +import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.full.superclasses + +val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) +val LitmusTest<*>.javaClassName get() = name.replace('.', '_') + +fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path) { + val targetFile = jcstressDirectory / "src/main/java/komem/litmus/${test.javaClassName}.java" + targetFile.createParentDirectories() + val targetCode = generateWrapperCode(test) + targetFile.writeText(targetCode) +} + +private fun generateWrapperCode(test: LitmusTest<*>): String { + val stateClass = test.stateProducer()!!::class + require(stateClass.allSuperclasses.contains(LitmusAutoOutcome::class)) { + "to use JCStress, test state must extend some LitmusAutoOutcome (e.g. LitmusIIOutcome)" + } + + val autoOutcomeClassList = stateClass.superclasses.filter { it.isSubclassOf(LitmusAutoOutcome::class) } + require(autoOutcomeClassList.size == 1) { "test state should extend exactly one LitmusAutoOutcome" } + val outcomeTypeName = autoOutcomeClassList.first().simpleName!! + .removePrefix("Litmus") + .removeSuffix("Outcome") + val (outcomeVarType, outcomeVarCount) = when (outcomeTypeName) { + "I" -> "Integer" to 1 + "II" -> "Integer" to 2 + "III" -> "Integer" to 3 + "IIII" -> "Integer" to 4 + else -> error("unknown AutoOutcome type $outcomeTypeName") + } + + fun javaTestGetter(): String { + val parts = test.name.split(".") + val getter = "get" + parts.last() + "()" + val className = parts.dropLast(1).last() + "Kt" + val packages = parts.dropLast(2) + val packagesLine = if (packages.isEmpty()) "" else packages.joinToString(".", postfix = ".") + return "$packagesLine$className.$getter" + } + + fun javaThreadFunctionDecl(index: Int) = + "private static final Function1 fT$index = test.getThreadFunctions().get($index);" + + fun javaActorDecl(index: Int) = """ + @Actor + public void t$index() { + fT$index.invoke(state); + } + """.trimIndent() + + fun javaArbiterDecl(): String { + + val jcstressResultClassName = outcomeTypeName + "_Result" + + return if (outcomeVarCount > 1) { + """ + @Arbiter + public void a($jcstressResultClassName r) { + List<$outcomeVarType> result = (List<$outcomeVarType>) fA.invoke(state); + ${List(outcomeVarCount) { "r.r${it + 1} = result.get($it);" }.joinToString("\n ")} + } + """.trimIndent() + } else { + // single values are handled differently + """ + @Arbiter + public void a($jcstressResultClassName r) { + r.r1 = ($outcomeVarType) fA.invoke(state); + } + """.trimIndent() + } + } + + fun jcstressOutcomeDecls(): String { + val outcomes = test.outcomeSpec.accepted.associateWith { "ACCEPTABLE" } + + test.outcomeSpec.interesting.associateWith { "ACCEPTABLE_INTERESTING" } + + test.outcomeSpec.forbidden.associateWith { "FORBIDDEN" } + + // since only AutoOutcome is allowed, each outcome is a list (unless it's a single value) + return outcomes.map { (o, t) -> + val oId = if (outcomeVarCount > 1) (o as List<*>).joinToString(", ") else o.toString() + "@Outcome(id = \"$oId\", expect = $t)" + }.joinToString("\n") + } + + val jcstressDefaultOutcomeType = when (test.outcomeSpec.default) { + LitmusOutcomeType.ACCEPTED -> "ACCEPTABLE" + LitmusOutcomeType.FORBIDDEN -> "FORBIDDEN" + LitmusOutcomeType.INTERESTING -> "ACCEPTABLE_INTERESTING" + } + + return """ +package komem.litmus; + +import komem.litmus.testsuite.*; + +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +import org.openjdk.jcstress.annotations.*; +import org.openjdk.jcstress.infra.results.*; + +import static org.openjdk.jcstress.annotations.Expect.*; + +import java.util.List; + +@JCStressTest +@State +${jcstressOutcomeDecls()} +@Outcome(expect = $jcstressDefaultOutcomeType) +public class ${test.javaClassName} { + + private static final LitmusTest test = (LitmusTest) ${javaTestGetter()}; + ${List(test.threadCount) { javaThreadFunctionDecl(it) }.joinToString("\n ")} + private static final Function1 fA = test.getOutcomeFinalizer(); + + public ${test.javaClassName}() {} + + public Object state = test.getStateProducer().invoke(); + + ${List(test.threadCount) { javaActorDecl(it).padded(4) }.joinToString("\n\n ")} + + ${javaArbiterDecl().padded(4)} +} + """.trimIndent() +} + +private fun String.padded(padding: Int) = replace("\n", "\n" + " ".repeat(padding)) diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt b/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt new file mode 100644 index 0000000..ba28f8c --- /dev/null +++ b/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt @@ -0,0 +1,53 @@ +package komem.litmus.jcstress + +import komem.litmus.LitmusResult +import komem.litmus.LitmusRunParams +import komem.litmus.LitmusRunner +import komem.litmus.LitmusTest +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.system.exitProcess + +/** + * Note that this 'runner' is severely different from all others. + */ +object JCStressRunner : LitmusRunner() { + override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult = + throw NotImplementedError("jcstress runner is not supposed to be used like this") + + fun runJCStress( + params: LitmusRunParams?, + tests: Collection>, + jcsDirectory: Path? = null + ) { + val jcstressDirectory = jcsDirectory ?: Path("../jcstress") + for (test in tests) generateWrapperFile(test, jcstressDirectory) + + val mvn = ProcessBuilder("mvn", "verify") + .directory(jcstressDirectory.toFile()) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start() + mvn.waitFor() + if (mvn.exitValue() != 0) { + exitProcess(mvn.exitValue()) + } + + val jcsParams = params?.run { + arrayOf("strideSize", "$syncPeriod", "strideCount", "${batchSize / syncPeriod}") + } ?: emptyArray() + val jcs = ProcessBuilder( + "java", + "-jar", + "target/jcstress.jar", + *jcsParams, + "-t", + tests.joinToString("|") { it.javaClassName }, + ) + .directory(jcstressDirectory.toFile()) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start() + jcs.waitFor() + } +} From e2d1bdef8e851ee1404bc76d1b1f694e6e07bf8c Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Thu, 23 Nov 2023 13:55:44 +0100 Subject: [PATCH 13/87] temp --- litmus-jcstress/build.gradle.kts | 7 +++++++ .../src/main/kotlin/komem/litmus/CodegenMain.kt | 5 +++++ settings.gradle.kts | 1 + 3 files changed, 13 insertions(+) create mode 100644 litmus-jcstress/build.gradle.kts create mode 100644 litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt diff --git a/litmus-jcstress/build.gradle.kts b/litmus-jcstress/build.gradle.kts new file mode 100644 index 0000000..02d3099 --- /dev/null +++ b/litmus-jcstress/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + kotlin("jvm") +} + +dependencies { + implementation(project(":litmus")) +} diff --git a/litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt b/litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt new file mode 100644 index 0000000..0cef778 --- /dev/null +++ b/litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt @@ -0,0 +1,5 @@ +package komem.litmus + +fun main() { + val t = litmusTest +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 81a8a73..df4c34b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,3 +2,4 @@ rootProject.name = "litmuskt" include(":litmus") include(":codegen") +include(":litmus-jcstress") From 586f2371b19bc7c6d05c6118026d22ccaa7c529f Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Wed, 13 Dec 2023 03:28:12 +0100 Subject: [PATCH 14/87] Improve affinity cinterop with compilerOpts --- litmus/.gitignore | 2 -- litmus/build.gradle.kts | 32 +++++++++---------- .../{kaffinity_gnu.c => kaffinity.def} | 6 ++-- litmus/src/nativeInterop/kaffinity.h | 9 ------ litmus/src/nativeInterop/setup.sh | 10 ------ 5 files changed, 19 insertions(+), 40 deletions(-) delete mode 100644 litmus/.gitignore rename litmus/src/nativeInterop/{kaffinity_gnu.c => kaffinity.def} (86%) delete mode 100644 litmus/src/nativeInterop/kaffinity.h delete mode 100755 litmus/src/nativeInterop/setup.sh diff --git a/litmus/.gitignore b/litmus/.gitignore deleted file mode 100644 index 04f9a31..0000000 --- a/litmus/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -src/nativeInterop/kaffinity.def -src/nativeInterop/kaffinity_gnu.o diff --git a/litmus/build.gradle.kts b/litmus/build.gradle.kts index 90c4904..486246b 100644 --- a/litmus/build.gradle.kts +++ b/litmus/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { if (affinitySupported) { val affinity by creating { defFile(project.file("src/nativeInterop/kaffinity.def")) - headers(project.file("src/nativeInterop/kaffinity.h")) + compilerOpts.add("-D_GNU_SOURCE") } } } @@ -82,21 +82,21 @@ kotlin { } } -val setupCinterop by tasks.register("setupCinterop") { - group = "interop" - doFirst { - val interopFolder = project.projectDir.resolve("src/nativeInterop") - if (!interopFolder.resolve("kaffinity.def").exists()) { - exec { - executable = interopFolder.resolve("setup.sh").absolutePath - args = listOf(interopFolder.absolutePath) - } - } - } -} - -tasks.matching { it.name.contains("cinterop") && it.name.contains("Linux") } - .forEach { it.dependsOn(setupCinterop) } +//val setupCinterop by tasks.register("setupCinterop") { +// group = "interop" +// doFirst { +// val interopFolder = project.projectDir.resolve("src/nativeInterop") +// if (!interopFolder.resolve("kaffinity.def").exists()) { +// exec { +// executable = interopFolder.resolve("setup.sh").absolutePath +// args = listOf(interopFolder.absolutePath) +// } +// } +// } +//} +// +//tasks.matching { it.name.contains("cinterop") && it.name.contains("Linux") } +// .forEach { it.dependsOn(setupCinterop) } val bitcodeInternal by tasks.register("bitcodeInternal") { val tempDir = projectDir.resolve("temp/bitcode") diff --git a/litmus/src/nativeInterop/kaffinity_gnu.c b/litmus/src/nativeInterop/kaffinity.def similarity index 86% rename from litmus/src/nativeInterop/kaffinity_gnu.c rename to litmus/src/nativeInterop/kaffinity.def index 3fd1f16..fea18f8 100644 --- a/litmus/src/nativeInterop/kaffinity_gnu.c +++ b/litmus/src/nativeInterop/kaffinity.def @@ -1,6 +1,6 @@ -#define _GNU_SOURCE +--- -#include "kaffinity.h" +#include int set_affinity(pthread_t thread, cpu_set_t* set) { return pthread_setaffinity_np(thread, sizeof(*set), set); @@ -19,5 +19,5 @@ int cpu_isset(int cpu, cpu_set_t* set) { CPU_ISSET(cpu, set); } int cpu_setsize() { - return CPU_SETSIZE; + return CPU_SETSIZE; } diff --git a/litmus/src/nativeInterop/kaffinity.h b/litmus/src/nativeInterop/kaffinity.h deleted file mode 100644 index 5d31bda..0000000 --- a/litmus/src/nativeInterop/kaffinity.h +++ /dev/null @@ -1,9 +0,0 @@ -#include - -int set_affinity(pthread_t thread, cpu_set_t* set); -int get_affinity(pthread_t thread, cpu_set_t* set); - -void cpu_zero(cpu_set_t* set); -void cpu_set(int cpu, cpu_set_t* set); -int cpu_isset(int cpu, cpu_set_t* set); -int cpu_setsize(); diff --git a/litmus/src/nativeInterop/setup.sh b/litmus/src/nativeInterop/setup.sh deleted file mode 100755 index 2a0c1b7..0000000 --- a/litmus/src/nativeInterop/setup.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -INTEROP_FOLDER=$1 - -BINARY_PATH="$INTEROP_FOLDER/kaffinity_gnu.o" -DEF_FILE_PATH="$INTEROP_FOLDER/kaffinity.def" -SOURCE_PATH="$INTEROP_FOLDER/kaffinity_gnu.c" - -gcc "$SOURCE_PATH" -c -o "$BINARY_PATH" -echo "linkerOpts.linux = $BINARY_PATH" > "$DEF_FILE_PATH" From 9798a48b6e573ef66b19c79bd76f6888ce7d1333 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 22 Jan 2024 02:12:09 +0100 Subject: [PATCH 15/87] Huge refactor to resolve cyclic dependency + get jcstress to work --- .gitignore | 1 + cli/build.gradle.kts | 51 +++++++++++ .../kotlin/komem/litmus/CliCommon.kt | 5 +- {litmus => cli}/src/jvmMain/kotlin/JvmMain.kt | 0 .../src/jvmMain/kotlin/komem/litmus/CliJvm.kt | 21 +++-- .../src/nativeMain/kotlin/NativeMain.kt | 0 .../kotlin/komem/litmus}/CliNative.kt | 0 {litmus => core}/build.gradle.kts | 26 ++---- .../kotlin/komem/litmus/AffinityMap.kt | 0 .../kotlin/komem/litmus/LitmusAutoOutcome.kt | 0 .../kotlin/komem/litmus/LitmusOutcomeStats.kt | 0 .../kotlin/komem/litmus/LitmusRunParams.kt | 0 .../kotlin/komem/litmus/LitmusRunner.kt | 0 .../kotlin/komem/litmus/LitmusTest.kt | 5 + .../commonMain/kotlin/komem/litmus/Utils.kt | 0 .../kotlin/komem/litmus/barriers/Barrier.kt | 0 .../komem/litmus/testsuite/ClassicTests.kt | 0 .../komem/litmus/testsuite/CustomTests.kt | 0 .../komem/litmus/testsuite/UPUBExtraTests.kt | 0 .../kotlin/komem.litmus/LitmusOutcomeTest.kt | 0 .../kotlin/komem.litmus/infra/TestDefaults.kt | 0 .../komem.litmus/testsuite/ClassicTests.kt | 0 .../komem.litmus/testsuite/CustomTests.kt | 0 .../komem.litmus/testsuite/UPUBExtraTests.kt | 0 .../kotlin/komem/litmus/JvmThreadRunner.kt | 0 .../jvmMain/kotlin/komem/litmus/JvmUtils.kt | 0 .../komem/litmus/barriers/JvmCyclicBarrier.kt | 0 .../komem/litmus/barriers/JvmSpinBarrier.kt | 0 .../komem/litmus/jcstress/JCStressRunner.kt | 15 +-- .../komem/litmus/infra/TestDefaults.jvm.kt | 0 .../komem/litmus/AffinityBindingsImplPosix.kt | 0 .../komem/litmus/AffinityBindingsImplNoop.kt | 0 .../src/nativeInterop/barrier.def | 0 {litmus => core}/src/nativeInterop/barrier.h | 0 .../src/nativeInterop/kaffinity.def | 0 .../kotlin/komem.litmus/AffinityManager.kt | 0 .../kotlin/komem.litmus/NativeUtils.kt | 0 .../kotlin/komem.litmus/WorkerRunner.kt | 0 .../barriers/CinteropSpinBarrier.kt | 0 .../barriers/KNativeSpinBarrier.kt | 0 .../kotlin/komem/litmus/NativeTest.kt | 0 .../komem/litmus/infra/TestDefaults.native.kt | 0 jcstress-wrapper/build.gradle.kts | 13 +++ jcstress-wrapper/src/main/kotlin/Main.kt | 18 ++++ .../src/main/kotlin/komem/litmus}/Codegen.kt | 21 +++-- jcstress/pom.xml | 91 +++++++++++++------ litmus-jcstress/build.gradle.kts | 7 -- .../main/kotlin/komem/litmus/CodegenMain.kt | 5 - settings.gradle.kts | 5 +- 49 files changed, 193 insertions(+), 91 deletions(-) create mode 100644 cli/build.gradle.kts rename {litmus => cli}/src/commonMain/kotlin/komem/litmus/CliCommon.kt (97%) rename {litmus => cli}/src/jvmMain/kotlin/JvmMain.kt (100%) rename {litmus => cli}/src/jvmMain/kotlin/komem/litmus/CliJvm.kt (80%) rename {litmus => cli}/src/nativeMain/kotlin/NativeMain.kt (100%) rename {litmus/src/nativeMain/kotlin/komem.litmus => cli/src/nativeMain/kotlin/komem/litmus}/CliNative.kt (100%) rename {litmus => core}/build.gradle.kts (81%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/AffinityMap.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/LitmusTest.kt (90%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/Utils.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt (100%) rename {litmus => core}/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt (100%) rename {litmus => core}/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt (100%) rename {litmus => core}/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt (100%) rename {litmus => core}/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt (100%) rename {litmus => core}/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt (100%) rename {litmus => core}/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt (100%) rename {litmus => core}/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt (100%) rename {litmus => core}/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt (100%) rename {litmus => core}/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt (100%) rename {litmus => core}/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt (100%) rename {litmus => core}/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt (76%) rename {litmus => core}/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt (100%) rename {litmus => core}/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt (100%) rename {litmus => core}/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt (100%) rename {litmus => core}/src/nativeInterop/barrier.def (100%) rename {litmus => core}/src/nativeInterop/barrier.h (100%) rename {litmus => core}/src/nativeInterop/kaffinity.def (100%) rename {litmus => core}/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt (100%) rename {litmus => core}/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt (100%) rename {litmus => core}/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt (100%) rename {litmus => core}/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt (100%) rename {litmus => core}/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt (100%) rename {litmus => core}/src/nativeTest/kotlin/komem/litmus/NativeTest.kt (100%) rename {litmus => core}/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt (100%) create mode 100644 jcstress-wrapper/build.gradle.kts create mode 100644 jcstress-wrapper/src/main/kotlin/Main.kt rename {litmus/src/jvmMain/kotlin/komem/litmus/jcstress => jcstress-wrapper/src/main/kotlin/komem/litmus}/Codegen.kt (92%) delete mode 100644 litmus-jcstress/build.gradle.kts delete mode 100644 litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt diff --git a/.gitignore b/.gitignore index c3483a0..5eaa214 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build/ temp/ *.hprof gitignored/ +local.properties diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts new file mode 100644 index 0000000..5cef217 --- /dev/null +++ b/cli/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + val nativeTargets = listOf( + linuxX64(), +// linuxArm64(), // 1) no machine currently available 2) CLI library does not support + macosX64(), + macosArm64(), + ) + nativeTargets.forEach { target -> + target.binaries { + executable { + entryPoint = "main" + } + } + } + jvm { + withJava() + } + + sourceSets { + commonMain { + dependencies { + implementation(project(":core")) + implementation("com.github.ajalt.clikt:clikt:4.2.1") + } + } + jvmMain { + dependencies { + implementation(project(":jcstress-wrapper")) + } + } + } +} + +val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) + +//tasks.named("jvmRun") { +// dependsOn(":core:copyLibToJCStress") +// dependsOn(":jcstress-wrapper:copyLibToJCStress") +//} + +// code above does not sync +tasks.whenTaskAdded { + if (name == "jvmRun") { + dependsOn(":core:copyLibToJCStress") + dependsOn(":jcstress-wrapper:run") + } +} diff --git a/litmus/src/commonMain/kotlin/komem/litmus/CliCommon.kt b/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt similarity index 97% rename from litmus/src/commonMain/kotlin/komem/litmus/CliCommon.kt rename to cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt index 7a37b98..0ec8b15 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/CliCommon.kt +++ b/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt @@ -12,7 +12,8 @@ import komem.litmus.generated.LitmusTestRegistry import kotlin.time.Duration abstract class CliCommon : CliktCommand( - name = "litmuskt" + name = "litmuskt", + printHelpOnEmptyArgs = true, ) { companion object { const val DEFAULT_BATCH_SIZE = 1_000_000 @@ -56,6 +57,8 @@ abstract class CliCommon : CliktCommand( protected abstract val barrierProducer: BarrierProducer // TODO: we don't talk about memshuffler for now + // TODO: dry run = simply list tests + override fun run() { echo("selected tests: \n" + tests.joinToString("\n") { " - " + LitmusTestRegistry.resolveName(it) }) echo("in total: ${tests.size} tests") diff --git a/litmus/src/jvmMain/kotlin/JvmMain.kt b/cli/src/jvmMain/kotlin/JvmMain.kt similarity index 100% rename from litmus/src/jvmMain/kotlin/JvmMain.kt rename to cli/src/jvmMain/kotlin/JvmMain.kt diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/CliJvm.kt b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt similarity index 80% rename from litmus/src/jvmMain/kotlin/komem/litmus/CliJvm.kt rename to cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt index 961535c..68a6346 100644 --- a/litmus/src/jvmMain/kotlin/komem/litmus/CliJvm.kt +++ b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt @@ -1,10 +1,8 @@ package komem.litmus -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.options.required +import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.choice -import com.github.ajalt.clikt.parameters.types.path +import jcstressDirectory import komem.litmus.barriers.JvmSpinBarrier import komem.litmus.jcstress.JCStressRunner @@ -15,15 +13,17 @@ class CliJvm : CliCommon() { override val barrierProducer = ::JvmSpinBarrier override val affinityMapSchedule = listOf(null) - private val jcstressDirectory by option("-j", "--jcsdir") - .path(canBeFile = false) private val allowJCStressReruns by option("--allow-jcs-reruns") .flag() - // TODO: freeJCStressArgs + private val jcstressFreeArgs by option("-j", "--jcsargs") + .convert { it.split(" ") } + .default(emptyList()) override fun run() = if (runner == JCStressRunner) jcstressRun() else super.run() private fun jcstressRun() { + println("haha: $jcstressFreeArgs") + val paramsList = variateRunParams( batchSizeSchedule = batchSizeSchedule, affinityMapSchedule = affinityMapSchedule, @@ -54,7 +54,12 @@ class CliJvm : CliCommon() { params.batchSize == DEFAULT_BATCH_SIZE && params.syncPeriod == DEFAULT_SYNC_EVERY ) null else params // jcstress defaults are different - JCStressRunner.runJCStress(nonDefaultParams, tests, jcstressDirectory) + JCStressRunner.runJCStress( + nonDefaultParams, + tests, + jcstressDirectory, + jcstressFreeArgs, + ) } } } diff --git a/litmus/src/nativeMain/kotlin/NativeMain.kt b/cli/src/nativeMain/kotlin/NativeMain.kt similarity index 100% rename from litmus/src/nativeMain/kotlin/NativeMain.kt rename to cli/src/nativeMain/kotlin/NativeMain.kt diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/CliNative.kt b/cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt similarity index 100% rename from litmus/src/nativeMain/kotlin/komem.litmus/CliNative.kt rename to cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt diff --git a/litmus/build.gradle.kts b/core/build.gradle.kts similarity index 81% rename from litmus/build.gradle.kts rename to core/build.gradle.kts index 486246b..5d9f432 100644 --- a/litmus/build.gradle.kts +++ b/core/build.gradle.kts @@ -18,7 +18,7 @@ kotlin { ) jvm { - withSourcesJar() + withJava() jvmToolchain(8) } @@ -56,7 +56,6 @@ kotlin { commonMain { dependencies { implementation("org.jetbrains.kotlinx:atomicfu:0.20.2") - implementation("com.github.ajalt.clikt:clikt:4.2.1") } kotlin.srcDir(layout.buildDirectory.dir("generated/ksp/metadata/commonMain/kotlin/")) // ksp } @@ -82,22 +81,6 @@ kotlin { } } -//val setupCinterop by tasks.register("setupCinterop") { -// group = "interop" -// doFirst { -// val interopFolder = project.projectDir.resolve("src/nativeInterop") -// if (!interopFolder.resolve("kaffinity.def").exists()) { -// exec { -// executable = interopFolder.resolve("setup.sh").absolutePath -// args = listOf(interopFolder.absolutePath) -// } -// } -// } -//} -// -//tasks.matching { it.name.contains("cinterop") && it.name.contains("Linux") } -// .forEach { it.dependsOn(setupCinterop) } - val bitcodeInternal by tasks.register("bitcodeInternal") { val tempDir = projectDir.resolve("temp/bitcode") doLast { @@ -135,8 +118,11 @@ tasks.whenTaskAdded { } } +val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) + tasks.register("copyLibToJCStress") { dependsOn("jvmJar") - from(layout.buildDirectory.file("libs/litmus-jvm-$version.jar")) - into(projectDir.resolve("../jcstress/libs/")) + from(layout.buildDirectory.file("libs/core-jvm-$version.jar")) + rename { "litmusktJvm-1.0.jar" } + into(jcsDir.resolve("libs/komem/litmus/litmusktJvm/1.0/")) } diff --git a/litmus/src/commonMain/kotlin/komem/litmus/AffinityMap.kt b/core/src/commonMain/kotlin/komem/litmus/AffinityMap.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/AffinityMap.kt rename to core/src/commonMain/kotlin/komem/litmus/AffinityMap.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt rename to core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt rename to core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt rename to core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt rename to core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/LitmusTest.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt similarity index 90% rename from litmus/src/commonMain/kotlin/komem/litmus/LitmusTest.kt rename to core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt index 7f8bad3..866160e 100644 --- a/litmus/src/commonMain/kotlin/komem/litmus/LitmusTest.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt @@ -1,5 +1,7 @@ package komem.litmus +import komem.litmus.generated.LitmusTestRegistry + data class LitmusTest( val stateProducer: () -> S, val threadFunctions: List Unit>, @@ -47,3 +49,6 @@ class LitmusTestScope( fun litmusTest(stateProducer: () -> S, setup: LitmusTestScope.() -> Unit) = LitmusTestScope(stateProducer).apply(setup).build() + +val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) +val LitmusTest<*>.javaClassName get() = name.replace('.', '_') diff --git a/litmus/src/commonMain/kotlin/komem/litmus/Utils.kt b/core/src/commonMain/kotlin/komem/litmus/Utils.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/Utils.kt rename to core/src/commonMain/kotlin/komem/litmus/Utils.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt b/core/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt rename to core/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt b/core/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt rename to core/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt b/core/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt rename to core/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt diff --git a/litmus/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt b/core/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt similarity index 100% rename from litmus/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt rename to core/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt diff --git a/litmus/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt b/core/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt similarity index 100% rename from litmus/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt rename to core/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt diff --git a/litmus/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt b/core/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt similarity index 100% rename from litmus/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt rename to core/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt diff --git a/litmus/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt b/core/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt similarity index 100% rename from litmus/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt rename to core/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt diff --git a/litmus/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt b/core/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt similarity index 100% rename from litmus/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt rename to core/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt diff --git a/litmus/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt b/core/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt similarity index 100% rename from litmus/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt rename to core/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt similarity index 100% rename from litmus/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt rename to core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt similarity index 100% rename from litmus/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt rename to core/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt b/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt similarity index 100% rename from litmus/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt rename to core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt b/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt similarity index 100% rename from litmus/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt rename to core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt similarity index 76% rename from litmus/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt rename to core/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt index ba28f8c..50e329f 100644 --- a/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt @@ -1,11 +1,7 @@ package komem.litmus.jcstress -import komem.litmus.LitmusResult -import komem.litmus.LitmusRunParams -import komem.litmus.LitmusRunner -import komem.litmus.LitmusTest +import komem.litmus.* import java.nio.file.Path -import kotlin.io.path.Path import kotlin.system.exitProcess /** @@ -18,12 +14,11 @@ object JCStressRunner : LitmusRunner() { fun runJCStress( params: LitmusRunParams?, tests: Collection>, - jcsDirectory: Path? = null + jcstressDirectory: Path, + jcstressFreeArgs: List, ) { - val jcstressDirectory = jcsDirectory ?: Path("../jcstress") - for (test in tests) generateWrapperFile(test, jcstressDirectory) - val mvn = ProcessBuilder("mvn", "verify") + val mvn = ProcessBuilder("mvn", "install", "verify") .directory(jcstressDirectory.toFile()) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .redirectError(ProcessBuilder.Redirect.INHERIT) @@ -40,7 +35,7 @@ object JCStressRunner : LitmusRunner() { "java", "-jar", "target/jcstress.jar", - *jcsParams, + *(jcsParams + jcstressFreeArgs), "-t", tests.joinToString("|") { it.javaClassName }, ) diff --git a/litmus/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt b/core/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt similarity index 100% rename from litmus/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt rename to core/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt diff --git a/litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt b/core/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt similarity index 100% rename from litmus/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt rename to core/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt diff --git a/litmus/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt b/core/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt similarity index 100% rename from litmus/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt rename to core/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt diff --git a/litmus/src/nativeInterop/barrier.def b/core/src/nativeInterop/barrier.def similarity index 100% rename from litmus/src/nativeInterop/barrier.def rename to core/src/nativeInterop/barrier.def diff --git a/litmus/src/nativeInterop/barrier.h b/core/src/nativeInterop/barrier.h similarity index 100% rename from litmus/src/nativeInterop/barrier.h rename to core/src/nativeInterop/barrier.h diff --git a/litmus/src/nativeInterop/kaffinity.def b/core/src/nativeInterop/kaffinity.def similarity index 100% rename from litmus/src/nativeInterop/kaffinity.def rename to core/src/nativeInterop/kaffinity.def diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt b/core/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt similarity index 100% rename from litmus/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt rename to core/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt b/core/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt similarity index 100% rename from litmus/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt rename to core/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt similarity index 100% rename from litmus/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt rename to core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt b/core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt similarity index 100% rename from litmus/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt rename to core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt diff --git a/litmus/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt b/core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt similarity index 100% rename from litmus/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt rename to core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt diff --git a/litmus/src/nativeTest/kotlin/komem/litmus/NativeTest.kt b/core/src/nativeTest/kotlin/komem/litmus/NativeTest.kt similarity index 100% rename from litmus/src/nativeTest/kotlin/komem/litmus/NativeTest.kt rename to core/src/nativeTest/kotlin/komem/litmus/NativeTest.kt diff --git a/litmus/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt b/core/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt similarity index 100% rename from litmus/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt rename to core/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt diff --git a/jcstress-wrapper/build.gradle.kts b/jcstress-wrapper/build.gradle.kts new file mode 100644 index 0000000..c4a2b00 --- /dev/null +++ b/jcstress-wrapper/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + kotlin("jvm") + application +} + +application { + mainClass = "MainKt" +} + +dependencies { + implementation(project(":core")) + implementation(kotlin("reflect")) +} diff --git a/jcstress-wrapper/src/main/kotlin/Main.kt b/jcstress-wrapper/src/main/kotlin/Main.kt new file mode 100644 index 0000000..b85dc63 --- /dev/null +++ b/jcstress-wrapper/src/main/kotlin/Main.kt @@ -0,0 +1,18 @@ +import komem.litmus.generateWrapperFile +import komem.litmus.generated.LitmusTestRegistry +import kotlin.io.path.Path + +fun main() { + var successCnt = 0 + val allTests = LitmusTestRegistry.all() + for (test in allTests) { + val success = generateWrapperFile(test, jcstressDirectory) + if (success) successCnt++ + } + if (successCnt != allTests.size) { + System.err.println("WARNING: generated wrappers for $successCnt out of ${allTests.size} known tests") + } +} + +val jcstressDirectory + get() = Path(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) diff --git a/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt similarity index 92% rename from litmus/src/jvmMain/kotlin/komem/litmus/jcstress/Codegen.kt rename to jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index aafd533..4746833 100644 --- a/litmus/src/jvmMain/kotlin/komem/litmus/jcstress/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -1,9 +1,5 @@ -package komem.litmus.jcstress +package komem.litmus -import komem.litmus.LitmusAutoOutcome -import komem.litmus.LitmusOutcomeType -import komem.litmus.LitmusTest -import komem.litmus.generated.LitmusTestRegistry import java.nio.file.Path import kotlin.io.path.createParentDirectories import kotlin.io.path.div @@ -12,14 +8,17 @@ import kotlin.reflect.full.allSuperclasses import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.superclasses -val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) -val LitmusTest<*>.javaClassName get() = name.replace('.', '_') - -fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path) { +fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path): Boolean { val targetFile = jcstressDirectory / "src/main/java/komem/litmus/${test.javaClassName}.java" targetFile.createParentDirectories() - val targetCode = generateWrapperCode(test) + val targetCode = try { + generateWrapperCode(test) + } catch (e: Throwable) { + System.err.println("WARNING: could not generate wrapper for ${test.name} because: ${e.message}") + return false + } targetFile.writeText(targetCode) + return true } private fun generateWrapperCode(test: LitmusTest<*>): String { @@ -137,4 +136,6 @@ public class ${test.javaClassName} { """.trimIndent() } +// TODO: arbiter padding is broken + private fun String.padded(padding: Int) = replace("\n", "\n" + " ".repeat(padding)) diff --git a/jcstress/pom.xml b/jcstress/pom.xml index db85c2b..adbc5c0 100644 --- a/jcstress/pom.xml +++ b/jcstress/pom.xml @@ -49,21 +49,36 @@ THE POSSIBILITY OF SUCH DAMAGE. 3.2 + + + libs + libs + + true + ignore + + + false + + file://${basedir}/libs + + + org.openjdk.jcstress jcstress-core ${jcstress.version} - - komem.litmus - litmuskt - 1.0 - org.jetbrains.kotlin kotlin-stdlib - 1.9.10 + 1.9.20 + + + komem.litmus + litmusktJvm + 1.0 @@ -122,33 +137,53 @@ THE POSSIBILITY OF SUCH DAMAGE. META-INF/TestList + + + * + + - - org.apache.maven.plugins - maven-install-plugin - 3.1.1 - - komem.litmus - litmuskt - 1.0 - jar - ${basedir}/libs/litmus-jvm-1.0-SNAPSHOT.jar - true - - - - install-jar-lib - - install-file - - validate - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/litmus-jcstress/build.gradle.kts b/litmus-jcstress/build.gradle.kts deleted file mode 100644 index 02d3099..0000000 --- a/litmus-jcstress/build.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - kotlin("jvm") -} - -dependencies { - implementation(project(":litmus")) -} diff --git a/litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt b/litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt deleted file mode 100644 index 0cef778..0000000 --- a/litmus-jcstress/src/main/kotlin/komem/litmus/CodegenMain.kt +++ /dev/null @@ -1,5 +0,0 @@ -package komem.litmus - -fun main() { - val t = litmusTest -} diff --git a/settings.gradle.kts b/settings.gradle.kts index df4c34b..d724a9f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,6 @@ rootProject.name = "litmuskt" -include(":litmus") +include(":core") include(":codegen") -include(":litmus-jcstress") +include(":jcstress-wrapper") +include(":cli") From a97540dd5d5ffdcbee01268f8922a5da30f7c042 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 22 Jan 2024 03:25:46 +0100 Subject: [PATCH 16/87] Refactor tests into separate subproject --- cli/build.gradle.kts | 2 + .../kotlin/komem/litmus/CliCommon.kt | 16 ++++-- cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt | 1 - .../komem/litmus/LitmusTestProcessor.kt | 5 +- core/build.gradle.kts | 20 ------- .../komem/litmus/{barriers => }/Barrier.kt | 2 +- .../kotlin/komem/litmus/LitmusRunParams.kt | 2 - .../kotlin/komem/litmus/LitmusTest.kt | 5 -- .../kotlin/komem.litmus/infra/TestDefaults.kt | 21 -------- .../komem.litmus/testsuite/ClassicTests.kt | 52 ------------------- .../komem.litmus/testsuite/CustomTests.kt | 9 ---- .../komem.litmus/testsuite/UPUBExtraTests.kt | 18 ------- .../komem/litmus/barriers/JvmCyclicBarrier.kt | 1 + .../komem/litmus/barriers/JvmSpinBarrier.kt | 1 + .../komem/litmus/infra/TestDefaults.jvm.kt | 14 ----- .../kotlin/komem.litmus/WorkerRunner.kt | 1 - .../barriers/CinteropSpinBarrier.kt | 1 + .../barriers/KNativeSpinBarrier.kt | 1 + .../kotlin/komem/litmus/NativeTest.kt | 15 ------ .../komem/litmus/infra/TestDefaults.native.kt | 14 ----- jcstress-wrapper/build.gradle.kts | 5 ++ .../kotlin/komem/litmus}/JCStressRunner.kt | 3 +- settings.gradle.kts | 1 + testsuite/build.gradle.kts | 39 ++++++++++++++ .../komem/litmus/LitmusTestExtensions.kt | 6 +++ .../komem/litmus/tests}/ClassicTests.kt | 2 +- .../kotlin/komem/litmus/tests}/CustomTests.kt | 2 +- .../komem/litmus/tests}/UPUBExtraTests.kt | 2 +- 28 files changed, 78 insertions(+), 183 deletions(-) rename core/src/commonMain/kotlin/komem/litmus/{barriers => }/Barrier.kt (73%) delete mode 100644 core/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt delete mode 100644 core/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt delete mode 100644 core/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt delete mode 100644 core/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt delete mode 100644 core/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt delete mode 100644 core/src/nativeTest/kotlin/komem/litmus/NativeTest.kt delete mode 100644 core/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt rename {core/src/jvmMain/kotlin/komem/litmus/jcstress => jcstress-wrapper/src/main/kotlin/komem/litmus}/JCStressRunner.kt (96%) create mode 100644 testsuite/build.gradle.kts create mode 100644 testsuite/src/commonMain/kotlin/komem/litmus/LitmusTestExtensions.kt rename {core/src/commonMain/kotlin/komem/litmus/testsuite => testsuite/src/commonMain/kotlin/komem/litmus/tests}/ClassicTests.kt (99%) rename {core/src/commonMain/kotlin/komem/litmus/testsuite => testsuite/src/commonMain/kotlin/komem/litmus/tests}/CustomTests.kt (92%) rename {core/src/commonMain/kotlin/komem/litmus/testsuite => testsuite/src/commonMain/kotlin/komem/litmus/tests}/UPUBExtraTests.kt (98%) diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index 5cef217..35b60d6 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -18,12 +18,14 @@ kotlin { } jvm { withJava() + jvmToolchain(8) } sourceSets { commonMain { dependencies { implementation(project(":core")) + implementation(project(":testsuite")) implementation("com.github.ajalt.clikt:clikt:4.2.1") } } diff --git a/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt b/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt index 0ec8b15..218a1a3 100644 --- a/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt +++ b/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt @@ -7,7 +7,6 @@ import com.github.ajalt.clikt.parameters.arguments.multiple import com.github.ajalt.clikt.parameters.arguments.transformAll import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.int -import komem.litmus.barriers.BarrierProducer import komem.litmus.generated.LitmusTestRegistry import kotlin.time.Duration @@ -38,7 +37,7 @@ abstract class CliCommon : CliktCommand( } regexes.flatMap { LitmusTestRegistry[it] }.toSet() } - .check("no tests were selected") { it.isNotEmpty() } + .check("no tests were selected") { it.isNotEmpty() || listOnly } protected val PARALLELISM_DISABLED = Int.MAX_VALUE - 1 protected val PARALLELISM_AUTO = Int.MAX_VALUE - 2 @@ -57,10 +56,15 @@ abstract class CliCommon : CliktCommand( protected abstract val barrierProducer: BarrierProducer // TODO: we don't talk about memshuffler for now + protected val listOnly by option("-l", "--listOnly").flag() // TODO: dry run = simply list tests override fun run() { - echo("selected tests: \n" + tests.joinToString("\n") { " - " + LitmusTestRegistry.resolveName(it) }) + if (listOnly) { + runListOnly() + return + } + echo("selected tests: \n" + tests.joinToString("\n") { " - " + it.name }) echo("in total: ${tests.size} tests") echo() @@ -118,6 +122,12 @@ abstract class CliCommon : CliktCommand( } } } + + private fun runListOnly() { + echo("all known tests:\n" + LitmusTestRegistry.all().joinToString("\n") { " * " + it.name }) + echo() + echo("selected tests:\n" + tests.joinToString("\n") { " - " + it.name }) + } } fun commonMain(args: Array, cli: CliCommon) { diff --git a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt index 68a6346..973238c 100644 --- a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt +++ b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt @@ -4,7 +4,6 @@ import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.choice import jcstressDirectory import komem.litmus.barriers.JvmSpinBarrier -import komem.litmus.jcstress.JCStressRunner class CliJvm : CliCommon() { override val runner by option("-r", "--runner") diff --git a/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt b/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt index fc16a44..1df63b5 100644 --- a/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt +++ b/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt @@ -14,8 +14,9 @@ class LitmusTestProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor { override fun process(resolver: Resolver): List { val basePackage = "komem.litmus" val registryFileName = "LitmusTestRegistry" + val testsPackage = "$basePackage.tests" - val testFiles = resolver.getAllFiles().filter { it.packageName.asString() == "$basePackage.testsuite" }.toList() + val testFiles = resolver.getAllFiles().filter { it.packageName.asString() == testsPackage }.toList() val dependencies = Dependencies(true, *testFiles.toTypedArray()) val registryFile = try { @@ -26,7 +27,7 @@ class LitmusTestProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor { val decls = testFiles.flatMap { it.declarations }.filterIsInstance() val namedTestsMap = decls.associate { - val relativePackage = it.packageName.asString().removePrefix("$basePackage.testsuite") + val relativePackage = it.packageName.asString().removePrefix(testsPackage) val testAlias = (if (relativePackage.isEmpty()) "" else "$relativePackage.") + it.containingFile!!.fileName.removeSuffix(".kt") + "." + it.simpleName.getShortName() diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5d9f432..6882173 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -2,7 +2,6 @@ import org.jetbrains.kotlin.incremental.createDirectory plugins { kotlin("multiplatform") - id("com.google.devtools.ksp") version "1.9.20-1.0.13" `java-library` } @@ -45,11 +44,6 @@ kotlin { kotlinOptions.freeCompilerArgs = listOf("-Xtemporary-files-dir=${tempDir.absolutePath}") } } - binaries { - executable { - entryPoint = "main" - } - } } } sourceSets { @@ -57,7 +51,6 @@ kotlin { dependencies { implementation("org.jetbrains.kotlinx:atomicfu:0.20.2") } - kotlin.srcDir(layout.buildDirectory.dir("generated/ksp/metadata/commonMain/kotlin/")) // ksp } commonTest { dependencies { @@ -105,19 +98,6 @@ tasks.register("bitcodeRelease") { finalizedBy(bitcodeInternal) } -// ======== ksp ======== - -dependencies { - add("kspCommonMainMetadata", project(":codegen")) -} - -tasks.whenTaskAdded { - if (name == "kspCommonMainKotlinMetadata") { - val kspTask = this - tasks.matching { it.name.startsWith("compileKotlin") }.forEach { it.dependsOn(kspTask) } - } -} - val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) tasks.register("copyLibToJCStress") { diff --git a/core/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt b/core/src/commonMain/kotlin/komem/litmus/Barrier.kt similarity index 73% rename from core/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt rename to core/src/commonMain/kotlin/komem/litmus/Barrier.kt index 91385e9..ffad6e7 100644 --- a/core/src/commonMain/kotlin/komem/litmus/barriers/Barrier.kt +++ b/core/src/commonMain/kotlin/komem/litmus/Barrier.kt @@ -1,4 +1,4 @@ -package komem.litmus.barriers +package komem.litmus interface Barrier { fun await() diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt index 411c6d8..8dfbe46 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt @@ -1,7 +1,5 @@ package komem.litmus -import komem.litmus.barriers.BarrierProducer - data class LitmusRunParams( val batchSize: Int, val syncPeriod: Int, diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt index 866160e..7f8bad3 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt @@ -1,7 +1,5 @@ package komem.litmus -import komem.litmus.generated.LitmusTestRegistry - data class LitmusTest( val stateProducer: () -> S, val threadFunctions: List Unit>, @@ -49,6 +47,3 @@ class LitmusTestScope( fun litmusTest(stateProducer: () -> S, setup: LitmusTestScope.() -> Unit) = LitmusTestScope(stateProducer).apply(setup).build() - -val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) -val LitmusTest<*>.javaClassName get() = name.replace('.', '_') diff --git a/core/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt b/core/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt deleted file mode 100644 index e78d528..0000000 --- a/core/src/commonTest/kotlin/komem.litmus/infra/TestDefaults.kt +++ /dev/null @@ -1,21 +0,0 @@ -package komem.litmus.infra - -import komem.litmus.LitmusOutcomeType -import komem.litmus.LitmusRunParams -import komem.litmus.LitmusRunner -import komem.litmus.LitmusTest -import kotlin.test.assertTrue - -expect val defaultParams: LitmusRunParams -expect val defaultRunner: LitmusRunner - -fun LitmusTest<*>.run( - params: LitmusRunParams = defaultParams, - runner: LitmusRunner = defaultRunner, -) { - val results = runner.runTest(params, this) - assertTrue { results.none { it.type == LitmusOutcomeType.FORBIDDEN } } - if (results.any { it.type == LitmusOutcomeType.INTERESTING }) { - println("interesting cases detected") // TODO: provide test name (?) - } -} diff --git a/core/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt b/core/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt deleted file mode 100644 index 9ecb1d0..0000000 --- a/core/src/commonTest/kotlin/komem.litmus/testsuite/ClassicTests.kt +++ /dev/null @@ -1,52 +0,0 @@ -package komem.litmus.testsuite - -import komem.litmus.infra.run -import kotlin.test.Test - -class ClassicTests { - - @Test - fun atom() = ATOM.run() - - @Test - fun sb() = SB.run() - - @Test - fun sbVolatile() = SBVolatile.run() - - @Test - fun mp() = MP.run() - - @Test - fun mpVolatile() = MPVolatile.run() - - @Test - fun mpDrf() = MP_DRF.run() - - @Test - fun coRR() = CoRR.run() - - @Test - fun coRRCse() = CoRR_CSE.run() - - @Test - fun iriw() = IRIW.run() - - @Test - fun iriwVolatile() = IRIWVolatile.run() - - @Test - fun upub() = UPUB.run() - - @Test - fun upubCtor() = UPUBCtor.run() - - @Test - fun lbDepsOOTA() = LB_DEPS_OOTA.run() - - @Test - fun lb() = LB.run() - - @Test - fun lbVolatile() = LBVolatile.run() -} diff --git a/core/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt b/core/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt deleted file mode 100644 index 489cacb..0000000 --- a/core/src/commonTest/kotlin/komem.litmus/testsuite/CustomTests.kt +++ /dev/null @@ -1,9 +0,0 @@ -package komem.litmus.testsuite - -import komem.litmus.infra.run -import kotlin.test.Test - -class CustomTests { - @Test - fun mpNoDrf() = MPNoDRF.run() -} diff --git a/core/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt b/core/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt deleted file mode 100644 index 2a9e7a2..0000000 --- a/core/src/commonTest/kotlin/komem.litmus/testsuite/UPUBExtraTests.kt +++ /dev/null @@ -1,18 +0,0 @@ -package komem.litmus.testsuite - -import komem.litmus.infra.run -import kotlin.test.Test - -class UPUBExtraTests { - @Test - fun upubVolatile() = UPUBVolatile.run() - - @Test - fun upubArray() = UPUBArray.run() - - @Test - fun upubRef() = UPUBRef.run() - - @Test - fun upubCtorLeaking() = UBUBCtorLeaking.run() -} diff --git a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt b/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt index 50f3918..3278ec6 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt @@ -1,5 +1,6 @@ package komem.litmus.barriers +import komem.litmus.Barrier import java.util.concurrent.CyclicBarrier class JvmCyclicBarrier(threadCount: Int) : Barrier { diff --git a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt b/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt index c4c5443..f1b1520 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt @@ -1,5 +1,6 @@ package komem.litmus.barriers +import komem.litmus.Barrier import java.util.concurrent.atomic.AtomicInteger class JvmSpinBarrier(private val threadCount: Int) : Barrier { diff --git a/core/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt b/core/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt deleted file mode 100644 index 6c64a3a..0000000 --- a/core/src/jvmTest/kotlin/komem/litmus/infra/TestDefaults.jvm.kt +++ /dev/null @@ -1,14 +0,0 @@ -package komem.litmus.infra - -import komem.litmus.JvmThreadRunner -import komem.litmus.LitmusRunParams -import komem.litmus.LitmusRunner -import komem.litmus.barriers.JvmSpinBarrier - -actual val defaultRunner: LitmusRunner = JvmThreadRunner -actual val defaultParams: LitmusRunParams = LitmusRunParams( - batchSize = 1_000_000, - syncPeriod = 1000, - affinityMap = null, - barrierProducer = ::JvmSpinBarrier -) diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index b0373ed..ded90e8 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -1,6 +1,5 @@ package komem.litmus -import komem.litmus.barriers.Barrier import kotlin.experimental.ExperimentalNativeApi import kotlin.native.concurrent.ObsoleteWorkersApi import kotlin.native.concurrent.TransferMode diff --git a/core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt b/core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt index e2b1086..2f2febb 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt @@ -3,6 +3,7 @@ package komem.litmus.barriers import barrier.CSpinBarrier import barrier.barrier_wait import barrier.create_barrier +import komem.litmus.Barrier import kotlinx.cinterop.CPointer import kotlinx.cinterop.ExperimentalForeignApi diff --git a/core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt b/core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt index a5435a3..f2e6685 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt @@ -1,5 +1,6 @@ package komem.litmus.barriers +import komem.litmus.Barrier import kotlin.concurrent.AtomicInt class KNativeSpinBarrier(private val threadCount: Int) : Barrier { diff --git a/core/src/nativeTest/kotlin/komem/litmus/NativeTest.kt b/core/src/nativeTest/kotlin/komem/litmus/NativeTest.kt deleted file mode 100644 index 1c05a4c..0000000 --- a/core/src/nativeTest/kotlin/komem/litmus/NativeTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package komem.litmus - -import komem.litmus.barriers.CinteropSpinBarrier -import komem.litmus.testsuite.ATOM -import kotlin.test.Test - -class NativeTest { - @Test - fun hehe() { - val test = ATOM - val runner = WorkerRunner - val params = LitmusRunParams(1_000_000, 100, null, ::CinteropSpinBarrier) - runner.runTest(params, test) - } -} diff --git a/core/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt b/core/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt deleted file mode 100644 index 06546c4..0000000 --- a/core/src/nativeTest/kotlin/komem/litmus/infra/TestDefaults.native.kt +++ /dev/null @@ -1,14 +0,0 @@ -package komem.litmus.infra - -import komem.litmus.LitmusRunParams -import komem.litmus.LitmusRunner -import komem.litmus.WorkerRunner -import komem.litmus.barriers.CinteropSpinBarrier - -actual val defaultRunner: LitmusRunner = WorkerRunner -actual val defaultParams: LitmusRunParams = LitmusRunParams( - batchSize = 1_000_000, - syncPeriod = 10, - affinityMap = null, - barrierProducer = ::CinteropSpinBarrier -) diff --git a/jcstress-wrapper/build.gradle.kts b/jcstress-wrapper/build.gradle.kts index c4a2b00..5b6f8c1 100644 --- a/jcstress-wrapper/build.gradle.kts +++ b/jcstress-wrapper/build.gradle.kts @@ -7,7 +7,12 @@ application { mainClass = "MainKt" } +kotlin { + jvmToolchain(8) +} + dependencies { implementation(project(":core")) + implementation(project(":testsuite")) implementation(kotlin("reflect")) } diff --git a/core/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt similarity index 96% rename from core/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt rename to jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt index 50e329f..f5416f2 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/jcstress/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt @@ -1,6 +1,5 @@ -package komem.litmus.jcstress +package komem.litmus -import komem.litmus.* import java.nio.file.Path import kotlin.system.exitProcess diff --git a/settings.gradle.kts b/settings.gradle.kts index d724a9f..ef43551 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,3 +4,4 @@ include(":core") include(":codegen") include(":jcstress-wrapper") include(":cli") +include(":testsuite") diff --git a/testsuite/build.gradle.kts b/testsuite/build.gradle.kts new file mode 100644 index 0000000..1d0bd36 --- /dev/null +++ b/testsuite/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") version "1.9.20-1.0.13" +} + +kotlin { + // targets have to be the same as in :cli (because it depends on this subproject) + linuxX64() +// linuxArm64() + macosX64() + macosArm64() + jvm { + withJava() + jvmToolchain(8) + } + + sourceSets { + commonMain { + dependencies { + implementation(project(":core")) + } + // ksp + kotlin.srcDir(layout.buildDirectory.dir("generated/ksp/metadata/commonMain/kotlin/")) + } + } +} + +// ======== ksp ======== + +dependencies { + add("kspCommonMainMetadata", project(":codegen")) +} + +tasks.whenTaskAdded { + if (name == "kspCommonMainKotlinMetadata") { + val kspTask = this + tasks.matching { it.name.startsWith("compileKotlin") }.forEach { it.dependsOn(kspTask) } + } +} diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/LitmusTestExtensions.kt b/testsuite/src/commonMain/kotlin/komem/litmus/LitmusTestExtensions.kt new file mode 100644 index 0000000..8f9b68c --- /dev/null +++ b/testsuite/src/commonMain/kotlin/komem/litmus/LitmusTestExtensions.kt @@ -0,0 +1,6 @@ +package komem.litmus + +import komem.litmus.generated.LitmusTestRegistry + +val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) +val LitmusTest<*>.javaClassName get() = name.replace('.', '_') diff --git a/core/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt similarity index 99% rename from core/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt rename to testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt index 040b4c7..2089d73 100644 --- a/core/src/commonMain/kotlin/komem/litmus/testsuite/ClassicTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt @@ -1,4 +1,4 @@ -package komem.litmus.testsuite +package komem.litmus.tests import komem.litmus.* import kotlin.concurrent.Volatile diff --git a/core/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt similarity index 92% rename from core/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt rename to testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt index 9f00dfe..cd47055 100644 --- a/core/src/commonMain/kotlin/komem/litmus/testsuite/CustomTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt @@ -1,4 +1,4 @@ -package komem.litmus.testsuite +package komem.litmus.tests import komem.litmus.LitmusTest import komem.litmus.litmusTest diff --git a/core/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt similarity index 98% rename from core/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt rename to testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt index c63318b..bf32935 100644 --- a/core/src/commonMain/kotlin/komem/litmus/testsuite/UPUBExtraTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt @@ -1,4 +1,4 @@ -package komem.litmus.testsuite +package komem.litmus.tests import komem.litmus.LitmusTest import komem.litmus.litmusTest From 7ce3efc81b7fca880a384b8da133db6fea19c636 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 28 Jan 2024 14:04:30 +0100 Subject: [PATCH 17/87] Minor improvements --- cli/build.gradle.kts | 6 ------ .../kotlin/komem/litmus/LitmusTestProcessor.kt | 2 +- .../kotlin/komem/litmus/LitmusRunner.kt | 12 ++++++------ .../commonMain/kotlin/komem/litmus/LitmusTest.kt | 6 +++--- .../kotlin/komem/litmus/JvmThreadRunner.kt | 2 +- .../kotlin/komem.litmus/WorkerRunner.kt | 2 +- .../src/main/kotlin/komem/litmus/Codegen.kt | 16 +++++++--------- .../main/kotlin/komem/litmus/JCStressRunner.kt | 2 +- 8 files changed, 20 insertions(+), 28 deletions(-) diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index 35b60d6..7cb9d93 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -39,12 +39,6 @@ kotlin { val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) -//tasks.named("jvmRun") { -// dependsOn(":core:copyLibToJCStress") -// dependsOn(":jcstress-wrapper:copyLibToJCStress") -//} - -// code above does not sync tasks.whenTaskAdded { if (name == "jvmRun") { dependsOn(":core:copyLibToJCStress") diff --git a/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt b/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt index 1df63b5..8f8d792 100644 --- a/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt +++ b/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt @@ -10,7 +10,7 @@ class LitmusTestProcessorProvider : SymbolProcessorProvider { } } -class LitmusTestProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor { +class LitmusTestProcessor(private val codeGenerator: CodeGenerator) : SymbolProcessor { override fun process(resolver: Resolver): List { val basePackage = "komem.litmus" val registryFileName = "LitmusTestRegistry" diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index 8778061..a162629 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -4,7 +4,7 @@ import kotlin.time.Duration import kotlin.time.TimeSource abstract class LitmusRunner { - abstract fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult + abstract fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult // be extremely careful due to LTOutcome = Any? protected fun List.calcStats(outcomeSpec: LitmusOutcomeSpec): LitmusResult = this @@ -15,7 +15,7 @@ abstract class LitmusRunner { } } -fun LitmusRunner.runTest( +fun LitmusRunner.runTest( timeLimit: Duration, params: LitmusRunParams, test: LitmusTest, @@ -33,7 +33,7 @@ fun LitmusRunner.runTest( * Example: for a map [ [0], [1], [2], [3] ],a test with 2 threads, and 2 instances, the * first instance will have a [ [0], [1] ] map and the second one will have [ [2], [3] ]. */ -fun LitmusRunner.runTestParallel( +fun LitmusRunner.runTestParallel( instances: Int, params: LitmusRunParams, test: LitmusTest, @@ -50,7 +50,7 @@ fun LitmusRunner.runTestParallel( return allOutcomes.mergeResults() } -fun LitmusRunner.runTestParallel( +fun LitmusRunner.runTestParallel( params: LitmusRunParams, test: LitmusTest ): LitmusResult = runTestParallel( @@ -59,7 +59,7 @@ fun LitmusRunner.runTestParallel( test ) -fun LitmusRunner.runTestParallel( +fun LitmusRunner.runTestParallel( instances: Int, timeLimit: Duration, params: LitmusRunParams, @@ -73,7 +73,7 @@ fun LitmusRunner.runTestParallel( return results.mergeResults() } -fun LitmusRunner.runTestParallel( +fun LitmusRunner.runTestParallel( timeLimit: Duration, params: LitmusRunParams, test: LitmusTest, diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt index 7f8bad3..f689de9 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt @@ -1,6 +1,6 @@ package komem.litmus -data class LitmusTest( +data class LitmusTest( val stateProducer: () -> S, val threadFunctions: List Unit>, val outcomeFinalizer: (S.() -> LitmusOutcome), @@ -9,7 +9,7 @@ data class LitmusTest( val threadCount = threadFunctions.size } -class LitmusTestScope( +class LitmusTestScope( private val stateProducer: () -> S ) { private val threadFunctions = mutableListOf Unit>() @@ -45,5 +45,5 @@ class LitmusTestScope( } } -fun litmusTest(stateProducer: () -> S, setup: LitmusTestScope.() -> Unit) = +fun litmusTest(stateProducer: () -> S, setup: LitmusTestScope.() -> Unit) = LitmusTestScope(stateProducer).apply(setup).build() diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index 3188f4b..104ed6a 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -3,7 +3,7 @@ package komem.litmus // does not support affinity object JvmThreadRunner : LitmusRunner() { - override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult { + override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult { val states = List(params.batchSize) { test.stateProducer() } val barrier = params.barrierProducer(test.threadCount) diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index ded90e8..4507871 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -8,7 +8,7 @@ import kotlin.native.concurrent.Worker object WorkerRunner : LitmusRunner() { @OptIn(ObsoleteWorkersApi::class, ExperimentalNativeApi::class) - override fun runTest( + override fun runTest( params: LitmusRunParams, test: LitmusTest, ): LitmusResult { diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index 4746833..d1daee2 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -22,7 +22,7 @@ fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path): Boolean { } private fun generateWrapperCode(test: LitmusTest<*>): String { - val stateClass = test.stateProducer()!!::class + val stateClass = test.stateProducer()::class require(stateClass.allSuperclasses.contains(LitmusAutoOutcome::class)) { "to use JCStress, test state must extend some LitmusAutoOutcome (e.g. LitmusIIOutcome)" } @@ -65,12 +65,12 @@ private fun generateWrapperCode(test: LitmusTest<*>): String { return if (outcomeVarCount > 1) { """ - @Arbiter - public void a($jcstressResultClassName r) { - List<$outcomeVarType> result = (List<$outcomeVarType>) fA.invoke(state); - ${List(outcomeVarCount) { "r.r${it + 1} = result.get($it);" }.joinToString("\n ")} - } - """.trimIndent() +@Arbiter +public void a($jcstressResultClassName r) { + List<$outcomeVarType> result = (List<$outcomeVarType>) fA.invoke(state); + ${List(outcomeVarCount) { "r.r${it + 1} = result.get($it);" }.joinToString("\n ")} +} + """.trim() } else { // single values are handled differently """ @@ -136,6 +136,4 @@ public class ${test.javaClassName} { """.trimIndent() } -// TODO: arbiter padding is broken - private fun String.padded(padding: Int) = replace("\n", "\n" + " ".repeat(padding)) diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt index f5416f2..35da935 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt @@ -7,7 +7,7 @@ import kotlin.system.exitProcess * Note that this 'runner' is severely different from all others. */ object JCStressRunner : LitmusRunner() { - override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult = + override fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult = throw NotImplementedError("jcstress runner is not supposed to be used like this") fun runJCStress( From ee8ed4e05a00b7dc59ecaafcdbb68a725fc8a1a1 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 28 Jan 2024 15:13:11 +0100 Subject: [PATCH 18/87] Update README --- README.md | 105 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index d3bb0e8..98d3e93 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # LitmusKt **LitmusKt** is a litmus testing tool for Kotlin. -Litmus tests are small concurrent programs exposing various relaxed behaviors, -arising due to compiler or hardware optimizations (for example, instruction reordering). +Litmus tests are small concurrent programs exposing various relaxed behaviors, arising due to compiler or hardware +optimizations (for example, instruction reordering). This project is in an **experimental** stage of the development. The tool's API is unstable and might be a subject to a further change. @@ -11,27 +11,41 @@ The tool's API is unstable and might be a subject to a further change. Simply clone the project and run `./gradlew build`. -### Running +Note that for Kotlin/JVM this project relies on [jcstress](https://github.com/openjdk/jcstress), so you also need to +set `JCS_DIR` environment variable to `/path/to/jcstress/folder`. -All classic Gradle building tasks (like `run`, `build`, `-debug-` or `-release-`) work as expected. -The only important distinction is that on various platforms these tasks are named differently. +Tip: if you wish to run Gradle tasks from IDEA, you need to set `JCS_DIR` for it too, and a handy way to do that is to +run IDEA from bash: `idea & disown -a` (note the singular `&`). -```bash -# Linux: -./gradlew runReleaseExecutableLinuxX64 +## Running + +The entry point is the CLI tool residing in `:cli` subproject. You can use the `--help` flag to find the details about +the CLI, but most basic example requires two settings: + +1. Choose a runner with `-r` option +1. After the options are specified, choose the tests to run using regex patterns + +### Running on Native + +Create an executable and run it: -# MacOS (x86): -./gradlew runReleaseExecutableMacosX64 -# MacOS (arm): -./gradlew runReleaseExecutableMacosArm64 -Parm # don't forget the -Parm flag! +```bash +./gradlew :cli:linkReleaseExecutableLinuxX64 +./build/bin/linuxX64/releaseExecutable/cli.kexe --help ``` -Substituting `Release` with `Debug` disables compiler `-opt` flag. +Depending on what you need, you can: + +* Switch between `debug` and `release` (which, among other things, toggles the `-opt` compiler flag) +* Specify the platform (`linuxX64` / `macosX64` / `macosArm64`) + +### Running on JVM -Also, it is possible to build the project manually with Kotlin CLI compiler. -You'd have to either declare several opt-ins or edit the code to remove `expect/actual` and C interop parts. -There aren't many benefits to manual compilation, but it allows at least some way to read the program's LLVM IR bitcode -(using `-Xtemporary-files-dir` compiler flag and then converting the `.bc` file into readable text with `llvm-dis`). +The entry point is still the `:cli` subproject. Simply run the project with Gradle: + +```bash +./gradlew :cli:jvmRun --args="--help" +``` ## Overview @@ -147,38 +161,34 @@ val StoreBuffering: LitmusTest<*> = litmusTest({ ### Litmus Test Runners -The tests are run with an `LitmusRunner`. -Currently, there are two implementations. -* `WorkerRunner`: runner based on `Worker` API for Kotlin/Native; -* `JvmThreadRunner`: custom threads-based runner for Kotlin/JVM. -* A proper, JCStress based runner for Kotlin/JVM is **in development**. - -`LitmusRunner` has several running functions: +Litmus tests are run with a `LitmusRunner`. This interface has several running functions: * `runTest(params, test)` simply runs the test with the given parameters. * `runTest(duration, params, test)` repeatedly runs the test with the given parameters until the given time duration passes. -* `runTestParallel(instances, ...)` it runs several instances of the test in parallel. +* `runTestParallel(instances, ...)` runs several instances of the test in parallel. * `runTestParallel(...)` without explicit instances number will run `#{of cpu cores} / #{of threads in test}` instances. -### Entry point - -Currently, the `main()` functions are the intended way of running particular litmus tests. -A proper CLI interface is in development. +The following implementations of `LitmusRunner` are available: -There is also an option to run the tests with `@Test` annotation using the default parameters. -However, the tests are run in debug mode by the `kotlinx.test` framework. -Running litmus tests in the debug mode can affect their results, potentially hiding some relaxed behaviors. +* For native: + * `WorkerRunner`: based on K/N `Worker` API + * `PthreadRunner`: based on C interop pthread API +* For JVM: + * `JvmThreadRunner`: a simple runner based on Java threads + * `JCStressRunner`: a **special** runner that delegates to JCStress. Note that many of `LitmusRunner` parameters are + not applicable to JCStress. ### Litmus Test Parameters +There is a number of parameters that can be varied between test runs. Their influence on the results can change +drastically depending on the particular test, hardware, and so on. + * `AffinityMap`: bindings from thread to CPU cores. Obtained through `AffinityManager`, which is available from `getAffinityManager()` top-level function. - -* `syncEvery`: the number of tests between barrier synchronizations. - Practice shows that on Native the reasonable range is somewhere in the range from 10 to 100, - while on JVM it works best in the range from 1000 to 10000. - This also depends on the particular test. - +* `syncEvery`: the number of tests between barrier synchronizations. + Practice shows that on Native the reasonable range is somewhere in the range from 10 to 100, + while on JVM it works best in the range from 1000 to 10000. + This highly depends on the particular test. * `Barrier`: can be either Kotlin-implemented (`KNativeSpinBarrier`) or C-implemented (`CinteropSpinBarrier`). C-implemented might yield better results. On JVM, use `JvmSpinBarrier` in favor of `JvmCyclicBarrier`. @@ -189,11 +199,24 @@ Common practice is to iterate through different parameter bundles and aggregate * For results aggregation, use `List.mergeResults()`. * You can also use `LitmusResult.prettyPrint()` to print the results. -### Notes +### Project structure + +The project consists of several subprojects: + +* `:core` contains the core infrastructure such as `LitmusTest` and `LitmusRunner` interfaces, etc. +* `:testsuite` contains the litmus tests themselves. +* `:codegen` uses KSP to collect all tests from `:testsuite`. +* `:jcstress-wrapper` contains the code to convert `LitmusTest`-s into JCStress-compatible Java wrappers. +* `:cli` is a user-friendly entry point. + +## Notes +* If you decide to add some litmus tests, you **must** put them into `:testsuite` subproject and + into `komem.litmus.tests` package. Deeper packages are allowed. * Setting thread affinity is not supported on macOS yet. As such, `getAffinityManager()` returns `null` on macOS. -* For some reason, running a lot of different tests in one go will drastically reduce the performance and weak outcomes' frequency. - For now, please try to avoid running tests for longer than 5 minutes. +* It is possible to run the tests with `@Test` annotation. However, the tests are run in debug mode by + the `kotlinx.test` framework. Running litmus tests in the debug mode can affect their results, potentially hiding some + relaxed behaviors. * In practice, all cases of currently found relaxed behaviors can be consistently found in under a minute of running. * Avoid creating unnecessary objects inside threads, especially if they get shared. This not only significantly slows down the performance, but can also introduce unexpected relaxed behaviors. * The tool currently doesn't address the false sharing problem. The memory shuffling API is in development. From c214aff4b6be9600c469a3ddb4ca4b830dfc6747 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 28 Jan 2024 15:16:30 +0100 Subject: [PATCH 19/87] Tiny README fixes --- README.md | 55 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 98d3e93..618bb1b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# LitmusKt +# LitmusKt **LitmusKt** is a litmus testing tool for Kotlin. Litmus tests are small concurrent programs exposing various relaxed behaviors, arising due to compiler or hardware optimizations (for example, instruction reordering). -This project is in an **experimental** stage of the development. +This project is in an **experimental** stage of the development. The tool's API is unstable and might be a subject to a further change. ## Setup @@ -41,7 +41,7 @@ Depending on what you need, you can: ### Running on JVM -The entry point is still the `:cli` subproject. Simply run the project with Gradle: +Simply run the project with Gradle: ```bash ./gradlew :cli:jvmRun --args="--help" @@ -50,20 +50,21 @@ The entry point is still the `:cli` subproject. Simply run the project with Grad ## Overview A single litmus test consists of the following parts: + * a state shared between threads; * code for each thread; * an outcome — a certain value which is the result of running the test; * a specification listing accepted and forbidden outcomes -The tool runs litmus tests with various parameters, -using the standard techniques also employed by other tools, +The tool runs litmus tests with various parameters, +using the standard techniques also employed by other tools, like [herdtools/litmus7](https://github.com/herd/herdtools7) and [JCStress](https://github.com/openjdk/jcstress). The tool allocates a batch of shared state instances -and runs the threads on one state instance after another, -occasionally synchronizing threads with barriers. -After all threads finish running, states are converted into outcomes, and the same outcomes are counted. -The end result is the list of all different observed outcomes, +and runs the threads on one state instance after another, +occasionally synchronizing threads with barriers. +After all threads finish running, states are converted into outcomes, and the same outcomes are counted. +The end result is the list of all different observed outcomes, their frequencies and their types (accepted, interesting or forbidden). ### Litmus Test Syntax @@ -113,24 +114,25 @@ And here is an example of the tool's output: Let us describe the litmus test's declaration. * As a first argument `litmusTest` takes a function producing the shared state instance. -* The second argument is DSL builder lambda, setting up the litmus test. -* `thread { ... }` lambdas set up the code run in different threads of the litmus tests — - these lambdas take shared state instance as a receiver. +* The second argument is DSL builder lambda, setting up the litmus test. +* `thread { ... }` lambdas set up the code run in different threads of the litmus tests — + these lambdas take shared state instance as a receiver. * `outcome { ... }` lambda sets up the outcome of a test obtained after all threads have run — these lambdas also take shared state instance as a receiver. -* the `spec { ... }` lambda classifies the outcomes into acceptable, interesting, and forbidden categories. +* the `spec { ... }` lambda classifies the outcomes into acceptable, interesting, and forbidden categories. Here are a few additional convenient features. * Classes implementing `LitmusAutoOutcome` interface set up an outcome automatically. - There are a few predefined subclasses of this interface. - For example, the class `LitmusIIOutcome` with `II` standing for "int, int" expects two integers as an outcome. - This class have two fields `var r1: Int` and `var r2: Int`. - These fields should be set inside litmus test's threads, and then they will be automatically used to form an outcome `listOf(r1, r2)`. - -* If the outcome is a `List`, you can use a shorter syntax for declaring accepted / interesting / forbidden outcomes. + There are a few predefined subclasses of this interface. + For example, the class `LitmusIIOutcome` with `II` standing for "int, int" expects two integers as an outcome. + This class have two fields `var r1: Int` and `var r2: Int`. + These fields should be set inside litmus test's threads, and then they will be automatically used to form an + outcome `listOf(r1, r2)`. + +* If the outcome is a `List`, you can use a shorter syntax for declaring accepted / interesting / forbidden outcomes. Just use `accept(vararg outcome)` counterparts to specify expected elements. - + * Since each test usually has its own specific state, it is quite useful to use anonymous classes for them. Using these features, the test from above can be shortened as follows: @@ -164,7 +166,8 @@ val StoreBuffering: LitmusTest<*> = litmusTest({ Litmus tests are run with a `LitmusRunner`. This interface has several running functions: * `runTest(params, test)` simply runs the test with the given parameters. -* `runTest(duration, params, test)` repeatedly runs the test with the given parameters until the given time duration passes. +* `runTest(duration, params, test)` repeatedly runs the test with the given parameters until the given time duration + passes. * `runTestParallel(instances, ...)` runs several instances of the test in parallel. * `runTestParallel(...)` without explicit instances number will run `#{of cpu cores} / #{of threads in test}` instances. @@ -183,18 +186,19 @@ The following implementations of `LitmusRunner` are available: There is a number of parameters that can be varied between test runs. Their influence on the results can change drastically depending on the particular test, hardware, and so on. -* `AffinityMap`: bindings from thread to CPU cores. +* `AffinityMap`: bindings from thread to CPU cores. Obtained through `AffinityManager`, which is available from `getAffinityManager()` top-level function. * `syncEvery`: the number of tests between barrier synchronizations. Practice shows that on Native the reasonable range is somewhere in the range from 10 to 100, while on JVM it works best in the range from 1000 to 10000. This highly depends on the particular test. -* `Barrier`: can be either Kotlin-implemented (`KNativeSpinBarrier`) or C-implemented (`CinteropSpinBarrier`). +* `Barrier`: can be either Kotlin-implemented (`KNativeSpinBarrier`) or C-implemented (`CinteropSpinBarrier`). C-implemented might yield better results. On JVM, use `JvmSpinBarrier` in favor of `JvmCyclicBarrier`. Common practice is to iterate through different parameter bundles and aggregate the results across them. -* Function `variateParameters()` takes the cross-product of all passed parameters + +* Function `variateParameters()` takes the cross-product of all passed parameters (hence use `listOf(null)` instead of `emptyList()` for unused arguments). * For results aggregation, use `List.mergeResults()`. * You can also use `LitmusResult.prettyPrint()` to print the results. @@ -218,5 +222,6 @@ The project consists of several subprojects: the `kotlinx.test` framework. Running litmus tests in the debug mode can affect their results, potentially hiding some relaxed behaviors. * In practice, all cases of currently found relaxed behaviors can be consistently found in under a minute of running. -* Avoid creating unnecessary objects inside threads, especially if they get shared. This not only significantly slows down the performance, but can also introduce unexpected relaxed behaviors. +* Avoid creating unnecessary objects inside threads, especially if they get shared. This not only significantly slows + down the performance, but can also introduce unexpected relaxed behaviors. * The tool currently doesn't address the false sharing problem. The memory shuffling API is in development. From 9888c07d60df951a8662fa2e6b586758b2baff30 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 5 Feb 2024 02:27:58 +0100 Subject: [PATCH 20/87] Move state allocation out of runner --- cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt | 55 ++++++++----- .../kotlin/komem/litmus/LitmusRunner.kt | 78 +++++++++++++------ .../kotlin/komem/litmus/JvmThreadRunner.kt | 13 +++- .../kotlin/komem.litmus/PthreadRunner.kt | 16 ++-- .../kotlin/komem.litmus/WorkerRunner.kt | 12 +-- .../kotlin/komem/litmus/JCStressRunner.kt | 55 +++++++++---- 6 files changed, 159 insertions(+), 70 deletions(-) diff --git a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt index 973238c..e9ef5b9 100644 --- a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt +++ b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt @@ -1,28 +1,47 @@ package komem.litmus -import com.github.ajalt.clikt.parameters.options.* -import com.github.ajalt.clikt.parameters.types.choice +import com.github.ajalt.clikt.parameters.groups.OptionGroup +import com.github.ajalt.clikt.parameters.groups.groupChoice +import com.github.ajalt.clikt.parameters.groups.required +import com.github.ajalt.clikt.parameters.options.convert +import com.github.ajalt.clikt.parameters.options.default +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option import jcstressDirectory import komem.litmus.barriers.JvmSpinBarrier +private sealed class RunnerOptions : OptionGroup() { + abstract val runner: LitmusRunner +} + +private class JvmThreadRunnerOptions : RunnerOptions() { + override val runner = JvmThreadRunner +} + +private class JCStressRunnerOptions : RunnerOptions() { + private val jcstressFreeArgs by option("-j", "--jcsargs") + .convert { it.split(" ") } + .default(emptyList()) + + override val runner = JCStressRunner(jcstressDirectory, jcstressFreeArgs) +} + class CliJvm : CliCommon() { - override val runner by option("-r", "--runner") - .choice(mapOf("thread" to JvmThreadRunner, "jcstress" to JCStressRunner)) - .required() + private val runnerOptions by option("-r", "--runner").groupChoice( + "thread" to JvmThreadRunnerOptions(), + "jcstress" to JCStressRunnerOptions(), + ).required() + override val runner get() = runnerOptions.runner + override val barrierProducer = ::JvmSpinBarrier override val affinityMapSchedule = listOf(null) private val allowJCStressReruns by option("--allow-jcs-reruns") .flag() - private val jcstressFreeArgs by option("-j", "--jcsargs") - .convert { it.split(" ") } - .default(emptyList()) - override fun run() = if (runner == JCStressRunner) jcstressRun() else super.run() + override fun run() = if (runner is JCStressRunner) jcstressRun() else super.run() private fun jcstressRun() { - println("haha: $jcstressFreeArgs") - val paramsList = variateRunParams( batchSizeSchedule = batchSizeSchedule, affinityMapSchedule = affinityMapSchedule, @@ -49,16 +68,14 @@ class CliJvm : CliCommon() { } for (params in paramsList) { - val nonDefaultParams = if ( + val jcsParams = if ( params.batchSize == DEFAULT_BATCH_SIZE && params.syncPeriod == DEFAULT_SYNC_EVERY - ) null else params // jcstress defaults are different - JCStressRunner.runJCStress( - nonDefaultParams, - tests, - jcstressDirectory, - jcstressFreeArgs, - ) + ) JCStressRunner.DEFAULT_LITMUSKT_PARAMS else params // jcstress defaults are different + + for (test in tests) { + runner.runTest(jcsParams, test) + } } } } diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index 8566818..d729a0c 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -4,17 +4,60 @@ import kotlin.time.Duration import kotlin.time.TimeSource abstract class LitmusRunner { + /** - * Starts threads for the test and returns a "join handle". These handles should block - * until the threads join and then collect and return the results. This is crucial for parallelism. + * Starts threads for the test and returns a "join handle". This handle should block + * until the threads join and then collect and return the results. */ - abstract fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult + protected abstract fun startTest( + test: LitmusTest, + states: List, + barrierProducer: BarrierProducer, + syncPeriod: Int, + affinityMap: AffinityMap?, + ): () -> LitmusResult /** - * Runs the test. Blocks the current thread until the test finishes. + * Entry point for running tests. This method can be overridden in case that particular runner + * does not need to allocate states. */ - fun runTest(params: LitmusRunParams, test: LitmusTest): LitmusResult { - return startTest(params, test)() + open fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { + val states = List(params.batchSize) { test.stateProducer() } + return startTest(test, states, params.barrierProducer, params.syncPeriod, params.affinityMap) + } + + /** + * Entry point for running tests in parallel. Again, can be overridden in case a particular runner + * implements parallel runs in a different manner. + * + * Note: default implementation interprets AffinityMap as a sequence of smaller maps. + * Example: for a map [ [0], [1], [2], [3] ],a test with 2 threads, and 2 instances, the + * first instance will have a [ [0], [1] ] map and the second one will have [ [2], [3] ]. + */ + open fun LitmusRunner.startTestParallel( + instances: Int, + params: LitmusRunParams, + test: LitmusTest, + ): List<() -> LitmusResult> { + // separated due to allocations severely impacting threads + val allStates = List(instances) { + List(params.batchSize) { test.stateProducer() } + } + val allJoinHandles = List(instances) { instanceIndex -> + val newAffinityMap = params.affinityMap?.let { oldMap -> + AffinityMap { threadIndex -> + oldMap.allowedCores(instanceIndex * test.threadCount + threadIndex) + } + } + startTest( + test = test, + states = allStates[instanceIndex], + barrierProducer = params.barrierProducer, + syncPeriod = params.syncPeriod, + affinityMap = newAffinityMap, + ) + } + return allJoinHandles } // be extremely careful due to LitmusOutcome = Any? @@ -26,6 +69,11 @@ abstract class LitmusRunner { } } +fun LitmusRunner.runTest( + params: LitmusRunParams, + test: LitmusTest, +): LitmusResult = startTest(params, test).invoke() + fun LitmusRunner.runTest( timeLimit: Duration, params: LitmusRunParams, @@ -39,27 +87,11 @@ fun LitmusRunner.runTest( return results.mergeResults() } -/* - * Note: interprets AffinityMap as a sequence of smaller maps - * Example: for a map [ [0], [1], [2], [3] ],a test with 2 threads, and 2 instances, the - * first instance will have a [ [0], [1] ] map and the second one will have [ [2], [3] ]. - */ fun LitmusRunner.runTestParallel( instances: Int, params: LitmusRunParams, test: LitmusTest, -): LitmusResult { - val allJoinHandles = List(instances) { instanceIndex -> - val newAffinityMap = params.affinityMap?.let { oldMap -> - AffinityMap { threadIndex -> - oldMap.allowedCores(instanceIndex * test.threadCount + threadIndex) - } - } - val newParams = params.copy(affinityMap = newAffinityMap) - startTest(newParams, test) - } - return allJoinHandles.map { it() }.mergeResults() -} +): LitmusResult = startTestParallel(instances, params, test).map { it() }.mergeResults() fun LitmusRunner.runTestParallel( params: LitmusRunParams, diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index 67fefcf..83e5d30 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -3,15 +3,20 @@ package komem.litmus // does not support affinity object JvmThreadRunner : LitmusRunner() { - override fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { - val states = List(params.batchSize) { test.stateProducer() } - val barrier = params.barrierProducer(test.threadCount) + override fun startTest( + test: LitmusTest, + states: List, + barrierProducer: BarrierProducer, + syncPeriod: Int, + affinityMap: AffinityMap? + ): () -> LitmusResult { + val barrier = barrierProducer(test.threadCount) val outcomeFinalizer = test.outcomeFinalizer val threads = List(test.threadCount) { threadIndex -> Thread { val threadFunction = test.threadFunctions[threadIndex] - val syncPeriod = params.syncPeriod + val syncPeriod = syncPeriod for (i in states.indices) { if (i % syncPeriod == 0) barrier.await() states[i].threadFunction() diff --git a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index 9742780..d4c13db 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -25,10 +25,16 @@ private fun threadRoutine(data: ThreadData): Unit = with(data) { private typealias PthreadVar = ULongVar object PthreadRunner : LitmusRunner() { + @OptIn(ExperimentalForeignApi::class) - override fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { - val states = List(params.batchSize) { test.stateProducer() } - val barrier = params.barrierProducer(test.threadCount) + override fun startTest( + test: LitmusTest, + states: List, + barrierProducer: BarrierProducer, + syncPeriod: Int, + affinityMap: AffinityMap? + ): () -> LitmusResult { + val barrier = barrierProducer(test.threadCount) fun startThread(threadIndex: Int): Pair> { val function: (Any?) -> Unit = { state -> @@ -36,7 +42,7 @@ object PthreadRunner : LitmusRunner() { @Suppress("UNCHECKED_CAST") test.threadFunctions[threadIndex].invoke(state as S) } - val threadData = ThreadData(states, function, params.syncPeriod, barrier) + val threadData = ThreadData(states, function, syncPeriod, barrier) val threadDataRef = StableRef.create(threadData) val pthreadVar = nativeHeap.alloc() @@ -53,7 +59,7 @@ object PthreadRunner : LitmusRunner() { if (code != 0) error("pthread_create failed; errno means: ${strerror(errno)?.toKString()}") // TODO: I don't think there is a way to assign affinity before the thread starts (would be useful for MacOS) getAffinityManager()?.let { am -> - val map = params.affinityMap?.allowedCores(threadIndex) ?: return@let + val map = affinityMap?.allowedCores(threadIndex) ?: return@let am.setAffinity(pthreadVar.value, map) require(am.getAffinity(pthreadVar.value) == map) { "setting affinity failed" } } diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index 9bbd89d..b196a04 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -8,8 +8,11 @@ object WorkerRunner : LitmusRunner() { @OptIn(ObsoleteWorkersApi::class) override fun startTest( - params: LitmusRunParams, test: LitmusTest, + states: List, + barrierProducer: BarrierProducer, + syncPeriod: Int, + affinityMap: AffinityMap? ): () -> LitmusResult { data class WorkerContext( @@ -19,13 +22,12 @@ object WorkerRunner : LitmusRunner() { val barrier: Barrier, ) - val states = List(params.batchSize) { test.stateProducer() } - val barrier = params.barrierProducer(test.threadCount) + val barrier = barrierProducer(test.threadCount) val outcomeFinalizer = test.outcomeFinalizer val workers = List(test.threadCount) { Worker.start() } val futures = workers.mapIndexed { threadIndex, worker -> - params.affinityMap?.let { affinityMap -> + affinityMap?.let { affinityMap -> getAffinityManager()?.run { val cpuSet = affinityMap.allowedCores(threadIndex) setAffinity(worker, cpuSet) @@ -35,7 +37,7 @@ object WorkerRunner : LitmusRunner() { val workerContext = WorkerContext( states, test.threadFunctions[threadIndex], - params.syncPeriod, + syncPeriod, barrier, ) worker.execute( diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt index 24694f8..efa42cd 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt @@ -1,22 +1,43 @@ package komem.litmus +import komem.litmus.barriers.JvmCyclicBarrier import java.nio.file.Path import kotlin.system.exitProcess /** * Note that this 'runner' is severely different from all others. */ -object JCStressRunner : LitmusRunner() { - override fun startTest(params: LitmusRunParams, test: LitmusTest) = - throw NotImplementedError("jcstress runner is not supposed to be used like this") +class JCStressRunner( + private val jcstressDirectory: Path, + private val jcstressFreeArgs: List, +) : LitmusRunner() { - fun runJCStress( - params: LitmusRunParams?, - tests: Collection>, - jcstressDirectory: Path, - jcstressFreeArgs: List, - ) { + companion object { + val DEFAULT_LITMUSKT_PARAMS = LitmusRunParams(0, 0, null, ::JvmCyclicBarrier) + } + + override fun startTest( + test: LitmusTest, + states: List, + barrierProducer: BarrierProducer, + syncPeriod: Int, + affinityMap: AffinityMap? + ): () -> LitmusResult { + throw NotImplementedError("jcstress runner should not be called like this") + } + override fun LitmusRunner.startTestParallel( + instances: Int, + params: LitmusRunParams, + test: LitmusTest + ): List<() -> LitmusResult> { + throw NotImplementedError( + "jcstress runs tests in parallel by default; asking for parallelism explicitly is meaningless" + ) + } + + // TODO: optimize for many tests (do not invoke jcstress many times) + override fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { val mvn = ProcessBuilder("mvn", "install", "verify") .directory(jcstressDirectory.toFile()) .redirectOutput(ProcessBuilder.Redirect.INHERIT) @@ -27,21 +48,27 @@ object JCStressRunner : LitmusRunner() { exitProcess(mvn.exitValue()) } - val jcsParams = params?.run { - arrayOf("strideSize", "$syncPeriod", "strideCount", "${batchSize / syncPeriod}") - } ?: emptyArray() + val jcsParams = if (params != DEFAULT_LITMUSKT_PARAMS) { + arrayOf("strideSize", "${params.syncPeriod}", "strideCount", "${params.batchSize / params.syncPeriod}") + } else emptyArray() val jcs = ProcessBuilder( "java", "-jar", "target/jcstress.jar", *(jcsParams + jcstressFreeArgs), "-t", - tests.joinToString("|") { it.javaClassName }, + test.javaClassName, ) .directory(jcstressDirectory.toFile()) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .redirectError(ProcessBuilder.Redirect.INHERIT) .start() - jcs.waitFor() + + return { + jcs.waitFor() + + // TODO: collect and return results + emptyList() + } } } From 0f52f5ed4004cf29412da35d567dfd8271a06860 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 5 Feb 2024 03:44:23 +0100 Subject: [PATCH 21/87] Improve results calculation speed --- .../kotlin/komem/litmus/LitmusRunner.kt | 47 ++++++++++++++++--- .../kotlin/komem/litmus/JvmThreadRunner.kt | 2 +- .../kotlin/komem.litmus/PthreadRunner.kt | 3 +- .../kotlin/komem.litmus/WorkerRunner.kt | 2 +- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index d729a0c..c6536b5 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -60,13 +60,48 @@ abstract class LitmusRunner { return allJoinHandles } - // be extremely careful due to LitmusOutcome = Any? - protected fun List.calcStats(outcomeSpec: LitmusOutcomeSpec): LitmusResult = this - .groupingBy { it } - .eachCount() - .map { (outcome, count) -> - LitmusOutcomeStats(outcome, count.toLong(), outcomeSpec.getType(outcome)) + // 1) be extremely careful due to LitmusOutcome = Any? + // 2) Sequence forces to think twice before accidentally allocating a huge list (like `states.map{...}`) + // 3) this is an optimized version which is ~30% faster than `.groupingBy().eachCount()` on List-s (!) + protected fun Sequence.calcStats(outcomeSpec: LitmusOutcomeSpec): LitmusResult { + val bucketsMap = mutableMapOf, MutableList>>() + + fun fastEquals(o1: Any?, o2: Any?): Boolean { + if (o1 is List<*> && o2 is List<*>) { + if (o1.size != o2.size) return false + for (i in o1.indices) { + if (o1[i] != o2[i]) return false + } + return true + } else return o1 == o2 + } + + outcomes@ for (o in this) { + val bucketId = o.hashCode() + val (bucket, counts) = bucketsMap.getOrPut(bucketId) { mutableListOf() to mutableListOf() } + if (bucket.isEmpty()) { + bucket.add(o) + counts.add(1L) + continue + } + if (fastEquals(bucket[0], o)) { // "fast path" + counts[0]++ + continue + } + for (i in 1.. bucket zip counts }.flatten() + return countsMap.map { (outcome, count) -> + LitmusOutcomeStats(outcome, count, outcomeSpec.getType(outcome)) + } + } } fun LitmusRunner.runTest( diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index 83e5d30..f307ca8 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -27,7 +27,7 @@ object JvmThreadRunner : LitmusRunner() { return { threads.forEach { it.join() } - val outcomes = states.map { it.outcomeFinalizer() } + val outcomes = states.asSequence().map { it.outcomeFinalizer() } outcomes.calcStats(test.outcomeSpec) } } diff --git a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index d4c13db..ae6509c 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -75,7 +75,8 @@ object PthreadRunner : LitmusRunner() { nativeHeap.free(pthreadVar) threadDataRef.dispose() } - val outcomes = states.map { test.outcomeFinalizer(it) } + val outcomes = states.asSequence().map { test.outcomeFinalizer(it) } + println("calcing stats") outcomes.calcStats(test.outcomeSpec) } } diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index b196a04..574ab99 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -54,7 +54,7 @@ object WorkerRunner : LitmusRunner() { return { futures.forEach { it.result } // await all results workers.forEach { it.requestTermination().result } // waits for all workers to stop - val outcomes = states.map { it.outcomeFinalizer() } + val outcomes = states.asSequence().map { it.outcomeFinalizer() } outcomes.calcStats(test.outcomeSpec) } } From 21c83f510a0cdab63477dc411e7790d16763c923 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 5 Feb 2024 04:44:12 +0100 Subject: [PATCH 22/87] PR cleanup --- cli/build.gradle.kts | 8 +-- cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt | 4 +- .../kotlin/komem/litmus/CliNative.kt | 4 +- core/build.gradle.kts | 8 +-- .../kotlin/komem/litmus/JvmThreadRunner.kt | 7 +-- .../kotlin/komem.litmus/PthreadRunner.kt | 13 ++--- .../kotlin/komem.litmus/WorkerRunner.kt | 6 ++- gradle.properties | 4 +- .../src/main/kotlin/komem/litmus/Codegen.kt | 54 ++++++++++--------- .../kotlin/komem/litmus/JCStressRunner.kt | 3 +- 10 files changed, 63 insertions(+), 48 deletions(-) diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index 7cb9d93..07f2bc6 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -5,7 +5,8 @@ plugins { kotlin { val nativeTargets = listOf( linuxX64(), -// linuxArm64(), // 1) no machine currently available 2) CLI library does not support + // 1) no machine currently available 2) CLI library does not support +// linuxArm64(), macosX64(), macosArm64(), ) @@ -23,10 +24,11 @@ kotlin { sourceSets { commonMain { + val cliktVersion = project.findProperty("cliktVersion") dependencies { implementation(project(":core")) implementation(project(":testsuite")) - implementation("com.github.ajalt.clikt:clikt:4.2.1") + implementation("com.github.ajalt.clikt:clikt:$cliktVersion") } } jvmMain { @@ -37,8 +39,6 @@ kotlin { } } -val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) - tasks.whenTaskAdded { if (name == "jvmRun") { dependsOn(":core:copyLibToJCStress") diff --git a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt index e9ef5b9..eb7d2e0 100644 --- a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt +++ b/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt @@ -15,7 +15,7 @@ private sealed class RunnerOptions : OptionGroup() { } private class JvmThreadRunnerOptions : RunnerOptions() { - override val runner = JvmThreadRunner + override val runner = JvmThreadRunner() } private class JCStressRunnerOptions : RunnerOptions() { @@ -23,7 +23,7 @@ private class JCStressRunnerOptions : RunnerOptions() { .convert { it.split(" ") } .default(emptyList()) - override val runner = JCStressRunner(jcstressDirectory, jcstressFreeArgs) + override val runner get() = JCStressRunner(jcstressDirectory, jcstressFreeArgs) } class CliJvm : CliCommon() { diff --git a/cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt b/cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt index 15181b7..d52e6c1 100644 --- a/cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt +++ b/cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt @@ -7,8 +7,8 @@ import komem.litmus.barriers.CinteropSpinBarrier class CliNative : CliCommon() { override val runner by option("-r", "--runner") - .choice(mapOf("worker" to WorkerRunner, "pthread" to PthreadRunner)) - .default(WorkerRunner) + .choice(mapOf("worker" to WorkerRunner(), "pthread" to PthreadRunner())) + .default(WorkerRunner()) private val affinityMapChoices = run { val schedulesMapped = mutableMapOf>("none" to listOf(null)) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6882173..ef36b83 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -11,7 +11,8 @@ version = "1.0-SNAPSHOT" kotlin { val nativeTargets = listOf( linuxX64(), -// linuxArm64(), // 1) no machine currently available 2) CLI library does not support + // 1) no machine currently available 2) CLI library does not support +// linuxArm64(), macosX64(), macosArm64(), ) @@ -48,13 +49,14 @@ kotlin { } sourceSets { commonMain { + val atomicfuVersion = project.findProperty("atomicfuVersion") dependencies { - implementation("org.jetbrains.kotlinx:atomicfu:0.20.2") + implementation("org.jetbrains.kotlinx:atomicfu:$atomicfuVersion") } } commonTest { dependencies { - implementation("org.jetbrains.kotlin:kotlin-test:1.9.0") + implementation(kotlin("test")) } } diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index f307ca8..83a70b3 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -1,7 +1,9 @@ package komem.litmus -// does not support affinity -object JvmThreadRunner : LitmusRunner() { +/** + * A simplistic runner based on JVM threads. Does not support affinity. + */ +class JvmThreadRunner : LitmusRunner() { override fun startTest( test: LitmusTest, @@ -16,7 +18,6 @@ object JvmThreadRunner : LitmusRunner() { val threads = List(test.threadCount) { threadIndex -> Thread { val threadFunction = test.threadFunctions[threadIndex] - val syncPeriod = syncPeriod for (i in states.indices) { if (i % syncPeriod == 0) barrier.await() states[i].threadFunction() diff --git a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index ae6509c..618273d 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -7,8 +7,8 @@ import platform.posix.pthread_join import platform.posix.strerror private class ThreadData( - val states: List, - val function: (Any?) -> Unit, + val states: List, + val function: (Any) -> Unit, val syncPeriod: Int, val barrier: Barrier, ) @@ -24,7 +24,10 @@ private fun threadRoutine(data: ThreadData): Unit = with(data) { // pthread_t = ULong private typealias PthreadVar = ULongVar -object PthreadRunner : LitmusRunner() { +/** + * A runner based on pthread API provided by C interop from stdlib. + */ +class PthreadRunner : LitmusRunner() { @OptIn(ExperimentalForeignApi::class) override fun startTest( @@ -37,8 +40,7 @@ object PthreadRunner : LitmusRunner() { val barrier = barrierProducer(test.threadCount) fun startThread(threadIndex: Int): Pair> { - val function: (Any?) -> Unit = { state -> - // TODO: fix thread function signature + val function: (Any) -> Unit = { state -> @Suppress("UNCHECKED_CAST") test.threadFunctions[threadIndex].invoke(state as S) } @@ -76,7 +78,6 @@ object PthreadRunner : LitmusRunner() { threadDataRef.dispose() } val outcomes = states.asSequence().map { test.outcomeFinalizer(it) } - println("calcing stats") outcomes.calcStats(test.outcomeSpec) } } diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index 574ab99..e3b1a76 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -4,7 +4,11 @@ import kotlin.native.concurrent.ObsoleteWorkersApi import kotlin.native.concurrent.TransferMode import kotlin.native.concurrent.Worker -object WorkerRunner : LitmusRunner() { +/** + * A runner based on Kotlin/Native Workers. They are declared obsolete, + * but an alternative is yet to exist. + */ +class WorkerRunner : LitmusRunner() { @OptIn(ObsoleteWorkersApi::class) override fun startTest( diff --git a/gradle.properties b/gradle.properties index b51bcc4..16c8225 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,9 @@ kotlin.code.style=official - kotlin.native.ignoreDisabledTargets=true kotlin.mpp.enableCInteropCommonization=true +cliktVersion=4.2.2 +atomicfuVersion=0.23.2 + # this one is required for jvmRun task; can also be passed as -DmainClass=JvmMainKt mainClass=JvmMainKt # path to custom compiler dist diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index d1daee2..e736e69 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -40,30 +40,18 @@ private fun generateWrapperCode(test: LitmusTest<*>): String { else -> error("unknown AutoOutcome type $outcomeTypeName") } - fun javaTestGetter(): String { + val javaTestGetter: String = run { val parts = test.name.split(".") val getter = "get" + parts.last() + "()" val className = parts.dropLast(1).last() + "Kt" val packages = parts.dropLast(2) val packagesLine = if (packages.isEmpty()) "" else packages.joinToString(".", postfix = ".") - return "$packagesLine$className.$getter" + "$packagesLine$className.$getter" } - fun javaThreadFunctionDecl(index: Int) = - "private static final Function1 fT$index = test.getThreadFunctions().get($index);" - - fun javaActorDecl(index: Int) = """ - @Actor - public void t$index() { - fT$index.invoke(state); - } - """.trimIndent() - - fun javaArbiterDecl(): String { - + val javaArbiterDecl: String = run { val jcstressResultClassName = outcomeTypeName + "_Result" - - return if (outcomeVarCount > 1) { + if (outcomeVarCount > 1) { """ @Arbiter public void a($jcstressResultClassName r) { @@ -82,13 +70,13 @@ public void a($jcstressResultClassName r) { } } - fun jcstressOutcomeDecls(): String { + val jcstressOutcomeDecls: String = run { val outcomes = test.outcomeSpec.accepted.associateWith { "ACCEPTABLE" } + test.outcomeSpec.interesting.associateWith { "ACCEPTABLE_INTERESTING" } + test.outcomeSpec.forbidden.associateWith { "FORBIDDEN" } // since only AutoOutcome is allowed, each outcome is a list (unless it's a single value) - return outcomes.map { (o, t) -> + outcomes.map { (o, t) -> val oId = if (outcomeVarCount > 1) (o as List<*>).joinToString(", ") else o.toString() "@Outcome(id = \"$oId\", expect = $t)" }.joinToString("\n") @@ -100,7 +88,26 @@ public void a($jcstressResultClassName r) { LitmusOutcomeType.INTERESTING -> "ACCEPTABLE_INTERESTING" } - return """ + return wrapperCode(test, jcstressOutcomeDecls, jcstressDefaultOutcomeType, javaTestGetter, javaArbiterDecl) +} + +private fun javaThreadFunctionDecl(index: Int) = + "private static final Function1 fT$index = test.getThreadFunctions().get($index);" + +private fun javaActorDecl(index: Int) = """ + @Actor + public void t$index() { + fT$index.invoke(state); + } + """.trimIndent() + +fun wrapperCode( + test: LitmusTest<*>, + jcstressOutcomeDecls: String, + jcstressDefaultOutcomeType: String, + javaTestGetter: String, + javaArbiterDecl: String, +) = """ package komem.litmus; import komem.litmus.testsuite.*; @@ -117,11 +124,11 @@ import java.util.List; @JCStressTest @State -${jcstressOutcomeDecls()} +$jcstressOutcomeDecls @Outcome(expect = $jcstressDefaultOutcomeType) public class ${test.javaClassName} { - private static final LitmusTest test = (LitmusTest) ${javaTestGetter()}; + private static final LitmusTest test = (LitmusTest) $javaTestGetter; ${List(test.threadCount) { javaThreadFunctionDecl(it) }.joinToString("\n ")} private static final Function1 fA = test.getOutcomeFinalizer(); @@ -131,9 +138,8 @@ public class ${test.javaClassName} { ${List(test.threadCount) { javaActorDecl(it).padded(4) }.joinToString("\n\n ")} - ${javaArbiterDecl().padded(4)} -} - """.trimIndent() + ${javaArbiterDecl.padded(4)} } +""".trimIndent() private fun String.padded(padding: Int) = replace("\n", "\n" + " ".repeat(padding)) diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt index efa42cd..741fa66 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt @@ -2,7 +2,6 @@ package komem.litmus import komem.litmus.barriers.JvmCyclicBarrier import java.nio.file.Path -import kotlin.system.exitProcess /** * Note that this 'runner' is severely different from all others. @@ -45,7 +44,7 @@ class JCStressRunner( .start() mvn.waitFor() if (mvn.exitValue() != 0) { - exitProcess(mvn.exitValue()) + error("mvn exited with code ${mvn.exitValue()}") } val jcsParams = if (params != DEFAULT_LITMUSKT_PARAMS) { From ab899114c7215061cfe4e37c44a6add3f7d3da43 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 18 Feb 2024 17:19:54 +0100 Subject: [PATCH 23/87] PR cleanup (part 2) --- .../src/main/kotlin/komem/litmus/Codegen.kt | 2 +- jcstress/.gitignore | 2 +- jcstress/pom.xml | 56 ++++++------------- 3 files changed, 20 insertions(+), 40 deletions(-) diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index e736e69..10856ec 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -9,7 +9,7 @@ import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.superclasses fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path): Boolean { - val targetFile = jcstressDirectory / "src/main/java/komem/litmus/${test.javaClassName}.java" + val targetFile = jcstressDirectory / "generatedSrc/main/java/komem/litmus/${test.javaClassName}.java" targetFile.createParentDirectories() val targetCode = try { generateWrapperCode(test) diff --git a/jcstress/.gitignore b/jcstress/.gitignore index 3346914..1110e26 100644 --- a/jcstress/.gitignore +++ b/jcstress/.gitignore @@ -3,4 +3,4 @@ libs/ results/ *.bin.gz test.iml -src/main/java/komem/litmus/ +generatedSrc/ diff --git a/jcstress/pom.xml b/jcstress/pom.xml index adbc5c0..1a4d3f0 100644 --- a/jcstress/pom.xml +++ b/jcstress/pom.xml @@ -146,44 +146,24 @@ THE POSSIBILITY OF SUCH DAMAGE. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + generate-sources + + add-source + + + + generatedSrc/main/ + + + + + From 44bb17403eb3141e5d32939008fd38d988a2cd27 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 19 Feb 2024 00:49:04 +0100 Subject: [PATCH 24/87] Use LitmusAutoState as both state and outcome, change lists to holder classes, rewrite calcStats --- .../kotlin/komem/litmus/LitmusAutoOutcome.kt | 35 -- .../kotlin/komem/litmus/LitmusAutoState.kt | 72 +++ .../kotlin/komem/litmus/LitmusOutcomeStats.kt | 41 +- .../kotlin/komem/litmus/LitmusRunner.kt | 65 ++- .../kotlin/komem/litmus/LitmusTest.kt | 10 +- .../kotlin/komem/litmus/JvmThreadRunner.kt | 4 +- .../kotlin/komem.litmus/PthreadRunner.kt | 3 +- .../kotlin/komem.litmus/WorkerRunner.kt | 4 +- .../src/main/kotlin/komem/litmus/Codegen.kt | 4 +- .../kotlin/komem/litmus/tests/ClassicTests.kt | 410 +++++++++--------- 10 files changed, 336 insertions(+), 312 deletions(-) delete mode 100644 core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt create mode 100644 core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt deleted file mode 100644 index 6ea6d23..0000000 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt +++ /dev/null @@ -1,35 +0,0 @@ -package komem.litmus - -interface LitmusAutoOutcome { - fun getOutcome(): LitmusOutcome -} - -open class LitmusIOutcome( - var r1: Int = 0, -) : LitmusAutoOutcome { - override fun getOutcome() = r1 // single values are handled differently -} - -open class LitmusIIOutcome( - var r1: Int = 0, - var r2: Int = 0 -) : LitmusAutoOutcome { - override fun getOutcome() = listOf(r1, r2) -} - -open class LitmusIIIOutcome( - var r1: Int = 0, - var r2: Int = 0, - var r3: Int = 0, -) : LitmusAutoOutcome { - override fun getOutcome() = listOf(r1, r2, r3) -} - -open class LitmusIIIIOutcome( - var r1: Int = 0, - var r2: Int = 0, - var r3: Int = 0, - var r4: Int = 0, -) : LitmusAutoOutcome { - override fun getOutcome() = listOf(r1, r2, r3, r4) -} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt new file mode 100644 index 0000000..ff64c71 --- /dev/null +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt @@ -0,0 +1,72 @@ +package komem.litmus + +/** + * A convenience interface to simplify specifying outcomes. + * + * All classes implementing this interface provide some r1, r2, ... variables + * to write the outcome into. If a litmus test's state extends one of these classes, + * specifying `outcome { ... }` is not necessary, as it will be inferred from r1, r2, ... + * + * These classes are also used as outcomes themselves to better utilize resources. + */ +interface LitmusAutoState { + override fun toString(): String + override fun hashCode(): Int + + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") + override fun equals(o: Any?): Boolean +} + +fun LitmusAutoState(values: List): LitmusAutoState { + if (values.size == 1) { + val o1 = values[0] + if (o1 is Int) return LitmusIState(o1) + } + if (values.size == 2) { + val o1 = values[0] + val o2 = values[1] + if (o1 is Int && o2 is Int) return LitmusIIState(o1, o2) + } + error("no LitmusAutoOutcome to support values $values") +} + +open class LitmusIState( + var r1: Int = 0, +) : LitmusAutoState { + // single values are handled differently (see LitmusOutcomeSpecScope) + final override fun toString() = "($r1)" + final override fun hashCode() = r1 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIState) return false + return r1 == o.r1 + } +} + +open class LitmusIIState( + var r1: Int = 0, + var r2: Int = 0 +) : LitmusAutoState { + final override fun toString() = "($r1, $r2)" + final override fun hashCode() = r1 shl 16 + r2 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIIState) return false + return r1 == o.r1 && r2 == o.r2 + } +} + +//open class LitmusIIIState( +// var r1: Int = 0, +// var r2: Int = 0, +// var r3: Int = 0, +//) : LitmusAutoState { +// override fun getOutcome() = listOf(r1, r2, r3) +//} +// +//open class LitmusIIIIState( +// var r1: Int = 0, +// var r2: Int = 0, +// var r3: Int = 0, +// var r4: Int = 0, +//) : LitmusAutoState { +// override fun getOutcome() = listOf(r1, r2, r3, r4) +//} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt index 96e7a83..7aaa0a2 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt @@ -11,9 +11,9 @@ data class LitmusOutcomeStats( ) data class LitmusOutcomeSpec( - val accepted: Set, - val interesting: Set, - val forbidden: Set, + val accepted: List, + val interesting: List, + val forbidden: List, val default: LitmusOutcomeType, ) { fun getType(outcome: LitmusOutcome) = when (outcome) { @@ -25,43 +25,31 @@ data class LitmusOutcomeSpec( } /** - * For convenience, it is possible to use `accept(vararg values)` if test outcome is a `List`. - * This is true for [LitmusAutoOutcome]. + * For convenience, it is possible to use `accept(vararg values)` if outcome is a [LitmusAutoState]. * * Use `accept(value)` otherwise. Notice that `accept(a, b)` is NOT the same as `accept(a); accept(b)`. + * Dev note: this is the reason why 'single values are handled differently' in some other places. * * The same applies to `interesting()` and `forbid()`. */ -class LitmusOutcomeSpecScope { - private val accepted = mutableSetOf() - private val interesting = mutableSetOf() - private val forbidden = mutableSetOf() +class LitmusOutcomeSpecScope { + private val accepted = mutableListOf() + private val interesting = mutableListOf() + private val forbidden = mutableListOf() private var default: LitmusOutcomeType? = null fun accept(outcome: LitmusOutcome) { accepted.add(outcome) } - fun accept(vararg outcome: LitmusOutcome) { - accepted.add(outcome.toList()) - } - fun interesting(outcome: LitmusOutcome) { interesting.add(outcome) } - fun interesting(vararg outcome: LitmusOutcome) { - interesting.add(outcome.toList()) - } - fun forbid(outcome: LitmusOutcome) { forbidden.add(outcome) } - fun forbid(vararg outcome: LitmusOutcome) { - forbidden.add(outcome.toList()) - } - fun default(outcomeType: LitmusOutcomeType) { if (default != null) error("cannot set default outcome type more than once") @@ -71,6 +59,17 @@ class LitmusOutcomeSpecScope { fun build() = LitmusOutcomeSpec(accepted, interesting, forbidden, default ?: LitmusOutcomeType.FORBIDDEN) } +// if S is LitmusAutoState, even single values should only be interpreted as r1 + +fun LitmusOutcomeSpecScope.accept(vararg values: LitmusOutcome) = + accept(LitmusAutoState(values.toList())) + +fun LitmusOutcomeSpecScope.interesting(vararg values: LitmusOutcome) = + interesting(LitmusAutoState(values.toList())) + +fun LitmusOutcomeSpecScope.forbid(vararg values: LitmusOutcome) = + forbid(LitmusAutoState(values.toList())) + typealias LitmusResult = List fun LitmusResult.generateTable(): String { diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index c6536b5..579c245 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -60,46 +60,39 @@ abstract class LitmusRunner { return allJoinHandles } - // 1) be extremely careful due to LitmusOutcome = Any? - // 2) Sequence forces to think twice before accidentally allocating a huge list (like `states.map{...}`) - // 3) this is an optimized version which is ~30% faster than `.groupingBy().eachCount()` on List-s (!) - protected fun Sequence.calcStats(outcomeSpec: LitmusOutcomeSpec): LitmusResult { - val bucketsMap = mutableMapOf, MutableList>>() + protected fun calcStats( + states: List, + spec: LitmusOutcomeSpec, + outcomeFinalizer: (S) -> LitmusOutcome + ): LitmusResult { + // cannot do `map.getOrPut(key){0L}++` with Long-s, and by getting rid of one + // extra put(), we are also getting rid of one extra hashCode() + class LongHolder(var value: Long) - fun fastEquals(o1: Any?, o2: Any?): Boolean { - if (o1 is List<*> && o2 is List<*>) { - if (o1.size != o2.size) return false - for (i in o1.indices) { - if (o1[i] != o2[i]) return false - } - return true - } else return o1 == o2 - } + // the absolute majority of outcomes will be declared in spec + val fastPathOutcomes = ArrayList(spec.accepted + spec.interesting + spec.forbidden) + val fastPathCounts = Array(fastPathOutcomes.size) { 0L } + val totalCounts = mutableMapOf() - outcomes@ for (o in this) { - val bucketId = o.hashCode() - val (bucket, counts) = bucketsMap.getOrPut(bucketId) { mutableListOf() to mutableListOf() } - if (bucket.isEmpty()) { - bucket.add(o) - counts.add(1L) - continue - } - if (fastEquals(bucket[0], o)) { // "fast path" - counts[0]++ - continue + for (s in states) { + val outcome = outcomeFinalizer(s) + val i = fastPathOutcomes.indexOf(outcome) + if (i != -1) { + fastPathCounts[i]++ + } else { + totalCounts.getOrPut(outcome) { LongHolder(0L) }.value++ } - for (i in 1.. bucket zip counts }.flatten() - return countsMap.map { (outcome, count) -> - LitmusOutcomeStats(outcome, count, outcomeSpec.getType(outcome)) + // update totalCounts with fastPathCounts + for (i in fastPathCounts.indices) { + val count = fastPathCounts[i] + if (count > 0) totalCounts + .getOrPut(fastPathOutcomes[i]) { LongHolder(0L) } + .value = count + } + + return totalCounts.map { (outcome, count) -> + LitmusOutcomeStats(outcome, count.value, spec.getType(outcome)) } } } diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt index f689de9..48ae252 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt @@ -14,7 +14,7 @@ class LitmusTestScope( ) { private val threadFunctions = mutableListOf Unit>() private lateinit var outcomeFinalizer: S.() -> LitmusOutcome - private lateinit var outcomeSpec: LitmusOutcomeSpecScope + private lateinit var outcomeSpec: LitmusOutcomeSpecScope fun thread(function: S.() -> Unit) { threadFunctions.add(function) @@ -25,9 +25,9 @@ class LitmusTestScope( outcomeFinalizer = function } - fun spec(setup: LitmusOutcomeSpecScope.() -> Unit) { + fun spec(setup: LitmusOutcomeSpecScope.() -> Unit) { if (::outcomeSpec.isInitialized) error("cannot set spec more than once") - outcomeSpec = LitmusOutcomeSpecScope().apply(setup) + outcomeSpec = LitmusOutcomeSpecScope().apply(setup) } fun build(): LitmusTest { @@ -35,8 +35,8 @@ class LitmusTestScope( if (!::outcomeSpec.isInitialized) error("spec not specified") val outcomeFinalizer: S.() -> LitmusOutcome = when { ::outcomeFinalizer.isInitialized -> outcomeFinalizer - stateProducer() is LitmusAutoOutcome -> { - { (this as LitmusAutoOutcome).getOutcome() } + stateProducer() is LitmusAutoState -> { + { this } } else -> error("outcome not specified") diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index 83a70b3..1667c38 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -13,7 +13,6 @@ class JvmThreadRunner : LitmusRunner() { affinityMap: AffinityMap? ): () -> LitmusResult { val barrier = barrierProducer(test.threadCount) - val outcomeFinalizer = test.outcomeFinalizer val threads = List(test.threadCount) { threadIndex -> Thread { @@ -28,8 +27,7 @@ class JvmThreadRunner : LitmusRunner() { return { threads.forEach { it.join() } - val outcomes = states.asSequence().map { it.outcomeFinalizer() } - outcomes.calcStats(test.outcomeSpec) + calcStats(states, test.outcomeSpec, test.outcomeFinalizer) } } } diff --git a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index 618273d..2f83707 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -77,8 +77,7 @@ class PthreadRunner : LitmusRunner() { nativeHeap.free(pthreadVar) threadDataRef.dispose() } - val outcomes = states.asSequence().map { test.outcomeFinalizer(it) } - outcomes.calcStats(test.outcomeSpec) + calcStats(states, test.outcomeSpec, test.outcomeFinalizer) } } } diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index e3b1a76..ef26631 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -27,7 +27,6 @@ class WorkerRunner : LitmusRunner() { ) val barrier = barrierProducer(test.threadCount) - val outcomeFinalizer = test.outcomeFinalizer val workers = List(test.threadCount) { Worker.start() } val futures = workers.mapIndexed { threadIndex, worker -> @@ -58,8 +57,7 @@ class WorkerRunner : LitmusRunner() { return { futures.forEach { it.result } // await all results workers.forEach { it.requestTermination().result } // waits for all workers to stop - val outcomes = states.asSequence().map { it.outcomeFinalizer() } - outcomes.calcStats(test.outcomeSpec) + calcStats(states, test.outcomeSpec, test.outcomeFinalizer) } } } diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index 10856ec..4477861 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -23,11 +23,11 @@ fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path): Boolean { private fun generateWrapperCode(test: LitmusTest<*>): String { val stateClass = test.stateProducer()::class - require(stateClass.allSuperclasses.contains(LitmusAutoOutcome::class)) { + require(stateClass.allSuperclasses.contains(LitmusAutoState::class)) { "to use JCStress, test state must extend some LitmusAutoOutcome (e.g. LitmusIIOutcome)" } - val autoOutcomeClassList = stateClass.superclasses.filter { it.isSubclassOf(LitmusAutoOutcome::class) } + val autoOutcomeClassList = stateClass.superclasses.filter { it.isSubclassOf(LitmusAutoState::class) } require(autoOutcomeClassList.size == 1) { "test state should extend exactly one LitmusAutoOutcome" } val outcomeTypeName = autoOutcomeClassList.first().simpleName!! .removePrefix("Litmus") diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt index 2089d73..1365a9a 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt @@ -10,7 +10,7 @@ class IntHolderCtor { } val ATOM: LitmusTest<*> = litmusTest({ - object : LitmusIOutcome() { + object : LitmusIState() { var x = 0 } }) { @@ -27,7 +27,7 @@ val ATOM: LitmusTest<*> = litmusTest({ } val SB: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { + object : LitmusIIState() { var x = 0 var y = 0 } @@ -50,7 +50,7 @@ val SB: LitmusTest<*> = litmusTest({ } val SBVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { + object : LitmusIIState() { @Volatile var x = 0 @@ -75,7 +75,7 @@ val SBVolatile: LitmusTest<*> = litmusTest({ } val MP: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { + object : LitmusIIState() { var x = 0 var y = 0 } @@ -97,7 +97,7 @@ val MP: LitmusTest<*> = litmusTest({ } val MPVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { + object : LitmusIIState() { @Volatile var x = 0 @@ -121,7 +121,7 @@ val MPVolatile: LitmusTest<*> = litmusTest({ } val MP_DRF: LitmusTest<*> = litmusTest({ - object : LitmusIOutcome() { + object : LitmusIState() { var x = 0 @Volatile @@ -142,7 +142,7 @@ val MP_DRF: LitmusTest<*> = litmusTest({ } val CoRR: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { + object : LitmusIIState() { var x = 0 } }) { @@ -161,201 +161,201 @@ val CoRR: LitmusTest<*> = litmusTest({ } } -val CoRR_CSE: LitmusTest<*> = litmusTest({ - data class Holder(var x: Int) - object : LitmusIIIOutcome() { - val holder1 = Holder(0) - val holder2 = holder1 - } -}) { - thread { - holder1.x = 1 - } - thread { - val h1 = holder1 - val h2 = holder2 - r1 = h1.x - r2 = h2.x - r3 = h1.x - } - spec { - interesting(1, 0, 0) - interesting(1, 1, 0) - default(LitmusOutcomeType.ACCEPTED) - } -} - -val IRIW: LitmusTest<*> = litmusTest({ - object : LitmusIIIIOutcome() { - var x = 0 - var y = 0 - } -}) { - thread { - x = 1 - } - thread { - y = 1 - } - thread { - r1 = x - r2 = y - } - thread { - r3 = y - r4 = x - } - spec { - interesting(1, 0, 1, 0) - interesting(0, 1, 0, 1) - default(LitmusOutcomeType.ACCEPTED) - } -} - -val IRIWVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIIIOutcome() { - @Volatile - var x = 0 - - @Volatile - var y = 0 - } -}) { - thread { - x = 1 - } - thread { - y = 1 - } - thread { - r1 = x - r2 = y - } - thread { - r3 = y - r4 = x - } - spec { - forbid(1, 0, 1, 0) - default(LitmusOutcomeType.ACCEPTED) - } -} - -val UPUB: LitmusTest<*> = litmusTest({ - object : LitmusIOutcome() { - var h: IntHolder? = null - } -}) { - thread { - h = IntHolder(0) - } - thread { - r1 = h?.x ?: -1 - } - spec { - accept(0) - accept(-1) - } -} - -val UPUBCtor: LitmusTest<*> = litmusTest({ - object : LitmusIOutcome() { - var h: IntHolderCtor? = null - } -}) { - thread { - h = IntHolderCtor() - } - thread { - r1 = h?.x ?: -1 - } - spec { - accept(1) - accept(-1) - } -} - -val LB_DEPS_OOTA: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { - var x = 0 - var y = 0 - } -}) { - thread { - r1 = x - y = r1 - } - thread { - r2 = y - x = r2 - } - spec { - accept(0, 0) - } -} - -val LB: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { - var x = 0 - var y = 0 - } -}) { - thread { - r1 = x - y = 1 - } - thread { - r2 = y - x = 1 - } - spec { - accept(0, 0) - accept(1, 0) - accept(0, 1) - interesting(1, 1) - } -} - -val LBVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { - @Volatile - var x = 0 - - @Volatile - var y = 0 - } -}) { - thread { - r1 = x - y = 1 - } - thread { - r2 = y - x = 1 - } - spec { - accept(0, 0) - accept(1, 0) - accept(0, 1) - } -} - -val LBFakeDEPS: LitmusTest<*> = litmusTest({ - object : LitmusIIOutcome() { - var x = 0 - var y = 0 - } -}) { - thread { - r1 = x - y = 1 + r1 * 0 - } - thread { - r2 = y - x = r2 - } - spec { - accept(0, 0) - accept(0, 1) - } -} +//val CoRR_CSE: LitmusTest<*> = litmusTest({ +// data class Holder(var x: Int) +// object : LitmusIIIState() { +// val holder1 = Holder(0) +// val holder2 = holder1 +// } +//}) { +// thread { +// holder1.x = 1 +// } +// thread { +// val h1 = holder1 +// val h2 = holder2 +// r1 = h1.x +// r2 = h2.x +// r3 = h1.x +// } +// spec { +// interesting(1, 0, 0) +// interesting(1, 1, 0) +// default(LitmusOutcomeType.ACCEPTED) +// } +//} +// +//val IRIW: LitmusTest<*> = litmusTest({ +// object : LitmusIIIIState() { +// var x = 0 +// var y = 0 +// } +//}) { +// thread { +// x = 1 +// } +// thread { +// y = 1 +// } +// thread { +// r1 = x +// r2 = y +// } +// thread { +// r3 = y +// r4 = x +// } +// spec { +// interesting(1, 0, 1, 0) +// interesting(0, 1, 0, 1) +// default(LitmusOutcomeType.ACCEPTED) +// } +//} +// +//val IRIWVolatile: LitmusTest<*> = litmusTest({ +// object : LitmusIIIIState() { +// @Volatile +// var x = 0 +// +// @Volatile +// var y = 0 +// } +//}) { +// thread { +// x = 1 +// } +// thread { +// y = 1 +// } +// thread { +// r1 = x +// r2 = y +// } +// thread { +// r3 = y +// r4 = x +// } +// spec { +// forbid(1, 0, 1, 0) +// default(LitmusOutcomeType.ACCEPTED) +// } +//} +// +//val UPUB: LitmusTest<*> = litmusTest({ +// object : LitmusIState() { +// var h: IntHolder? = null +// } +//}) { +// thread { +// h = IntHolder(0) +// } +// thread { +// r1 = h?.x ?: -1 +// } +// spec { +// accept(0) +// accept(-1) +// } +//} +// +//val UPUBCtor: LitmusTest<*> = litmusTest({ +// object : LitmusIState() { +// var h: IntHolderCtor? = null +// } +//}) { +// thread { +// h = IntHolderCtor() +// } +// thread { +// r1 = h?.x ?: -1 +// } +// spec { +// accept(1) +// accept(-1) +// } +//} +// +//val LB_DEPS_OOTA: LitmusTest<*> = litmusTest({ +// object : LitmusIIState() { +// var x = 0 +// var y = 0 +// } +//}) { +// thread { +// r1 = x +// y = r1 +// } +// thread { +// r2 = y +// x = r2 +// } +// spec { +// accept(0, 0) +// } +//} +// +//val LB: LitmusTest<*> = litmusTest({ +// object : LitmusIIState() { +// var x = 0 +// var y = 0 +// } +//}) { +// thread { +// r1 = x +// y = 1 +// } +// thread { +// r2 = y +// x = 1 +// } +// spec { +// accept(0, 0) +// accept(1, 0) +// accept(0, 1) +// interesting(1, 1) +// } +//} +// +//val LBVolatile: LitmusTest<*> = litmusTest({ +// object : LitmusIIState() { +// @Volatile +// var x = 0 +// +// @Volatile +// var y = 0 +// } +//}) { +// thread { +// r1 = x +// y = 1 +// } +// thread { +// r2 = y +// x = 1 +// } +// spec { +// accept(0, 0) +// accept(1, 0) +// accept(0, 1) +// } +//} +// +//val LBFakeDEPS: LitmusTest<*> = litmusTest({ +// object : LitmusIIState() { +// var x = 0 +// var y = 0 +// } +//}) { +// thread { +// r1 = x +// y = 1 + r1 * 0 +// } +// thread { +// r2 = y +// x = r2 +// } +// spec { +// accept(0, 0) +// accept(0, 1) +// } +//} From c829ecd01fc3226aeb1aa6de05d2dfb8d92e8ed8 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 19 Feb 2024 02:41:38 +0100 Subject: [PATCH 25/87] Implement CustomList, confirm that it improves performance --- .../kotlin/komem/litmus/LitmusRunner.kt | 8 ++--- .../commonMain/kotlin/komem/litmus/Utils.kt | 32 +++++++++++++++++++ .../kotlin/komem/litmus/JvmThreadRunner.kt | 2 +- .../kotlin/komem.litmus/PthreadRunner.kt | 2 +- .../kotlin/komem.litmus/WorkerRunner.kt | 2 +- .../kotlin/komem/litmus/JCStressRunner.kt | 2 +- 6 files changed, 40 insertions(+), 8 deletions(-) diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index 579c245..3d8a77a 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -11,7 +11,7 @@ abstract class LitmusRunner { */ protected abstract fun startTest( test: LitmusTest, - states: List, + states: CustomList, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap?, @@ -22,7 +22,7 @@ abstract class LitmusRunner { * does not need to allocate states. */ open fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { - val states = List(params.batchSize) { test.stateProducer() } + val states = CustomList(params.batchSize) { test.stateProducer() } return startTest(test, states, params.barrierProducer, params.syncPeriod, params.affinityMap) } @@ -41,7 +41,7 @@ abstract class LitmusRunner { ): List<() -> LitmusResult> { // separated due to allocations severely impacting threads val allStates = List(instances) { - List(params.batchSize) { test.stateProducer() } + CustomList(params.batchSize) { test.stateProducer() } } val allJoinHandles = List(instances) { instanceIndex -> val newAffinityMap = params.affinityMap?.let { oldMap -> @@ -70,7 +70,7 @@ abstract class LitmusRunner { class LongHolder(var value: Long) // the absolute majority of outcomes will be declared in spec - val fastPathOutcomes = ArrayList(spec.accepted + spec.interesting + spec.forbidden) + val fastPathOutcomes = (spec.accepted + spec.interesting + spec.forbidden).toCustomList() val fastPathCounts = Array(fastPathOutcomes.size) { 0L } val totalCounts = mutableMapOf() diff --git a/core/src/commonMain/kotlin/komem/litmus/Utils.kt b/core/src/commonMain/kotlin/komem/litmus/Utils.kt index 647df87..488db48 100644 --- a/core/src/commonMain/kotlin/komem/litmus/Utils.kt +++ b/core/src/commonMain/kotlin/komem/litmus/Utils.kt @@ -24,3 +24,35 @@ fun List>.tableFormat(hasHeader: Boolean = false): String { } expect fun cpuCount(): Int + +/** + * A wrapper over Array that is also a list. + */ +@Suppress("UNCHECKED_CAST") +class CustomList(override val size: Int, initializer: (Int) -> T) : List { + private val delegate = Array(size, initializer) + + override fun get(index: Int) = delegate[index] as T + + override fun iterator() = object : Iterator { + private val iter = delegate.iterator() + override fun next() = iter.next() as T + override fun hasNext() = iter.hasNext() + } + + override fun isEmpty() = size == 0 + + override fun contains(element: T) = Iterable { iterator() }.contains(element) + + override fun indexOf(element: T) = Iterable { iterator() }.indexOf(element) + + override fun containsAll(elements: Collection) = elements.all { contains(it) } + + override fun lastIndexOf(element: T) = Iterable { iterator() }.lastIndexOf(element) + + override fun subList(fromIndex: Int, toIndex: Int) = throw UnsupportedOperationException() + override fun listIterator() = throw UnsupportedOperationException() + override fun listIterator(index: Int) = throw UnsupportedOperationException() +} + +fun List.toCustomList(): CustomList = CustomList(size) { this[it] } diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index 1667c38..3b1aa87 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -7,7 +7,7 @@ class JvmThreadRunner : LitmusRunner() { override fun startTest( test: LitmusTest, - states: List, + states: CustomList, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? diff --git a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index 2f83707..24ba4c0 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -32,7 +32,7 @@ class PthreadRunner : LitmusRunner() { @OptIn(ExperimentalForeignApi::class) override fun startTest( test: LitmusTest, - states: List, + states: CustomList, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index ef26631..7ab6ce2 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -13,7 +13,7 @@ class WorkerRunner : LitmusRunner() { @OptIn(ObsoleteWorkersApi::class) override fun startTest( test: LitmusTest, - states: List, + states: CustomList, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt index 741fa66..29f1779 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt @@ -17,7 +17,7 @@ class JCStressRunner( override fun startTest( test: LitmusTest, - states: List, + states: CustomList, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? From d2b4b4a7af85cb77278a72456c48615e5612ac32 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 25 Feb 2024 19:31:50 +0100 Subject: [PATCH 26/87] MR fixes --- .../kotlin/komem/litmus/LitmusAutoState.kt | 55 ++- .../kotlin/komem/litmus/LitmusOutcomeStats.kt | 12 +- .../kotlin/komem/litmus/LitmusRunner.kt | 24 +- .../commonMain/kotlin/komem/litmus/Utils.kt | 18 +- .../kotlin/komem/litmus/tests/ClassicTests.kt | 396 +++++++++--------- 5 files changed, 259 insertions(+), 246 deletions(-) diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt index ff64c71..88fbb42 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt @@ -27,6 +27,19 @@ fun LitmusAutoState(values: List): LitmusAutoState { val o2 = values[1] if (o1 is Int && o2 is Int) return LitmusIIState(o1, o2) } + if (values.size == 3) { + val o1 = values[0] + val o2 = values[1] + val o3 = values[2] + if (o1 is Int && o2 is Int && o3 is Int) return LitmusIIIState(o1, o2, o3) + } + if (values.size == 4) { + val o1 = values[0] + val o2 = values[1] + val o3 = values[2] + val o4 = values[3] + if (o1 is Int && o2 is Int && o3 is Int && o4 is Int) return LitmusIIIIState(o1, o2, o3, o4) + } error("no LitmusAutoOutcome to support values $values") } @@ -54,19 +67,29 @@ open class LitmusIIState( } } -//open class LitmusIIIState( -// var r1: Int = 0, -// var r2: Int = 0, -// var r3: Int = 0, -//) : LitmusAutoState { -// override fun getOutcome() = listOf(r1, r2, r3) -//} -// -//open class LitmusIIIIState( -// var r1: Int = 0, -// var r2: Int = 0, -// var r3: Int = 0, -// var r4: Int = 0, -//) : LitmusAutoState { -// override fun getOutcome() = listOf(r1, r2, r3, r4) -//} \ No newline at end of file +open class LitmusIIIState( + var r1: Int = 0, + var r2: Int = 0, + var r3: Int = 0, +) : LitmusAutoState { + final override fun toString() = "($r1, $r2, $r3)" + final override fun hashCode() = r1 shl 20 + r2 shl 10 + r3 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIIIState) return false + return r1 == o.r1 && r2 == o.r2 && r2 == o.r3 + } +} + +open class LitmusIIIIState( + var r1: Int = 0, + var r2: Int = 0, + var r3: Int = 0, + var r4: Int = 0, +) : LitmusAutoState { + final override fun toString() = "($r1, $r2, $r3, $r4)" + final override fun hashCode() = r1 shl 24 + r2 shl 16 + r3 shl 8 + r4 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIIIIState) return false + return r1 == o.r1 && r2 == o.r2 && r2 == o.r3 && r4 == o.r4 + } +} diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt index 7aaa0a2..bad4397 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt @@ -11,9 +11,9 @@ data class LitmusOutcomeStats( ) data class LitmusOutcomeSpec( - val accepted: List, - val interesting: List, - val forbidden: List, + val accepted: Set, + val interesting: Set, + val forbidden: Set, val default: LitmusOutcomeType, ) { fun getType(outcome: LitmusOutcome) = when (outcome) { @@ -33,9 +33,9 @@ data class LitmusOutcomeSpec( * The same applies to `interesting()` and `forbid()`. */ class LitmusOutcomeSpecScope { - private val accepted = mutableListOf() - private val interesting = mutableListOf() - private val forbidden = mutableListOf() + private val accepted = mutableSetOf() + private val interesting = mutableSetOf() + private val forbidden = mutableSetOf() private var default: LitmusOutcomeType? = null fun accept(outcome: LitmusOutcome) { diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index 3d8a77a..150b9a4 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -70,24 +70,28 @@ abstract class LitmusRunner { class LongHolder(var value: Long) // the absolute majority of outcomes will be declared in spec - val fastPathOutcomes = (spec.accepted + spec.interesting + spec.forbidden).toCustomList() - val fastPathCounts = Array(fastPathOutcomes.size) { 0L } + val specifiedOutcomes = (spec.accepted + spec.interesting + spec.forbidden).toCustomList() + val specifiedCounts = Array(specifiedOutcomes.size) { 0L } + val useFastPath = specifiedOutcomes.size <= 10 + val totalCounts = mutableMapOf() for (s in states) { val outcome = outcomeFinalizer(s) - val i = fastPathOutcomes.indexOf(outcome) - if (i != -1) { - fastPathCounts[i]++ - } else { - totalCounts.getOrPut(outcome) { LongHolder(0L) }.value++ + if (useFastPath) { + val i = specifiedOutcomes.indexOf(outcome) + if (i != -1) { + specifiedCounts[i]++ + continue + } } + totalCounts.getOrPut(outcome) { LongHolder(0L) }.value++ } // update totalCounts with fastPathCounts - for (i in fastPathCounts.indices) { - val count = fastPathCounts[i] + for (i in specifiedCounts.indices) { + val count = specifiedCounts[i] if (count > 0) totalCounts - .getOrPut(fastPathOutcomes[i]) { LongHolder(0L) } + .getOrPut(specifiedOutcomes[i]) { LongHolder(0L) } .value = count } diff --git a/core/src/commonMain/kotlin/komem/litmus/Utils.kt b/core/src/commonMain/kotlin/komem/litmus/Utils.kt index 488db48..ca55d9d 100644 --- a/core/src/commonMain/kotlin/komem/litmus/Utils.kt +++ b/core/src/commonMain/kotlin/komem/litmus/Utils.kt @@ -29,7 +29,7 @@ expect fun cpuCount(): Int * A wrapper over Array that is also a list. */ @Suppress("UNCHECKED_CAST") -class CustomList(override val size: Int, initializer: (Int) -> T) : List { +class CustomList(override val size: Int, initializer: (Int) -> T) : AbstractList() { private val delegate = Array(size, initializer) override fun get(index: Int) = delegate[index] as T @@ -39,20 +39,6 @@ class CustomList(override val size: Int, initializer: (Int) -> T) : List { override fun next() = iter.next() as T override fun hasNext() = iter.hasNext() } - - override fun isEmpty() = size == 0 - - override fun contains(element: T) = Iterable { iterator() }.contains(element) - - override fun indexOf(element: T) = Iterable { iterator() }.indexOf(element) - - override fun containsAll(elements: Collection) = elements.all { contains(it) } - - override fun lastIndexOf(element: T) = Iterable { iterator() }.lastIndexOf(element) - - override fun subList(fromIndex: Int, toIndex: Int) = throw UnsupportedOperationException() - override fun listIterator() = throw UnsupportedOperationException() - override fun listIterator(index: Int) = throw UnsupportedOperationException() } -fun List.toCustomList(): CustomList = CustomList(size) { this[it] } +fun Collection.toCustomList(): CustomList = with(iterator()) { CustomList(size) { next() } } diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt index 1365a9a..6950fd3 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt @@ -161,201 +161,201 @@ val CoRR: LitmusTest<*> = litmusTest({ } } -//val CoRR_CSE: LitmusTest<*> = litmusTest({ -// data class Holder(var x: Int) -// object : LitmusIIIState() { -// val holder1 = Holder(0) -// val holder2 = holder1 -// } -//}) { -// thread { -// holder1.x = 1 -// } -// thread { -// val h1 = holder1 -// val h2 = holder2 -// r1 = h1.x -// r2 = h2.x -// r3 = h1.x -// } -// spec { -// interesting(1, 0, 0) -// interesting(1, 1, 0) -// default(LitmusOutcomeType.ACCEPTED) -// } -//} -// -//val IRIW: LitmusTest<*> = litmusTest({ -// object : LitmusIIIIState() { -// var x = 0 -// var y = 0 -// } -//}) { -// thread { -// x = 1 -// } -// thread { -// y = 1 -// } -// thread { -// r1 = x -// r2 = y -// } -// thread { -// r3 = y -// r4 = x -// } -// spec { -// interesting(1, 0, 1, 0) -// interesting(0, 1, 0, 1) -// default(LitmusOutcomeType.ACCEPTED) -// } -//} -// -//val IRIWVolatile: LitmusTest<*> = litmusTest({ -// object : LitmusIIIIState() { -// @Volatile -// var x = 0 -// -// @Volatile -// var y = 0 -// } -//}) { -// thread { -// x = 1 -// } -// thread { -// y = 1 -// } -// thread { -// r1 = x -// r2 = y -// } -// thread { -// r3 = y -// r4 = x -// } -// spec { -// forbid(1, 0, 1, 0) -// default(LitmusOutcomeType.ACCEPTED) -// } -//} -// -//val UPUB: LitmusTest<*> = litmusTest({ -// object : LitmusIState() { -// var h: IntHolder? = null -// } -//}) { -// thread { -// h = IntHolder(0) -// } -// thread { -// r1 = h?.x ?: -1 -// } -// spec { -// accept(0) -// accept(-1) -// } -//} -// -//val UPUBCtor: LitmusTest<*> = litmusTest({ -// object : LitmusIState() { -// var h: IntHolderCtor? = null -// } -//}) { -// thread { -// h = IntHolderCtor() -// } -// thread { -// r1 = h?.x ?: -1 -// } -// spec { -// accept(1) -// accept(-1) -// } -//} -// -//val LB_DEPS_OOTA: LitmusTest<*> = litmusTest({ -// object : LitmusIIState() { -// var x = 0 -// var y = 0 -// } -//}) { -// thread { -// r1 = x -// y = r1 -// } -// thread { -// r2 = y -// x = r2 -// } -// spec { -// accept(0, 0) -// } -//} -// -//val LB: LitmusTest<*> = litmusTest({ -// object : LitmusIIState() { -// var x = 0 -// var y = 0 -// } -//}) { -// thread { -// r1 = x -// y = 1 -// } -// thread { -// r2 = y -// x = 1 -// } -// spec { -// accept(0, 0) -// accept(1, 0) -// accept(0, 1) -// interesting(1, 1) -// } -//} -// -//val LBVolatile: LitmusTest<*> = litmusTest({ -// object : LitmusIIState() { -// @Volatile -// var x = 0 -// -// @Volatile -// var y = 0 -// } -//}) { -// thread { -// r1 = x -// y = 1 -// } -// thread { -// r2 = y -// x = 1 -// } -// spec { -// accept(0, 0) -// accept(1, 0) -// accept(0, 1) -// } -//} -// -//val LBFakeDEPS: LitmusTest<*> = litmusTest({ -// object : LitmusIIState() { -// var x = 0 -// var y = 0 -// } -//}) { -// thread { -// r1 = x -// y = 1 + r1 * 0 -// } -// thread { -// r2 = y -// x = r2 -// } -// spec { -// accept(0, 0) -// accept(0, 1) -// } -//} +val CoRR_CSE: LitmusTest<*> = litmusTest({ + data class Holder(var x: Int) + object : LitmusIIIState() { + val holder1 = Holder(0) + val holder2 = holder1 + } +}) { + thread { + holder1.x = 1 + } + thread { + val h1 = holder1 + val h2 = holder2 + r1 = h1.x + r2 = h2.x + r3 = h1.x + } + spec { + interesting(1, 0, 0) + interesting(1, 1, 0) + default(LitmusOutcomeType.ACCEPTED) + } +} + +val IRIW: LitmusTest<*> = litmusTest({ + object : LitmusIIIIState() { + var x = 0 + var y = 0 + } +}) { + thread { + x = 1 + } + thread { + y = 1 + } + thread { + r1 = x + r2 = y + } + thread { + r3 = y + r4 = x + } + spec { + interesting(1, 0, 1, 0) + interesting(0, 1, 0, 1) + default(LitmusOutcomeType.ACCEPTED) + } +} + +val IRIWVolatile: LitmusTest<*> = litmusTest({ + object : LitmusIIIIState() { + @Volatile + var x = 0 + + @Volatile + var y = 0 + } +}) { + thread { + x = 1 + } + thread { + y = 1 + } + thread { + r1 = x + r2 = y + } + thread { + r3 = y + r4 = x + } + spec { + forbid(1, 0, 1, 0) + default(LitmusOutcomeType.ACCEPTED) + } +} + +val UPUB: LitmusTest<*> = litmusTest({ + object : LitmusIState() { + var h: IntHolder? = null + } +}) { + thread { + h = IntHolder(0) + } + thread { + r1 = h?.x ?: -1 + } + spec { + accept(0) + accept(-1) + } +} + +val UPUBCtor: LitmusTest<*> = litmusTest({ + object : LitmusIState() { + var h: IntHolderCtor? = null + } +}) { + thread { + h = IntHolderCtor() + } + thread { + r1 = h?.x ?: -1 + } + spec { + accept(1) + accept(-1) + } +} + +val LB_DEPS_OOTA: LitmusTest<*> = litmusTest({ + object : LitmusIIState() { + var x = 0 + var y = 0 + } +}) { + thread { + r1 = x + y = r1 + } + thread { + r2 = y + x = r2 + } + spec { + accept(0, 0) + } +} + +val LB: LitmusTest<*> = litmusTest({ + object : LitmusIIState() { + var x = 0 + var y = 0 + } +}) { + thread { + r1 = x + y = 1 + } + thread { + r2 = y + x = 1 + } + spec { + accept(0, 0) + accept(1, 0) + accept(0, 1) + interesting(1, 1) + } +} + +val LBVolatile: LitmusTest<*> = litmusTest({ + object : LitmusIIState() { + @Volatile + var x = 0 + + @Volatile + var y = 0 + } +}) { + thread { + r1 = x + y = 1 + } + thread { + r2 = y + x = 1 + } + spec { + accept(0, 0) + accept(1, 0) + accept(0, 1) + } +} + +val LBFakeDEPS: LitmusTest<*> = litmusTest({ + object : LitmusIIState() { + var x = 0 + var y = 0 + } +}) { + thread { + r1 = x + y = 1 + r1 * 0 + } + thread { + r2 = y + x = r2 + } + spec { + accept(0, 0) + accept(0, 1) + } +} From eb199353de0a57eadbca72ab61bb5040e70caf66 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 26 Feb 2024 09:59:48 +0100 Subject: [PATCH 27/87] Fix typo in LitmusIIIIState --- core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt index 88fbb42..bf54041 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt @@ -76,7 +76,7 @@ open class LitmusIIIState( final override fun hashCode() = r1 shl 20 + r2 shl 10 + r3 final override fun equals(o: Any?): Boolean { if (o !is LitmusIIIState) return false - return r1 == o.r1 && r2 == o.r2 && r2 == o.r3 + return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 } } @@ -90,6 +90,6 @@ open class LitmusIIIIState( final override fun hashCode() = r1 shl 24 + r2 shl 16 + r3 shl 8 + r4 final override fun equals(o: Any?): Boolean { if (o !is LitmusIIIIState) return false - return r1 == o.r1 && r2 == o.r2 && r2 == o.r3 && r4 == o.r4 + return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 && r4 == o.r4 } } From 8944afc12c0aaecc7522d8024dbc1d4214980e85 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 7 Apr 2024 22:41:07 +0200 Subject: [PATCH 28/87] More PR fixes --- .../kotlin/komem/litmus/LitmusAutoOutcome.kt | 104 ++++++++++++++++++ .../kotlin/komem/litmus/LitmusAutoState.kt | 95 ---------------- .../kotlin/komem/litmus/LitmusOutcomeStats.kt | 14 +-- .../kotlin/komem/litmus/LitmusRunner.kt | 10 +- .../kotlin/komem/litmus/LitmusTest.kt | 2 +- .../commonMain/kotlin/komem/litmus/Utils.kt | 17 +-- .../kotlin/komem/litmus/JvmThreadRunner.kt | 2 +- .../kotlin/komem.litmus/PthreadRunner.kt | 10 +- .../kotlin/komem.litmus/WorkerRunner.kt | 4 +- .../src/main/kotlin/komem/litmus/Codegen.kt | 4 +- .../kotlin/komem/litmus/JCStressRunner.kt | 2 +- .../kotlin/komem/litmus/tests/ClassicTests.kt | 32 +++--- 12 files changed, 140 insertions(+), 156 deletions(-) create mode 100644 core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt delete mode 100644 core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt new file mode 100644 index 0000000..93e186a --- /dev/null +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt @@ -0,0 +1,104 @@ +package komem.litmus + +/** + * A convenience interface to simplify specifying outcomes. + * + * All classes implementing this interface provide some r1, r2, ... variables + * to write the outcome into. If a litmus test's state extends one of these classes, + * specifying `outcome { ... }` is not necessary, as it will be inferred from r1, r2, ... + * + * Children classes should override `hashCode()` and `equals()` so that they are compared + * based on their outcome only. They should also override `toString()` so that they only display + * their outcome when printed. For these reasons the functions are overridden in this + * interface such that their implementation is forced in children. + * + * These classes are also used as outcomes themselves in order to better utilize resources. + */ +sealed interface LitmusAutoOutcome { + override fun toString(): String + override fun hashCode(): Int + + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") + override fun equals(o: Any?): Boolean +} + +open class LitmusIOutcome( + var r1: Int = 0, +) : LitmusAutoOutcome { + // single values are handled differently (see LitmusOutcomeSpecScope) + final override fun toString() = "$r1" + final override fun hashCode() = r1 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIOutcome) return false + return r1 == o.r1 + } + + // note: if S is LitmusAutoState, even single values should only be interpreted as r1 + // hence no overloads for accept() and so on +} + +open class LitmusIIOutcome( + var r1: Int = 0, + var r2: Int = 0 +) : LitmusAutoOutcome { + final override fun toString() = "($r1, $r2)" + final override fun hashCode() = r1 shl 16 + r2 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIIOutcome) return false + return r1 == o.r1 && r2 == o.r2 + } +} + +fun LitmusOutcomeSpecScope.accept(r1: Int, r2: Int) = + accept(LitmusIIOutcome(r1, r2)) + +fun LitmusOutcomeSpecScope.interesting(r1: Int, r2: Int) = + interesting(LitmusIIOutcome(r1, r2)) + +fun LitmusOutcomeSpecScope.forbid(r1: Int, r2: Int) = + forbid(LitmusIIOutcome(r1, r2)) + +open class LitmusIIIOutcome( + var r1: Int = 0, + var r2: Int = 0, + var r3: Int = 0, +) : LitmusAutoOutcome { + final override fun toString() = "($r1, $r2, $r3)" + final override fun hashCode() = r1 shl 20 + r2 shl 10 + r3 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIIIOutcome) return false + return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 + } +} + +fun LitmusOutcomeSpecScope.accept(r1: Int, r2: Int, r3: Int) = + accept(LitmusIIIOutcome(r1, r2, r3)) + +fun LitmusOutcomeSpecScope.interesting(r1: Int, r2: Int, r3: Int) = + interesting(LitmusIIIOutcome(r1, r2, r3)) + +fun LitmusOutcomeSpecScope.forbid(r1: Int, r2: Int, r3: Int) = + forbid(LitmusIIIOutcome(r1, r2, r3)) + +open class LitmusIIIIOutcome( + var r1: Int = 0, + var r2: Int = 0, + var r3: Int = 0, + var r4: Int = 0, +) : LitmusAutoOutcome { + final override fun toString() = "($r1, $r2, $r3, $r4)" + final override fun hashCode() = r1 shl 24 + r2 shl 16 + r3 shl 8 + r4 + final override fun equals(o: Any?): Boolean { + if (o !is LitmusIIIIOutcome) return false + return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 && r4 == o.r4 + } +} + +fun LitmusOutcomeSpecScope.accept(r1: Int, r2: Int, r3: Int, r4: Int) = + accept(LitmusIIIIOutcome(r1, r2, r3, r4)) + +fun LitmusOutcomeSpecScope.interesting(r1: Int, r2: Int, r3: Int, r4: Int) = + interesting(LitmusIIIIOutcome(r1, r2, r3, r4)) + +fun LitmusOutcomeSpecScope.forbid(r1: Int, r2: Int, r3: Int, r4: Int) = + forbid(LitmusIIIIOutcome(r1, r2, r3, r4)) diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt deleted file mode 100644 index bf54041..0000000 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoState.kt +++ /dev/null @@ -1,95 +0,0 @@ -package komem.litmus - -/** - * A convenience interface to simplify specifying outcomes. - * - * All classes implementing this interface provide some r1, r2, ... variables - * to write the outcome into. If a litmus test's state extends one of these classes, - * specifying `outcome { ... }` is not necessary, as it will be inferred from r1, r2, ... - * - * These classes are also used as outcomes themselves to better utilize resources. - */ -interface LitmusAutoState { - override fun toString(): String - override fun hashCode(): Int - - @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") - override fun equals(o: Any?): Boolean -} - -fun LitmusAutoState(values: List): LitmusAutoState { - if (values.size == 1) { - val o1 = values[0] - if (o1 is Int) return LitmusIState(o1) - } - if (values.size == 2) { - val o1 = values[0] - val o2 = values[1] - if (o1 is Int && o2 is Int) return LitmusIIState(o1, o2) - } - if (values.size == 3) { - val o1 = values[0] - val o2 = values[1] - val o3 = values[2] - if (o1 is Int && o2 is Int && o3 is Int) return LitmusIIIState(o1, o2, o3) - } - if (values.size == 4) { - val o1 = values[0] - val o2 = values[1] - val o3 = values[2] - val o4 = values[3] - if (o1 is Int && o2 is Int && o3 is Int && o4 is Int) return LitmusIIIIState(o1, o2, o3, o4) - } - error("no LitmusAutoOutcome to support values $values") -} - -open class LitmusIState( - var r1: Int = 0, -) : LitmusAutoState { - // single values are handled differently (see LitmusOutcomeSpecScope) - final override fun toString() = "($r1)" - final override fun hashCode() = r1 - final override fun equals(o: Any?): Boolean { - if (o !is LitmusIState) return false - return r1 == o.r1 - } -} - -open class LitmusIIState( - var r1: Int = 0, - var r2: Int = 0 -) : LitmusAutoState { - final override fun toString() = "($r1, $r2)" - final override fun hashCode() = r1 shl 16 + r2 - final override fun equals(o: Any?): Boolean { - if (o !is LitmusIIState) return false - return r1 == o.r1 && r2 == o.r2 - } -} - -open class LitmusIIIState( - var r1: Int = 0, - var r2: Int = 0, - var r3: Int = 0, -) : LitmusAutoState { - final override fun toString() = "($r1, $r2, $r3)" - final override fun hashCode() = r1 shl 20 + r2 shl 10 + r3 - final override fun equals(o: Any?): Boolean { - if (o !is LitmusIIIState) return false - return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 - } -} - -open class LitmusIIIIState( - var r1: Int = 0, - var r2: Int = 0, - var r3: Int = 0, - var r4: Int = 0, -) : LitmusAutoState { - final override fun toString() = "($r1, $r2, $r3, $r4)" - final override fun hashCode() = r1 shl 24 + r2 shl 16 + r3 shl 8 + r4 - final override fun equals(o: Any?): Boolean { - if (o !is LitmusIIIIState) return false - return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 && r4 == o.r4 - } -} diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt index bad4397..93afbdf 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt @@ -25,7 +25,8 @@ data class LitmusOutcomeSpec( } /** - * For convenience, it is possible to use `accept(vararg values)` if outcome is a [LitmusAutoState]. + * For convenience, it is possible to use `accept(vararg values)` if outcome is a [LitmusAutoOutcome]. + * See [LitmusAutoOutcome] file for those functions. The generic is used precisely for this. * * Use `accept(value)` otherwise. Notice that `accept(a, b)` is NOT the same as `accept(a); accept(b)`. * Dev note: this is the reason why 'single values are handled differently' in some other places. @@ -59,17 +60,6 @@ class LitmusOutcomeSpecScope { fun build() = LitmusOutcomeSpec(accepted, interesting, forbidden, default ?: LitmusOutcomeType.FORBIDDEN) } -// if S is LitmusAutoState, even single values should only be interpreted as r1 - -fun LitmusOutcomeSpecScope.accept(vararg values: LitmusOutcome) = - accept(LitmusAutoState(values.toList())) - -fun LitmusOutcomeSpecScope.interesting(vararg values: LitmusOutcome) = - interesting(LitmusAutoState(values.toList())) - -fun LitmusOutcomeSpecScope.forbid(vararg values: LitmusOutcome) = - forbid(LitmusAutoState(values.toList())) - typealias LitmusResult = List fun LitmusResult.generateTable(): String { diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt index 150b9a4..2ec6989 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt @@ -11,7 +11,7 @@ abstract class LitmusRunner { */ protected abstract fun startTest( test: LitmusTest, - states: CustomList, + states: Array, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap?, @@ -22,7 +22,7 @@ abstract class LitmusRunner { * does not need to allocate states. */ open fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { - val states = CustomList(params.batchSize) { test.stateProducer() } + val states = TypedArray(params.batchSize) { test.stateProducer() } return startTest(test, states, params.barrierProducer, params.syncPeriod, params.affinityMap) } @@ -41,7 +41,7 @@ abstract class LitmusRunner { ): List<() -> LitmusResult> { // separated due to allocations severely impacting threads val allStates = List(instances) { - CustomList(params.batchSize) { test.stateProducer() } + TypedArray(params.batchSize) { test.stateProducer() } } val allJoinHandles = List(instances) { instanceIndex -> val newAffinityMap = params.affinityMap?.let { oldMap -> @@ -61,7 +61,7 @@ abstract class LitmusRunner { } protected fun calcStats( - states: List, + states: Array, spec: LitmusOutcomeSpec, outcomeFinalizer: (S) -> LitmusOutcome ): LitmusResult { @@ -70,7 +70,7 @@ abstract class LitmusRunner { class LongHolder(var value: Long) // the absolute majority of outcomes will be declared in spec - val specifiedOutcomes = (spec.accepted + spec.interesting + spec.forbidden).toCustomList() + val specifiedOutcomes = (spec.accepted + spec.interesting + spec.forbidden).toTypedArray() val specifiedCounts = Array(specifiedOutcomes.size) { 0L } val useFastPath = specifiedOutcomes.size <= 10 diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt index 48ae252..42e6361 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt @@ -35,7 +35,7 @@ class LitmusTestScope( if (!::outcomeSpec.isInitialized) error("spec not specified") val outcomeFinalizer: S.() -> LitmusOutcome = when { ::outcomeFinalizer.isInitialized -> outcomeFinalizer - stateProducer() is LitmusAutoState -> { + stateProducer() is LitmusAutoOutcome -> { { this } } diff --git a/core/src/commonMain/kotlin/komem/litmus/Utils.kt b/core/src/commonMain/kotlin/komem/litmus/Utils.kt index ca55d9d..151c0f2 100644 --- a/core/src/commonMain/kotlin/komem/litmus/Utils.kt +++ b/core/src/commonMain/kotlin/komem/litmus/Utils.kt @@ -25,20 +25,5 @@ fun List>.tableFormat(hasHeader: Boolean = false): String { expect fun cpuCount(): Int -/** - * A wrapper over Array that is also a list. - */ @Suppress("UNCHECKED_CAST") -class CustomList(override val size: Int, initializer: (Int) -> T) : AbstractList() { - private val delegate = Array(size, initializer) - - override fun get(index: Int) = delegate[index] as T - - override fun iterator() = object : Iterator { - private val iter = delegate.iterator() - override fun next() = iter.next() as T - override fun hasNext() = iter.hasNext() - } -} - -fun Collection.toCustomList(): CustomList = with(iterator()) { CustomList(size) { next() } } +fun TypedArray(size: Int, init: (Int) -> S): Array = Array(size, init) as Array diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt index 3b1aa87..f4d34f5 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt @@ -7,7 +7,7 @@ class JvmThreadRunner : LitmusRunner() { override fun startTest( test: LitmusTest, - states: CustomList, + states: Array, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? diff --git a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt index 24ba4c0..dc7e55d 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt @@ -6,14 +6,14 @@ import platform.posix.pthread_create import platform.posix.pthread_join import platform.posix.strerror -private class ThreadData( - val states: List, +private class ThreadData( + val states: Array, val function: (Any) -> Unit, val syncPeriod: Int, val barrier: Barrier, ) -private fun threadRoutine(data: ThreadData): Unit = with(data) { +private fun threadRoutine(data: ThreadData): Unit = with(data) { for (i in states.indices) { function(states[i]) if (i % syncPeriod == 0) barrier.await() @@ -32,7 +32,7 @@ class PthreadRunner : LitmusRunner() { @OptIn(ExperimentalForeignApi::class) override fun startTest( test: LitmusTest, - states: CustomList, + states: Array, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? @@ -52,7 +52,7 @@ class PthreadRunner : LitmusRunner() { __newthread = pthreadVar.ptr, __attr = null, __start_routine = staticCFunction { - val data = it!!.asStableRef().get() + val data = it!!.asStableRef>().get() threadRoutine(data) return@staticCFunction null }, diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt index 7ab6ce2..0bdc6e7 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt @@ -13,14 +13,14 @@ class WorkerRunner : LitmusRunner() { @OptIn(ObsoleteWorkersApi::class) override fun startTest( test: LitmusTest, - states: CustomList, + states: Array, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? ): () -> LitmusResult { data class WorkerContext( - val states: List, + val states: Array, val threadFunction: S.() -> Any?, val syncPeriod: Int, val barrier: Barrier, diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index 4477861..10856ec 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -23,11 +23,11 @@ fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path): Boolean { private fun generateWrapperCode(test: LitmusTest<*>): String { val stateClass = test.stateProducer()::class - require(stateClass.allSuperclasses.contains(LitmusAutoState::class)) { + require(stateClass.allSuperclasses.contains(LitmusAutoOutcome::class)) { "to use JCStress, test state must extend some LitmusAutoOutcome (e.g. LitmusIIOutcome)" } - val autoOutcomeClassList = stateClass.superclasses.filter { it.isSubclassOf(LitmusAutoState::class) } + val autoOutcomeClassList = stateClass.superclasses.filter { it.isSubclassOf(LitmusAutoOutcome::class) } require(autoOutcomeClassList.size == 1) { "test state should extend exactly one LitmusAutoOutcome" } val outcomeTypeName = autoOutcomeClassList.first().simpleName!! .removePrefix("Litmus") diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt index 29f1779..39237d4 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt @@ -17,7 +17,7 @@ class JCStressRunner( override fun startTest( test: LitmusTest, - states: CustomList, + states: Array, barrierProducer: BarrierProducer, syncPeriod: Int, affinityMap: AffinityMap? diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt index 6950fd3..2089d73 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt @@ -10,7 +10,7 @@ class IntHolderCtor { } val ATOM: LitmusTest<*> = litmusTest({ - object : LitmusIState() { + object : LitmusIOutcome() { var x = 0 } }) { @@ -27,7 +27,7 @@ val ATOM: LitmusTest<*> = litmusTest({ } val SB: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { var x = 0 var y = 0 } @@ -50,7 +50,7 @@ val SB: LitmusTest<*> = litmusTest({ } val SBVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { @Volatile var x = 0 @@ -75,7 +75,7 @@ val SBVolatile: LitmusTest<*> = litmusTest({ } val MP: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { var x = 0 var y = 0 } @@ -97,7 +97,7 @@ val MP: LitmusTest<*> = litmusTest({ } val MPVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { @Volatile var x = 0 @@ -121,7 +121,7 @@ val MPVolatile: LitmusTest<*> = litmusTest({ } val MP_DRF: LitmusTest<*> = litmusTest({ - object : LitmusIState() { + object : LitmusIOutcome() { var x = 0 @Volatile @@ -142,7 +142,7 @@ val MP_DRF: LitmusTest<*> = litmusTest({ } val CoRR: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { var x = 0 } }) { @@ -163,7 +163,7 @@ val CoRR: LitmusTest<*> = litmusTest({ val CoRR_CSE: LitmusTest<*> = litmusTest({ data class Holder(var x: Int) - object : LitmusIIIState() { + object : LitmusIIIOutcome() { val holder1 = Holder(0) val holder2 = holder1 } @@ -186,7 +186,7 @@ val CoRR_CSE: LitmusTest<*> = litmusTest({ } val IRIW: LitmusTest<*> = litmusTest({ - object : LitmusIIIIState() { + object : LitmusIIIIOutcome() { var x = 0 var y = 0 } @@ -213,7 +213,7 @@ val IRIW: LitmusTest<*> = litmusTest({ } val IRIWVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIIIState() { + object : LitmusIIIIOutcome() { @Volatile var x = 0 @@ -242,7 +242,7 @@ val IRIWVolatile: LitmusTest<*> = litmusTest({ } val UPUB: LitmusTest<*> = litmusTest({ - object : LitmusIState() { + object : LitmusIOutcome() { var h: IntHolder? = null } }) { @@ -259,7 +259,7 @@ val UPUB: LitmusTest<*> = litmusTest({ } val UPUBCtor: LitmusTest<*> = litmusTest({ - object : LitmusIState() { + object : LitmusIOutcome() { var h: IntHolderCtor? = null } }) { @@ -276,7 +276,7 @@ val UPUBCtor: LitmusTest<*> = litmusTest({ } val LB_DEPS_OOTA: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { var x = 0 var y = 0 } @@ -295,7 +295,7 @@ val LB_DEPS_OOTA: LitmusTest<*> = litmusTest({ } val LB: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { var x = 0 var y = 0 } @@ -317,7 +317,7 @@ val LB: LitmusTest<*> = litmusTest({ } val LBVolatile: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { @Volatile var x = 0 @@ -341,7 +341,7 @@ val LBVolatile: LitmusTest<*> = litmusTest({ } val LBFakeDEPS: LitmusTest<*> = litmusTest({ - object : LitmusIIState() { + object : LitmusIIOutcome() { var x = 0 var y = 0 } From 7e7c4518eba8e44d6c25eb819b163b710bcf94ca Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 7 Apr 2024 23:44:43 +0200 Subject: [PATCH 29/87] Fix single outcome bug, notice jcstress-interop not working --- .../kotlin/komem/litmus/LitmusAutoOutcome.kt | 40 +++++++++++++------ .../kotlin/komem/litmus/LitmusOutcomeStats.kt | 24 ++++++----- .../src/main/kotlin/komem/litmus/Codegen.kt | 22 +++------- .../kotlin/komem/litmus/tests/ClassicTests.kt | 3 +- .../kotlin/komem/litmus/tests/CustomTests.kt | 9 ++--- .../komem/litmus/tests/UPUBExtraTests.kt | 26 +++++------- 6 files changed, 61 insertions(+), 63 deletions(-) diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt index 93e186a..474b437 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt @@ -20,12 +20,14 @@ sealed interface LitmusAutoOutcome { @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") override fun equals(o: Any?): Boolean + + // for JCStress interop + fun toList(): List } open class LitmusIOutcome( var r1: Int = 0, ) : LitmusAutoOutcome { - // single values are handled differently (see LitmusOutcomeSpecScope) final override fun toString() = "$r1" final override fun hashCode() = r1 final override fun equals(o: Any?): Boolean { @@ -33,10 +35,18 @@ open class LitmusIOutcome( return r1 == o.r1 } - // note: if S is LitmusAutoState, even single values should only be interpreted as r1 - // hence no overloads for accept() and so on + final override fun toList() = listOf(r1) } +fun LitmusOutcomeSpecScope.accept(r1: Int) = + accept(setOf(LitmusIOutcome(r1))) + +fun LitmusOutcomeSpecScope.interesting(r1: Int) = + interesting(setOf(LitmusIOutcome(r1))) + +fun LitmusOutcomeSpecScope.forbid(r1: Int) = + forbid(setOf(LitmusIOutcome(r1))) + open class LitmusIIOutcome( var r1: Int = 0, var r2: Int = 0 @@ -47,16 +57,18 @@ open class LitmusIIOutcome( if (o !is LitmusIIOutcome) return false return r1 == o.r1 && r2 == o.r2 } + + final override fun toList() = listOf(r1, r2) } fun LitmusOutcomeSpecScope.accept(r1: Int, r2: Int) = - accept(LitmusIIOutcome(r1, r2)) + accept(setOf(LitmusIIOutcome(r1, r2))) fun LitmusOutcomeSpecScope.interesting(r1: Int, r2: Int) = - interesting(LitmusIIOutcome(r1, r2)) + interesting(setOf(LitmusIIOutcome(r1, r2))) fun LitmusOutcomeSpecScope.forbid(r1: Int, r2: Int) = - forbid(LitmusIIOutcome(r1, r2)) + forbid(setOf(LitmusIIOutcome(r1, r2))) open class LitmusIIIOutcome( var r1: Int = 0, @@ -69,16 +81,18 @@ open class LitmusIIIOutcome( if (o !is LitmusIIIOutcome) return false return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 } + + final override fun toList() = listOf(r1, r2, r3) } fun LitmusOutcomeSpecScope.accept(r1: Int, r2: Int, r3: Int) = - accept(LitmusIIIOutcome(r1, r2, r3)) + accept(setOf(LitmusIIIOutcome(r1, r2, r3))) fun LitmusOutcomeSpecScope.interesting(r1: Int, r2: Int, r3: Int) = - interesting(LitmusIIIOutcome(r1, r2, r3)) + interesting(setOf(LitmusIIIOutcome(r1, r2, r3))) fun LitmusOutcomeSpecScope.forbid(r1: Int, r2: Int, r3: Int) = - forbid(LitmusIIIOutcome(r1, r2, r3)) + forbid(setOf(LitmusIIIOutcome(r1, r2, r3))) open class LitmusIIIIOutcome( var r1: Int = 0, @@ -92,13 +106,15 @@ open class LitmusIIIIOutcome( if (o !is LitmusIIIIOutcome) return false return r1 == o.r1 && r2 == o.r2 && r3 == o.r3 && r4 == o.r4 } + + final override fun toList() = listOf(r1, r2, r3, r4) } fun LitmusOutcomeSpecScope.accept(r1: Int, r2: Int, r3: Int, r4: Int) = - accept(LitmusIIIIOutcome(r1, r2, r3, r4)) + accept(setOf(LitmusIIIIOutcome(r1, r2, r3, r4))) fun LitmusOutcomeSpecScope.interesting(r1: Int, r2: Int, r3: Int, r4: Int) = - interesting(LitmusIIIIOutcome(r1, r2, r3, r4)) + interesting(setOf(LitmusIIIIOutcome(r1, r2, r3, r4))) fun LitmusOutcomeSpecScope.forbid(r1: Int, r2: Int, r3: Int, r4: Int) = - forbid(LitmusIIIIOutcome(r1, r2, r3, r4)) + forbid(setOf(LitmusIIIIOutcome(r1, r2, r3, r4))) diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt index 93afbdf..5201563 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt +++ b/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt @@ -25,13 +25,13 @@ data class LitmusOutcomeSpec( } /** - * For convenience, it is possible to use `accept(vararg values)` if outcome is a [LitmusAutoOutcome]. - * See [LitmusAutoOutcome] file for those functions. The generic is used precisely for this. + * For convenience, it is possible to use `accept(r1, r2, ...)` if outcome is a [LitmusAutoOutcome]. + * In other cases, use `accept(setOf(...))` to accept one or many values. Note that to accept an iterable, + * it has to be wrapped in an extra `setOf()`. All of this applies as well to `interesting()` and `forbid()`. * - * Use `accept(value)` otherwise. Notice that `accept(a, b)` is NOT the same as `accept(a); accept(b)`. - * Dev note: this is the reason why 'single values are handled differently' in some other places. + * See [LitmusAutoOutcome] file for those extension functions. The generic is used precisely for them. * - * The same applies to `interesting()` and `forbid()`. + * single values are handled differently !!!!!!!!!!!!! TODO */ class LitmusOutcomeSpecScope { private val accepted = mutableSetOf() @@ -39,16 +39,18 @@ class LitmusOutcomeSpecScope { private val forbidden = mutableSetOf() private var default: LitmusOutcomeType? = null - fun accept(outcome: LitmusOutcome) { - accepted.add(outcome) + // note: if S is LitmusIOutcome, even single values should be interpreted as r1 + + fun accept(outcomes: Iterable) { + accepted.addAll(outcomes) } - fun interesting(outcome: LitmusOutcome) { - interesting.add(outcome) + fun interesting(outcomes: Iterable) { + interesting.addAll(outcomes) } - fun forbid(outcome: LitmusOutcome) { - forbidden.add(outcome) + fun forbid(outcomes: Iterable) { + forbidden.addAll(outcomes) } fun default(outcomeType: LitmusOutcomeType) { diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index 10856ec..aabdbe2 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -9,7 +9,7 @@ import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.superclasses fun generateWrapperFile(test: LitmusTest<*>, jcstressDirectory: Path): Boolean { - val targetFile = jcstressDirectory / "generatedSrc/main/java/komem/litmus/${test.javaClassName}.java" + val targetFile = jcstressDirectory / "generatedSrc/main/komem/litmus/${test.javaClassName}.java" targetFile.createParentDirectories() val targetCode = try { generateWrapperCode(test) @@ -51,23 +51,13 @@ private fun generateWrapperCode(test: LitmusTest<*>): String { val javaArbiterDecl: String = run { val jcstressResultClassName = outcomeTypeName + "_Result" - if (outcomeVarCount > 1) { - """ + """ @Arbiter public void a($jcstressResultClassName r) { - List<$outcomeVarType> result = (List<$outcomeVarType>) fA.invoke(state); + List<$outcomeVarType> result = (List<$outcomeVarType>) ((LitmusAutoOutcome) fA.invoke(state)).toList(); ${List(outcomeVarCount) { "r.r${it + 1} = result.get($it);" }.joinToString("\n ")} } - """.trim() - } else { - // single values are handled differently - """ - @Arbiter - public void a($jcstressResultClassName r) { - r.r1 = ($outcomeVarType) fA.invoke(state); - } - """.trimIndent() - } + """.trim() } val jcstressOutcomeDecls: String = run { @@ -75,9 +65,9 @@ public void a($jcstressResultClassName r) { test.outcomeSpec.interesting.associateWith { "ACCEPTABLE_INTERESTING" } + test.outcomeSpec.forbidden.associateWith { "FORBIDDEN" } - // since only AutoOutcome is allowed, each outcome is a list (unless it's a single value) + // since only AutoOutcome is allowed, the cast is safe outcomes.map { (o, t) -> - val oId = if (outcomeVarCount > 1) (o as List<*>).joinToString(", ") else o.toString() + val oId = (o as LitmusAutoOutcome).toList().joinToString(", ") "@Outcome(id = \"$oId\", expect = $t)" }.joinToString("\n") } diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt index 2089d73..264b4c0 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt @@ -66,11 +66,11 @@ val SBVolatile: LitmusTest<*> = litmusTest({ y = 1 r2 = x } - // no need for explicit outcome{} spec { accept(0, 1) accept(1, 0) accept(1, 1) + forbid(0, 0) // redundant as forbidden is the default } } @@ -207,7 +207,6 @@ val IRIW: LitmusTest<*> = litmusTest({ } spec { interesting(1, 0, 1, 0) - interesting(0, 1, 0, 1) default(LitmusOutcomeType.ACCEPTED) } } diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt index cd47055..bc53f6d 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt @@ -1,13 +1,11 @@ package komem.litmus.tests -import komem.litmus.LitmusTest -import komem.litmus.litmusTest +import komem.litmus.* val MPNoDRF: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var x = 0 var y = 0 - var o = 0 } }) { thread { @@ -15,9 +13,8 @@ val MPNoDRF: LitmusTest<*> = litmusTest({ y = 1 } thread { - o = if (y != 0) x else -1 + r1 = if (y != 0) x else -1 } - outcome { o } spec { accept(1) accept(-1) diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt b/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt index bf32935..c649944 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt +++ b/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt @@ -1,23 +1,23 @@ package komem.litmus.tests +import komem.litmus.LitmusIOutcome import komem.litmus.LitmusTest +import komem.litmus.accept import komem.litmus.litmusTest import kotlin.concurrent.Volatile val UPUBVolatile: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { @Volatile var h: IntHolder? = null - var o = 0 } }) { thread { h = IntHolder(0) } thread { - o = h?.x ?: -1 + r1 = h?.x ?: -1 } - outcome { o } spec { accept(0) accept(-1) @@ -25,18 +25,16 @@ val UPUBVolatile: LitmusTest<*> = litmusTest({ } val UPUBArray: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var arr: Array? = null - var o = 0 } }) { thread { arr = Array(10) { 0 } } thread { - o = arr?.get(0) ?: -1 + r1 = arr?.get(0) ?: -1 } - outcome { o } spec { accept(0) accept(-1) @@ -47,9 +45,8 @@ private class UPUBRefInner(val x: Int) private class UPUBRefHolder(val ref: UPUBRefInner) val UPUBRef: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var h: UPUBRefHolder? = null - var o = 0 } }) { thread { @@ -58,9 +55,8 @@ val UPUBRef: LitmusTest<*> = litmusTest({ } thread { val t = h - o = t?.ref?.x ?: -1 + r1 = t?.ref?.x ?: -1 } - outcome { o } spec { accept(1) accept(-1) @@ -81,18 +77,16 @@ private class UPUBIntHolderInnerLeaking { } val UBUBCtorLeaking: LitmusTest<*> = litmusTest({ - object { + object : LitmusIOutcome() { var h = UPUBIntHolderInnerLeaking() - var o = 0 } }) { thread { h.InnerHolder() } thread { - o = h.ih?.x ?: -1 + r1 = h.ih?.x ?: -1 } - outcome { o } spec { accept(1) accept(0) From 1a73d1b0873ee54fcb6925bcbc69e36cab4f2168 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Mon, 8 Apr 2024 00:46:58 +0200 Subject: [PATCH 30/87] Fix jcstress interop --- cli/build.gradle.kts | 3 +- core/build.gradle.kts | 54 ------------------- jcstress-wrapper/build.gradle.kts | 18 +++++++ .../src/main/kotlin/komem/litmus/Codegen.kt | 4 +- .../kotlin/komem/litmus/JCStressRunner.kt | 2 +- jcstress/pom.xml | 5 ++ testsuite/build.gradle.kts | 1 + 7 files changed, 29 insertions(+), 58 deletions(-) diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index 07f2bc6..f30cbc3 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -41,7 +41,8 @@ kotlin { tasks.whenTaskAdded { if (name == "jvmRun") { - dependsOn(":core:copyLibToJCStress") + dependsOn(":jcstress-wrapper:copyCoreToJCStress") + dependsOn(":jcstress-wrapper:copyTestsuiteToJCStress") dependsOn(":jcstress-wrapper:run") } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index ef36b83..a134ace 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.incremental.createDirectory - plugins { kotlin("multiplatform") `java-library` @@ -39,21 +37,10 @@ kotlin { } } } - if (gradle.startParameter.taskNames.any { it.contains("bitcode") }) { - val tempDir = projectDir.resolve("temp/bitcode") - if (!tempDir.exists()) tempDir.createDirectory() - kotlinOptions.freeCompilerArgs = listOf("-Xtemporary-files-dir=${tempDir.absolutePath}") - } } } } sourceSets { - commonMain { - val atomicfuVersion = project.findProperty("atomicfuVersion") - dependencies { - implementation("org.jetbrains.kotlinx:atomicfu:$atomicfuVersion") - } - } commonTest { dependencies { implementation(kotlin("test")) @@ -65,46 +52,5 @@ kotlin { implementation(kotlin("reflect")) } } - - macosMain { - kotlin.srcDirs("src/macosMain/kotlin") - } - - linuxMain { - kotlin.srcDirs("src/linuxMain/kotlin") - } - } -} - -val bitcodeInternal by tasks.register("bitcodeInternal") { - val tempDir = projectDir.resolve("temp/bitcode") - doLast { - exec { - executable = "sh" - args = listOf( - "-c", """ - llvm-dis -o ${tempDir.resolve("bitcode.txt")} ${tempDir.resolve("out.bc")} - """.trimIndent() - ) - } } } - -tasks.register("bitcodeDebug") { - dependsOn(tasks.matching { it.name.startsWith("linkDebugExecutable") }) - finalizedBy(bitcodeInternal) -} - -tasks.register("bitcodeRelease") { - dependsOn(tasks.matching { it.name.startsWith("linkReleaseExecutable") }) - finalizedBy(bitcodeInternal) -} - -val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) - -tasks.register("copyLibToJCStress") { - dependsOn("jvmJar") - from(layout.buildDirectory.file("libs/core-jvm-$version.jar")) - rename { "litmusktJvm-1.0.jar" } - into(jcsDir.resolve("libs/komem/litmus/litmusktJvm/1.0/")) -} diff --git a/jcstress-wrapper/build.gradle.kts b/jcstress-wrapper/build.gradle.kts index 5b6f8c1..a717570 100644 --- a/jcstress-wrapper/build.gradle.kts +++ b/jcstress-wrapper/build.gradle.kts @@ -16,3 +16,21 @@ dependencies { implementation(project(":testsuite")) implementation(kotlin("reflect")) } + +val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) + +tasks.register("copyCoreToJCStress") { + dependsOn(":core:jvmJar") + from(project(":core").layout.buildDirectory.file("libs/core-jvm-1.0-SNAPSHOT.jar")) + if (inputs.sourceFiles.isEmpty) throw BuildCancelledException("missing files to copy") + rename { "litmusktJvm-1.0.jar" } + into(jcsDir.resolve("libs/komem/litmus/litmusktJvm/1.0/")) +} + +tasks.register("copyTestsuiteToJCStress") { + dependsOn(":testsuite:jvmJar") + from(project(":testsuite").layout.buildDirectory.file("libs/testsuite-jvm.jar")) + if (inputs.sourceFiles.isEmpty) throw BuildCancelledException("missing files to copy") + rename { "litmusktJvmTestsuite-1.0.jar" } + into(jcsDir.resolve("libs/komem/litmus/litmusktJvmTestsuite/1.0/")) +} diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt index aabdbe2..530ce3b 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt @@ -54,7 +54,7 @@ private fun generateWrapperCode(test: LitmusTest<*>): String { """ @Arbiter public void a($jcstressResultClassName r) { - List<$outcomeVarType> result = (List<$outcomeVarType>) ((LitmusAutoOutcome) fA.invoke(state)).toList(); + List<$outcomeVarType> result = (List<$outcomeVarType>) (Object) ((LitmusAutoOutcome) fA.invoke(state)).toList(); ${List(outcomeVarCount) { "r.r${it + 1} = result.get($it);" }.joinToString("\n ")} } """.trim() @@ -100,7 +100,7 @@ fun wrapperCode( ) = """ package komem.litmus; -import komem.litmus.testsuite.*; +import komem.litmus.tests.*; import kotlin.Unit; import kotlin.jvm.functions.Function1; diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt index 39237d4..59a7ec9 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt @@ -37,7 +37,7 @@ class JCStressRunner( // TODO: optimize for many tests (do not invoke jcstress many times) override fun startTest(params: LitmusRunParams, test: LitmusTest): () -> LitmusResult { - val mvn = ProcessBuilder("mvn", "install", "verify") + val mvn = ProcessBuilder("mvn", "install", "verify", "-U") .directory(jcstressDirectory.toFile()) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .redirectError(ProcessBuilder.Redirect.INHERIT) diff --git a/jcstress/pom.xml b/jcstress/pom.xml index 1a4d3f0..dfb6a2b 100644 --- a/jcstress/pom.xml +++ b/jcstress/pom.xml @@ -80,6 +80,11 @@ THE POSSIBILITY OF SUCH DAMAGE. litmusktJvm 1.0 + + komem.litmus + litmusktJvmTestsuite + 1.0 + diff --git a/testsuite/build.gradle.kts b/testsuite/build.gradle.kts index 1d0bd36..24e4b10 100644 --- a/testsuite/build.gradle.kts +++ b/testsuite/build.gradle.kts @@ -1,6 +1,7 @@ plugins { kotlin("multiplatform") id("com.google.devtools.ksp") version "1.9.20-1.0.13" + `java-library` } kotlin { From 6121e673655ef1481222a79d4560b6d9564f11f9 Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 14 Apr 2024 23:24:29 +0200 Subject: [PATCH 31/87] Change package to org.jetbrains.litmuskt --- .../litmus => org/jetbrains/litmuskt}/CliCommon.kt | 5 +++-- cli/src/jvmMain/kotlin/JvmMain.kt | 2 +- .../{komem/litmus => org/jetbrains/litmuskt}/CliJvm.kt | 10 ++++++++-- cli/src/nativeMain/kotlin/NativeMain.kt | 4 ++-- .../litmus => org/jetbrains/litmuskt}/CliNative.kt | 10 +++++++--- codegen/build.gradle.kts | 2 +- .../jetbrains/litmuskt}/LitmusTestProcessor.kt | 4 ++-- ...gle.devtools.ksp.processing.SymbolProcessorProvider | 2 +- core/build.gradle.kts | 2 +- .../litmus => org/jetbrains/litmuskt}/AffinityMap.kt | 2 +- .../litmus => org/jetbrains/litmuskt}/Barrier.kt | 2 +- .../jetbrains/litmuskt}/LitmusAutoOutcome.kt | 2 +- .../jetbrains/litmuskt}/LitmusOutcomeStats.kt | 2 +- .../jetbrains/litmuskt}/LitmusRunParams.kt | 2 +- .../litmus => org/jetbrains/litmuskt}/LitmusRunner.kt | 2 +- .../litmus => org/jetbrains/litmuskt}/LitmusTest.kt | 2 +- .../{komem/litmus => org/jetbrains/litmuskt}/Utils.kt | 2 +- .../jetbrains/litmuskt}/LitmusOutcomeTest.kt | 3 +++ .../jetbrains/litmuskt}/JvmThreadRunner.kt | 5 ++++- .../litmus => org/jetbrains/litmuskt}/JvmUtils.kt | 2 +- .../jetbrains/litmuskt}/barriers/JvmCyclicBarrier.kt | 4 ++-- .../jetbrains/litmuskt}/barriers/JvmSpinBarrier.kt | 4 ++-- .../jetbrains/litmuskt}/AffinityBindingsImplPosix.kt | 4 +++- .../kotlin/komem/litmus/AffinityBindingsImplNoop.kt | 3 --- .../org/jetbrains/litmuskt/AffinityBindingsImplNoop.kt | 5 +++++ .../jetbrains/litmuskt}/AffinityManager.kt | 3 ++- .../jetbrains/litmuskt}/NativeUtils.kt | 2 +- .../jetbrains/litmuskt}/PthreadRunner.kt | 4 ++-- .../jetbrains/litmuskt}/WorkerRunner.kt | 6 ++++-- .../litmuskt}/barriers/CinteropSpinBarrier.kt | 4 ++-- .../jetbrains/litmuskt}/barriers/KNativeSpinBarrier.kt | 4 ++-- jcstress-wrapper/build.gradle.kts | 4 ++-- jcstress-wrapper/src/main/kotlin/Main.kt | 4 ++-- .../litmus => org/jetbrains/litmuskt}/Codegen.kt | 9 ++++++--- .../jetbrains/litmuskt}/JCStressRunner.kt | 6 ++++-- jcstress/pom.xml | 4 ++-- .../jetbrains/litmuskt}/LitmusTestExtensions.kt | 5 +++-- .../jetbrains/litmuskt}/tests/ClassicTests.kt | 6 ++++-- .../jetbrains/litmuskt}/tests/CustomTests.kt | 6 ++++-- .../jetbrains/litmuskt}/tests/UPUBExtraTests.kt | 10 +++++----- 40 files changed, 99 insertions(+), 65 deletions(-) rename cli/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/CliCommon.kt (97%) rename cli/src/jvmMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/CliJvm.kt (88%) rename cli/src/nativeMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/CliNative.kt (72%) rename codegen/src/main/kotlin/{komem/litmus => org/jetbrains/litmuskt}/LitmusTestProcessor.kt (96%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/AffinityMap.kt (72%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/Barrier.kt (73%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/LitmusAutoOutcome.kt (99%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/LitmusOutcomeStats.kt (99%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/LitmusRunParams.kt (96%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/LitmusRunner.kt (99%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/LitmusTest.kt (98%) rename core/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/Utils.kt (97%) rename core/src/commonTest/kotlin/{komem.litmus => org/jetbrains/litmuskt}/LitmusOutcomeTest.kt (88%) rename core/src/jvmMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/JvmThreadRunner.kt (90%) rename core/src/jvmMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/JvmUtils.kt (68%) rename core/src/jvmMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/barriers/JvmCyclicBarrier.kt (72%) rename core/src/jvmMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/barriers/JvmSpinBarrier.kt (87%) rename core/src/linuxMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/AffinityBindingsImplPosix.kt (91%) delete mode 100644 core/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt create mode 100644 core/src/macosMain/kotlin/org/jetbrains/litmuskt/AffinityBindingsImplNoop.kt rename core/src/nativeMain/kotlin/{komem.litmus => org/jetbrains/litmuskt}/AffinityManager.kt (96%) rename core/src/nativeMain/kotlin/{komem.litmus => org/jetbrains/litmuskt}/NativeUtils.kt (93%) rename core/src/nativeMain/kotlin/{komem.litmus => org/jetbrains/litmuskt}/PthreadRunner.kt (96%) rename core/src/nativeMain/kotlin/{komem.litmus => org/jetbrains/litmuskt}/WorkerRunner.kt (92%) rename core/src/nativeMain/kotlin/{komem.litmus => org/jetbrains/litmuskt}/barriers/CinteropSpinBarrier.kt (84%) rename core/src/nativeMain/kotlin/{komem.litmus => org/jetbrains/litmuskt}/barriers/KNativeSpinBarrier.kt (87%) rename jcstress-wrapper/src/main/kotlin/{komem/litmus => org/jetbrains/litmuskt}/Codegen.kt (95%) rename jcstress-wrapper/src/main/kotlin/{komem/litmus => org/jetbrains/litmuskt}/JCStressRunner.kt (93%) rename testsuite/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/LitmusTestExtensions.kt (50%) rename testsuite/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/tests/ClassicTests.kt (97%) rename testsuite/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/tests/CustomTests.kt (69%) rename testsuite/src/commonMain/kotlin/{komem/litmus => org/jetbrains/litmuskt}/tests/UPUBExtraTests.kt (88%) diff --git a/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt b/cli/src/commonMain/kotlin/org/jetbrains/litmuskt/CliCommon.kt similarity index 97% rename from cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt rename to cli/src/commonMain/kotlin/org/jetbrains/litmuskt/CliCommon.kt index 218a1a3..c6b48c4 100644 --- a/cli/src/commonMain/kotlin/komem/litmus/CliCommon.kt +++ b/cli/src/commonMain/kotlin/org/jetbrains/litmuskt/CliCommon.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.arguments.argument @@ -7,7 +7,8 @@ import com.github.ajalt.clikt.parameters.arguments.multiple import com.github.ajalt.clikt.parameters.arguments.transformAll import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.int -import komem.litmus.generated.LitmusTestRegistry +import org.jetbrains.litmuskt.generated.LitmusTestRegistry +import org.jetbrains.litmuskt.* import kotlin.time.Duration abstract class CliCommon : CliktCommand( diff --git a/cli/src/jvmMain/kotlin/JvmMain.kt b/cli/src/jvmMain/kotlin/JvmMain.kt index 8397aa7..3d9c77a 100644 --- a/cli/src/jvmMain/kotlin/JvmMain.kt +++ b/cli/src/jvmMain/kotlin/JvmMain.kt @@ -1,3 +1,3 @@ -import komem.litmus.CliJvm +import org.jetbrains.litmuskt.CliJvm fun main(args: Array) = CliJvm().main(args) diff --git a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt b/cli/src/jvmMain/kotlin/org/jetbrains/litmuskt/CliJvm.kt similarity index 88% rename from cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt rename to cli/src/jvmMain/kotlin/org/jetbrains/litmuskt/CliJvm.kt index eb7d2e0..7d7281b 100644 --- a/cli/src/jvmMain/kotlin/komem/litmus/CliJvm.kt +++ b/cli/src/jvmMain/kotlin/org/jetbrains/litmuskt/CliJvm.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt import com.github.ajalt.clikt.parameters.groups.OptionGroup import com.github.ajalt.clikt.parameters.groups.groupChoice @@ -8,7 +8,13 @@ import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option import jcstressDirectory -import komem.litmus.barriers.JvmSpinBarrier +import org.jetbrains.litmuskt.JCStressRunner +import org.jetbrains.litmuskt.CliCommon +import org.jetbrains.litmuskt.barriers.JvmSpinBarrier +import org.jetbrains.litmuskt.JvmThreadRunner +import org.jetbrains.litmuskt.LitmusRunner +import org.jetbrains.litmuskt.runTest +import org.jetbrains.litmuskt.variateRunParams private sealed class RunnerOptions : OptionGroup() { abstract val runner: LitmusRunner diff --git a/cli/src/nativeMain/kotlin/NativeMain.kt b/cli/src/nativeMain/kotlin/NativeMain.kt index 42e5e4b..b5ef711 100755 --- a/cli/src/nativeMain/kotlin/NativeMain.kt +++ b/cli/src/nativeMain/kotlin/NativeMain.kt @@ -1,4 +1,4 @@ -import komem.litmus.CliNative -import komem.litmus.commonMain +import org.jetbrains.litmuskt.CliNative +import org.jetbrains.litmuskt.commonMain fun main(args: Array) = commonMain(args, CliNative()) diff --git a/cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt b/cli/src/nativeMain/kotlin/org/jetbrains/litmuskt/CliNative.kt similarity index 72% rename from cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt rename to cli/src/nativeMain/kotlin/org/jetbrains/litmuskt/CliNative.kt index d52e6c1..178f4bd 100644 --- a/cli/src/nativeMain/kotlin/komem/litmus/CliNative.kt +++ b/cli/src/nativeMain/kotlin/org/jetbrains/litmuskt/CliNative.kt @@ -1,9 +1,13 @@ -package komem.litmus +package org.jetbrains.litmuskt import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.choice -import komem.litmus.barriers.CinteropSpinBarrier +import org.jetbrains.litmuskt.CliCommon +import org.jetbrains.litmuskt.barriers.CinteropSpinBarrier +import org.jetbrains.litmuskt.PthreadRunner +import org.jetbrains.litmuskt.WorkerRunner +import org.jetbrains.litmuskt.AffinityMap class CliNative : CliCommon() { override val runner by option("-r", "--runner") @@ -12,7 +16,7 @@ class CliNative : CliCommon() { private val affinityMapChoices = run { val schedulesMapped = mutableMapOf>("none" to listOf(null)) - getAffinityManager()?.let { + org.jetbrains.litmuskt.getAffinityManager()?.let { schedulesMapped["short"] = it.presetShort() schedulesMapped["long"] = it.presetLong() } diff --git a/codegen/build.gradle.kts b/codegen/build.gradle.kts index b32f804..f9266a7 100644 --- a/codegen/build.gradle.kts +++ b/codegen/build.gradle.kts @@ -2,7 +2,7 @@ plugins { kotlin("multiplatform") } -group = "komem.litmus" +group = "org.jetbrains.litmuskt" version = "1.0-SNAPSHOT" kotlin { diff --git a/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt b/codegen/src/main/kotlin/org/jetbrains/litmuskt/LitmusTestProcessor.kt similarity index 96% rename from codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt rename to codegen/src/main/kotlin/org/jetbrains/litmuskt/LitmusTestProcessor.kt index 8f8d792..f336a31 100644 --- a/codegen/src/main/kotlin/komem/litmus/LitmusTestProcessor.kt +++ b/codegen/src/main/kotlin/org/jetbrains/litmuskt/LitmusTestProcessor.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt import com.google.devtools.ksp.processing.* import com.google.devtools.ksp.symbol.KSAnnotated @@ -12,7 +12,7 @@ class LitmusTestProcessorProvider : SymbolProcessorProvider { class LitmusTestProcessor(private val codeGenerator: CodeGenerator) : SymbolProcessor { override fun process(resolver: Resolver): List { - val basePackage = "komem.litmus" + val basePackage = "org.jetbrains.litmuskt" val registryFileName = "LitmusTestRegistry" val testsPackage = "$basePackage.tests" diff --git a/codegen/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/codegen/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider index 693bed9..8e4e936 100644 --- a/codegen/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +++ b/codegen/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -1 +1 @@ -komem.litmus.LitmusTestProcessorProvider \ No newline at end of file +org.jetbrains.litmuskt.LitmusTestProcessorProvider \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index a134ace..58823fc 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -3,7 +3,7 @@ plugins { `java-library` } -group = "komem.litmus" +group = "org.jetbrains.litmuskt" version = "1.0-SNAPSHOT" kotlin { diff --git a/core/src/commonMain/kotlin/komem/litmus/AffinityMap.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/AffinityMap.kt similarity index 72% rename from core/src/commonMain/kotlin/komem/litmus/AffinityMap.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/AffinityMap.kt index bcdf0e1..7e9387c 100644 --- a/core/src/commonMain/kotlin/komem/litmus/AffinityMap.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/AffinityMap.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt fun interface AffinityMap { fun allowedCores(threadIndex: Int): Set diff --git a/core/src/commonMain/kotlin/komem/litmus/Barrier.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/Barrier.kt similarity index 73% rename from core/src/commonMain/kotlin/komem/litmus/Barrier.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/Barrier.kt index ffad6e7..029c454 100644 --- a/core/src/commonMain/kotlin/komem/litmus/Barrier.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/Barrier.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt interface Barrier { fun await() diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusAutoOutcome.kt similarity index 99% rename from core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusAutoOutcome.kt index 474b437..83633ca 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusAutoOutcome.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusAutoOutcome.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt /** * A convenience interface to simplify specifying outcomes. diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusOutcomeStats.kt similarity index 99% rename from core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusOutcomeStats.kt index 5201563..905cf9b 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusOutcomeStats.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusOutcomeStats.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt typealias LitmusOutcome = Any? diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusRunParams.kt similarity index 96% rename from core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusRunParams.kt index 8dfbe46..1e56251 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunParams.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusRunParams.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt data class LitmusRunParams( val batchSize: Int, diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusRunner.kt similarity index 99% rename from core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusRunner.kt index 2ec6989..7c0fbf7 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusRunner.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusRunner.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt import kotlin.time.Duration import kotlin.time.TimeSource diff --git a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusTest.kt similarity index 98% rename from core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusTest.kt index 42e6361..aa43b4f 100644 --- a/core/src/commonMain/kotlin/komem/litmus/LitmusTest.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusTest.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt data class LitmusTest( val stateProducer: () -> S, diff --git a/core/src/commonMain/kotlin/komem/litmus/Utils.kt b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/Utils.kt similarity index 97% rename from core/src/commonMain/kotlin/komem/litmus/Utils.kt rename to core/src/commonMain/kotlin/org/jetbrains/litmuskt/Utils.kt index 151c0f2..3bd7d76 100644 --- a/core/src/commonMain/kotlin/komem/litmus/Utils.kt +++ b/core/src/commonMain/kotlin/org/jetbrains/litmuskt/Utils.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt fun List>.tableFormat(hasHeader: Boolean = false): String { val columnCount = maxOf { it.size } diff --git a/core/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt b/core/src/commonTest/kotlin/org/jetbrains/litmuskt/LitmusOutcomeTest.kt similarity index 88% rename from core/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt rename to core/src/commonTest/kotlin/org/jetbrains/litmuskt/LitmusOutcomeTest.kt index da0a8fb..b7fe426 100644 --- a/core/src/commonTest/kotlin/komem.litmus/LitmusOutcomeTest.kt +++ b/core/src/commonTest/kotlin/org/jetbrains/litmuskt/LitmusOutcomeTest.kt @@ -1,5 +1,8 @@ package komem.litmus +import org.jetbrains.litmuskt.LitmusOutcomeStats +import org.jetbrains.litmuskt.LitmusOutcomeType +import org.jetbrains.litmuskt.mergeResults import kotlin.test.Test import kotlin.test.assertEquals diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/JvmThreadRunner.kt similarity index 90% rename from core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt rename to core/src/jvmMain/kotlin/org/jetbrains/litmuskt/JvmThreadRunner.kt index f4d34f5..e034edf 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmThreadRunner.kt +++ b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/JvmThreadRunner.kt @@ -1,4 +1,7 @@ -package komem.litmus +package org.jetbrains.litmuskt + +import org.jetbrains.litmuskt.* +import org.jetbrains.litmuskt.* /** * A simplistic runner based on JVM threads. Does not support affinity. diff --git a/core/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/JvmUtils.kt similarity index 68% rename from core/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt rename to core/src/jvmMain/kotlin/org/jetbrains/litmuskt/JvmUtils.kt index 22e0392..4050958 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/JvmUtils.kt +++ b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/JvmUtils.kt @@ -1,3 +1,3 @@ -package komem.litmus +package org.jetbrains.litmuskt actual fun cpuCount() = Runtime.getRuntime().availableProcessors() diff --git a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/barriers/JvmCyclicBarrier.kt similarity index 72% rename from core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt rename to core/src/jvmMain/kotlin/org/jetbrains/litmuskt/barriers/JvmCyclicBarrier.kt index 3278ec6..a05efa3 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmCyclicBarrier.kt +++ b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/barriers/JvmCyclicBarrier.kt @@ -1,6 +1,6 @@ -package komem.litmus.barriers +package org.jetbrains.litmuskt.barriers -import komem.litmus.Barrier +import org.jetbrains.litmuskt.Barrier import java.util.concurrent.CyclicBarrier class JvmCyclicBarrier(threadCount: Int) : Barrier { diff --git a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/barriers/JvmSpinBarrier.kt similarity index 87% rename from core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt rename to core/src/jvmMain/kotlin/org/jetbrains/litmuskt/barriers/JvmSpinBarrier.kt index f1b1520..1375cc6 100644 --- a/core/src/jvmMain/kotlin/komem/litmus/barriers/JvmSpinBarrier.kt +++ b/core/src/jvmMain/kotlin/org/jetbrains/litmuskt/barriers/JvmSpinBarrier.kt @@ -1,6 +1,6 @@ -package komem.litmus.barriers +package org.jetbrains.litmuskt.barriers -import komem.litmus.Barrier +import org.jetbrains.litmuskt.Barrier import java.util.concurrent.atomic.AtomicInteger class JvmSpinBarrier(private val threadCount: Int) : Barrier { diff --git a/core/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt b/core/src/linuxMain/kotlin/org/jetbrains/litmuskt/AffinityBindingsImplPosix.kt similarity index 91% rename from core/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt rename to core/src/linuxMain/kotlin/org/jetbrains/litmuskt/AffinityBindingsImplPosix.kt index c1698c5..d6e4fa8 100644 --- a/core/src/linuxMain/kotlin/komem/litmus/AffinityBindingsImplPosix.kt +++ b/core/src/linuxMain/kotlin/org/jetbrains/litmuskt/AffinityBindingsImplPosix.kt @@ -1,6 +1,8 @@ -package komem.litmus +package org.jetbrains.litmuskt import kaffinity.* +import org.jetbrains.litmuskt.AffinityManager +import org.jetbrains.litmuskt.syscallCheck import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.alloc import kotlinx.cinterop.memScoped diff --git a/core/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt b/core/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt deleted file mode 100644 index f74d3b6..0000000 --- a/core/src/macosMain/kotlin/komem/litmus/AffinityBindingsImplNoop.kt +++ /dev/null @@ -1,3 +0,0 @@ -package komem.litmus - -actual fun getAffinityManager(): AffinityManager? = null \ No newline at end of file diff --git a/core/src/macosMain/kotlin/org/jetbrains/litmuskt/AffinityBindingsImplNoop.kt b/core/src/macosMain/kotlin/org/jetbrains/litmuskt/AffinityBindingsImplNoop.kt new file mode 100644 index 0000000..1ae7e73 --- /dev/null +++ b/core/src/macosMain/kotlin/org/jetbrains/litmuskt/AffinityBindingsImplNoop.kt @@ -0,0 +1,5 @@ +package org.jetbrains.litmuskt + +import org.jetbrains.litmuskt.AffinityManager + +actual fun getAffinityManager(): AffinityManager? = null \ No newline at end of file diff --git a/core/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/AffinityManager.kt similarity index 96% rename from core/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt rename to core/src/nativeMain/kotlin/org/jetbrains/litmuskt/AffinityManager.kt index b47b1a3..ce5ee0f 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/AffinityManager.kt +++ b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/AffinityManager.kt @@ -1,9 +1,10 @@ @file:OptIn(kotlin.native.concurrent.ObsoleteWorkersApi::class) -package komem.litmus +package org.jetbrains.litmuskt // TODO: add documentation +import org.jetbrains.litmuskt.AffinityMap import platform.posix.pthread_t import kotlin.native.concurrent.Worker import kotlin.random.Random diff --git a/core/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/NativeUtils.kt similarity index 93% rename from core/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt rename to core/src/nativeMain/kotlin/org/jetbrains/litmuskt/NativeUtils.kt index 67ec227..236036b 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/NativeUtils.kt +++ b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/NativeUtils.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.toKString diff --git a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/PthreadRunner.kt similarity index 96% rename from core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt rename to core/src/nativeMain/kotlin/org/jetbrains/litmuskt/PthreadRunner.kt index dc7e55d..da3703c 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/PthreadRunner.kt +++ b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/PthreadRunner.kt @@ -1,4 +1,4 @@ -package komem.litmus +package org.jetbrains.litmuskt import kotlinx.cinterop.* import platform.posix.errno @@ -60,7 +60,7 @@ class PthreadRunner : LitmusRunner() { ) if (code != 0) error("pthread_create failed; errno means: ${strerror(errno)?.toKString()}") // TODO: I don't think there is a way to assign affinity before the thread starts (would be useful for MacOS) - getAffinityManager()?.let { am -> + org.jetbrains.litmuskt.getAffinityManager()?.let { am -> val map = affinityMap?.allowedCores(threadIndex) ?: return@let am.setAffinity(pthreadVar.value, map) require(am.getAffinity(pthreadVar.value) == map) { "setting affinity failed" } diff --git a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/WorkerRunner.kt similarity index 92% rename from core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt rename to core/src/nativeMain/kotlin/org/jetbrains/litmuskt/WorkerRunner.kt index 0bdc6e7..39dbe3a 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/WorkerRunner.kt +++ b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/WorkerRunner.kt @@ -1,5 +1,7 @@ -package komem.litmus +package org.jetbrains.litmuskt +import org.jetbrains.litmuskt.* +import org.jetbrains.litmuskt.* import kotlin.native.concurrent.ObsoleteWorkersApi import kotlin.native.concurrent.TransferMode import kotlin.native.concurrent.Worker @@ -31,7 +33,7 @@ class WorkerRunner : LitmusRunner() { val futures = workers.mapIndexed { threadIndex, worker -> affinityMap?.let { affinityMap -> - getAffinityManager()?.run { + org.jetbrains.litmuskt.getAffinityManager()?.run { val cpuSet = affinityMap.allowedCores(threadIndex) setAffinity(worker, cpuSet) require(getAffinity(worker) == cpuSet) { "affinity setting failed" } diff --git a/core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/barriers/CinteropSpinBarrier.kt similarity index 84% rename from core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt rename to core/src/nativeMain/kotlin/org/jetbrains/litmuskt/barriers/CinteropSpinBarrier.kt index 2f2febb..ccda413 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/barriers/CinteropSpinBarrier.kt +++ b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/barriers/CinteropSpinBarrier.kt @@ -1,9 +1,9 @@ -package komem.litmus.barriers +package org.jetbrains.litmuskt.barriers import barrier.CSpinBarrier import barrier.barrier_wait import barrier.create_barrier -import komem.litmus.Barrier +import org.jetbrains.litmuskt.Barrier import kotlinx.cinterop.CPointer import kotlinx.cinterop.ExperimentalForeignApi diff --git a/core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/barriers/KNativeSpinBarrier.kt similarity index 87% rename from core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt rename to core/src/nativeMain/kotlin/org/jetbrains/litmuskt/barriers/KNativeSpinBarrier.kt index f2e6685..93f9de4 100644 --- a/core/src/nativeMain/kotlin/komem.litmus/barriers/KNativeSpinBarrier.kt +++ b/core/src/nativeMain/kotlin/org/jetbrains/litmuskt/barriers/KNativeSpinBarrier.kt @@ -1,6 +1,6 @@ -package komem.litmus.barriers +package org.jetbrains.litmuskt.barriers -import komem.litmus.Barrier +import org.jetbrains.litmuskt.Barrier import kotlin.concurrent.AtomicInt class KNativeSpinBarrier(private val threadCount: Int) : Barrier { diff --git a/jcstress-wrapper/build.gradle.kts b/jcstress-wrapper/build.gradle.kts index a717570..8b01aad 100644 --- a/jcstress-wrapper/build.gradle.kts +++ b/jcstress-wrapper/build.gradle.kts @@ -24,7 +24,7 @@ tasks.register("copyCoreToJCStress") { from(project(":core").layout.buildDirectory.file("libs/core-jvm-1.0-SNAPSHOT.jar")) if (inputs.sourceFiles.isEmpty) throw BuildCancelledException("missing files to copy") rename { "litmusktJvm-1.0.jar" } - into(jcsDir.resolve("libs/komem/litmus/litmusktJvm/1.0/")) + into(jcsDir.resolve("libs/org/jetbrains/litmuskt/litmusktJvm/1.0/")) } tasks.register("copyTestsuiteToJCStress") { @@ -32,5 +32,5 @@ tasks.register("copyTestsuiteToJCStress") { from(project(":testsuite").layout.buildDirectory.file("libs/testsuite-jvm.jar")) if (inputs.sourceFiles.isEmpty) throw BuildCancelledException("missing files to copy") rename { "litmusktJvmTestsuite-1.0.jar" } - into(jcsDir.resolve("libs/komem/litmus/litmusktJvmTestsuite/1.0/")) + into(jcsDir.resolve("libs/org/jetbrains/litmuskt/litmusktJvmTestsuite/1.0/")) } diff --git a/jcstress-wrapper/src/main/kotlin/Main.kt b/jcstress-wrapper/src/main/kotlin/Main.kt index b85dc63..4360803 100644 --- a/jcstress-wrapper/src/main/kotlin/Main.kt +++ b/jcstress-wrapper/src/main/kotlin/Main.kt @@ -1,5 +1,5 @@ -import komem.litmus.generateWrapperFile -import komem.litmus.generated.LitmusTestRegistry +import org.jetbrains.litmuskt.generateWrapperFile +import org.jetbrains.litmuskt.generated.LitmusTestRegistry import kotlin.io.path.Path fun main() { diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt b/jcstress-wrapper/src/main/kotlin/org/jetbrains/litmuskt/Codegen.kt similarity index 95% rename from jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt rename to jcstress-wrapper/src/main/kotlin/org/jetbrains/litmuskt/Codegen.kt index 530ce3b..504892a 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/Codegen.kt +++ b/jcstress-wrapper/src/main/kotlin/org/jetbrains/litmuskt/Codegen.kt @@ -1,5 +1,8 @@ -package komem.litmus +package org.jetbrains.litmuskt +import org.jetbrains.litmuskt.LitmusAutoOutcome +import org.jetbrains.litmuskt.LitmusOutcomeType +import org.jetbrains.litmuskt.LitmusTest import java.nio.file.Path import kotlin.io.path.createParentDirectories import kotlin.io.path.div @@ -98,9 +101,9 @@ fun wrapperCode( javaTestGetter: String, javaArbiterDecl: String, ) = """ -package komem.litmus; +package org.jetbrains.litmuskt; -import komem.litmus.tests.*; +import org.jetbrains.litmuskt.tests.*; import kotlin.Unit; import kotlin.jvm.functions.Function1; diff --git a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt b/jcstress-wrapper/src/main/kotlin/org/jetbrains/litmuskt/JCStressRunner.kt similarity index 93% rename from jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt rename to jcstress-wrapper/src/main/kotlin/org/jetbrains/litmuskt/JCStressRunner.kt index 59a7ec9..a95bda4 100644 --- a/jcstress-wrapper/src/main/kotlin/komem/litmus/JCStressRunner.kt +++ b/jcstress-wrapper/src/main/kotlin/org/jetbrains/litmuskt/JCStressRunner.kt @@ -1,6 +1,8 @@ -package komem.litmus +package org.jetbrains.litmuskt -import komem.litmus.barriers.JvmCyclicBarrier +import org.jetbrains.litmuskt.barriers.JvmCyclicBarrier +import org.jetbrains.litmuskt.* +import org.jetbrains.litmuskt.* import java.nio.file.Path /** diff --git a/jcstress/pom.xml b/jcstress/pom.xml index dfb6a2b..316b3e8 100644 --- a/jcstress/pom.xml +++ b/jcstress/pom.xml @@ -76,12 +76,12 @@ THE POSSIBILITY OF SUCH DAMAGE. 1.9.20 - komem.litmus + org.jetbrains.litmuskt litmusktJvm 1.0 - komem.litmus + org.jetbrains.litmuskt litmusktJvmTestsuite 1.0 diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/LitmusTestExtensions.kt b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusTestExtensions.kt similarity index 50% rename from testsuite/src/commonMain/kotlin/komem/litmus/LitmusTestExtensions.kt rename to testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusTestExtensions.kt index 8f9b68c..d3775de 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/LitmusTestExtensions.kt +++ b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/LitmusTestExtensions.kt @@ -1,6 +1,7 @@ -package komem.litmus +package org.jetbrains.litmuskt -import komem.litmus.generated.LitmusTestRegistry +import org.jetbrains.litmuskt.generated.LitmusTestRegistry +import org.jetbrains.litmuskt.LitmusTest val LitmusTest<*>.name get() = LitmusTestRegistry.resolveName(this) val LitmusTest<*>.javaClassName get() = name.replace('.', '_') diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/ClassicTests.kt similarity index 97% rename from testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt rename to testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/ClassicTests.kt index 264b4c0..cd819d6 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/ClassicTests.kt +++ b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/ClassicTests.kt @@ -1,6 +1,8 @@ -package komem.litmus.tests +package org.jetbrains.litmuskt.tests -import komem.litmus.* +import org.jetbrains.litmuskt.* +import org.jetbrains.litmuskt.* +import org.jetbrains.litmuskt.* import kotlin.concurrent.Volatile data class IntHolder(val x: Int) diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/CustomTests.kt similarity index 69% rename from testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt rename to testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/CustomTests.kt index bc53f6d..ccddac2 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/CustomTests.kt +++ b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/CustomTests.kt @@ -1,6 +1,8 @@ -package komem.litmus.tests +package org.jetbrains.litmuskt.tests -import komem.litmus.* +import org.jetbrains.litmuskt.* +import org.jetbrains.litmuskt.* +import org.jetbrains.litmuskt.* val MPNoDRF: LitmusTest<*> = litmusTest({ object : LitmusIOutcome() { diff --git a/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/UPUBExtraTests.kt similarity index 88% rename from testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt rename to testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/UPUBExtraTests.kt index c649944..3731ecd 100644 --- a/testsuite/src/commonMain/kotlin/komem/litmus/tests/UPUBExtraTests.kt +++ b/testsuite/src/commonMain/kotlin/org/jetbrains/litmuskt/tests/UPUBExtraTests.kt @@ -1,9 +1,9 @@ -package komem.litmus.tests +package org.jetbrains.litmuskt.tests -import komem.litmus.LitmusIOutcome -import komem.litmus.LitmusTest -import komem.litmus.accept -import komem.litmus.litmusTest +import org.jetbrains.litmuskt.LitmusIOutcome +import org.jetbrains.litmuskt.LitmusTest +import org.jetbrains.litmuskt.accept +import org.jetbrains.litmuskt.litmusTest import kotlin.concurrent.Volatile val UPUBVolatile: LitmusTest<*> = litmusTest({ From 8f215a24e85bb91d1310147e4460f421aed607fe Mon Sep 17 00:00:00 2001 From: Denis Lochmelis Date: Sun, 14 Apr 2024 23:51:05 +0200 Subject: [PATCH 32/87] Cleanup build files --- build.gradle.kts | 2 ++ cli/build.gradle.kts | 1 - codegen/build.gradle.kts | 18 +++--------------- core/build.gradle.kts | 15 +++------------ jcstress-wrapper/build.gradle.kts | 22 +++++++++++----------- jcstress/pom.xml | 2 +- testsuite/build.gradle.kts | 1 - 7 files changed, 20 insertions(+), 41 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6a659e6..38ca55b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,6 +8,8 @@ repositories { } subprojects { + group = "org.jetbrains.litmuskt" + version = "1.0-SNAPSHOT" repositories { mavenCentral() gradlePluginPortal() diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index f30cbc3..74e957f 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -19,7 +19,6 @@ kotlin { } jvm { withJava() - jvmToolchain(8) } sourceSets { diff --git a/codegen/build.gradle.kts b/codegen/build.gradle.kts index f9266a7..2fff865 100644 --- a/codegen/build.gradle.kts +++ b/codegen/build.gradle.kts @@ -1,19 +1,7 @@ plugins { - kotlin("multiplatform") + kotlin("jvm") } -group = "org.jetbrains.litmuskt" -version = "1.0-SNAPSHOT" - -kotlin { - jvm() - sourceSets { - jvmMain { - dependencies { - implementation("com.google.devtools.ksp:symbol-processing-api:1.9.10-1.0.13") - } - kotlin.srcDir("src/main/kotlin") - resources.srcDir("src/main/resources") - } - } +dependencies { + implementation("com.google.devtools.ksp:symbol-processing-api:1.9.10-1.0.13") } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 58823fc..9986452 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -3,9 +3,6 @@ plugins { `java-library` } -group = "org.jetbrains.litmuskt" -version = "1.0-SNAPSHOT" - kotlin { val nativeTargets = listOf( linuxX64(), @@ -17,7 +14,7 @@ kotlin { jvm { withJava() - jvmToolchain(8) + jvmToolchain(17) } val hostOs = System.getProperty("os.name") @@ -26,12 +23,12 @@ kotlin { target.apply { compilations.getByName("main") { cinterops { - val barrier by creating { + create("barrier") { defFile(project.file("src/nativeInterop/barrier.def")) headers(project.file("src/nativeInterop/barrier.h")) } if (affinitySupported) { - val affinity by creating { + create("affinity") { defFile(project.file("src/nativeInterop/kaffinity.def")) compilerOpts.add("-D_GNU_SOURCE") } @@ -46,11 +43,5 @@ kotlin { implementation(kotlin("test")) } } - - jvmMain { - dependencies { - implementation(kotlin("reflect")) - } - } } } diff --git a/jcstress-wrapper/build.gradle.kts b/jcstress-wrapper/build.gradle.kts index 8b01aad..7875dc9 100644 --- a/jcstress-wrapper/build.gradle.kts +++ b/jcstress-wrapper/build.gradle.kts @@ -7,30 +7,30 @@ application { mainClass = "MainKt" } -kotlin { - jvmToolchain(8) -} - dependencies { implementation(project(":core")) implementation(project(":testsuite")) implementation(kotlin("reflect")) } -val jcsDir: File get() = File(System.getenv("JCS_DIR") ?: error("JCS_DIR envvar is not set")) +val jcsDir = rootProject.layout.projectDirectory.dir("jcstress") tasks.register("copyCoreToJCStress") { dependsOn(":core:jvmJar") - from(project(":core").layout.buildDirectory.file("libs/core-jvm-1.0-SNAPSHOT.jar")) - if (inputs.sourceFiles.isEmpty) throw BuildCancelledException("missing files to copy") + from(project(":core").layout.buildDirectory.file("libs/core-jvm-$version.jar")) rename { "litmusktJvm-1.0.jar" } - into(jcsDir.resolve("libs/org/jetbrains/litmuskt/litmusktJvm/1.0/")) + into(jcsDir.dir("libs/org/jetbrains/litmuskt/litmusktJvm/1.0/")) + doFirst { + if (inputs.sourceFiles.isEmpty) throw GradleException("missing files to copy") + } } tasks.register("copyTestsuiteToJCStress") { dependsOn(":testsuite:jvmJar") - from(project(":testsuite").layout.buildDirectory.file("libs/testsuite-jvm.jar")) - if (inputs.sourceFiles.isEmpty) throw BuildCancelledException("missing files to copy") + from(project(":testsuite").layout.buildDirectory.file("libs/testsuite-jvm-$version.jar")) rename { "litmusktJvmTestsuite-1.0.jar" } - into(jcsDir.resolve("libs/org/jetbrains/litmuskt/litmusktJvmTestsuite/1.0/")) + into(jcsDir.dir("libs/org/jetbrains/litmuskt/litmusktJvmTestsuite/1.0/")) + doFirst { + if (inputs.sourceFiles.isEmpty) throw GradleException("missing files to copy") + } } diff --git a/jcstress/pom.xml b/jcstress/pom.xml index 316b3e8..bbc22ef 100644 --- a/jcstress/pom.xml +++ b/jcstress/pom.xml @@ -98,7 +98,7 @@ THE POSSIBILITY OF SUCH DAMAGE. - 1.8 + 17