From f5251e31af54e080e0f48146aa0a9657b4d93f6a Mon Sep 17 00:00:00 2001 From: krock21rus Date: Mon, 21 Dec 2020 14:55:58 +0300 Subject: [PATCH 01/72] Just rename java files to kt --- .../kotlinx/lincheck/annotations/{LogLevel.java => LogLevel.kt} | 0 .../lincheck/annotations/{OpGroupConfig.java => OpGroupConfig.kt} | 0 .../kotlinx/lincheck/annotations/{Operation.java => Operation.kt} | 0 .../kotlinx/lincheck/annotations/{Param.java => Param.kt} | 0 .../execution/{ExecutionGenerator.java => ExecutionGenerator.kt} | 0 .../execution/{ExecutionScenario.java => ExecutionScenario.kt} | 0 ...{RandomExecutionGenerator.java => RandomExecutionGenerator.kt} | 0 .../kotlinx/lincheck/paramgen/{ByteGen.java => ByteGen.kt} | 0 .../kotlinx/lincheck/paramgen/{DoubleGen.java => DoubleGen.kt} | 0 .../kotlinx/lincheck/paramgen/{FloatGen.java => FloatGen.kt} | 0 .../kotlinx/lincheck/paramgen/{IntGen.java => IntGen.kt} | 0 .../kotlinx/lincheck/paramgen/{LongGen.java => LongGen.kt} | 0 .../paramgen/{ParameterGenerator.java => ParameterGenerator.kt} | 0 .../kotlinx/lincheck/paramgen/{ShortGen.java => ShortGen.kt} | 0 .../kotlinx/lincheck/paramgen/{StringGen.java => StringGen.kt} | 0 .../lincheck/strategy/stress/{StressCTest.java => StressCTest.kt} | 0 .../lincheck/verifier/{CachedVerifier.java => CachedVerifier.kt} | 0 ...quentialSpecification.java => DummySequentialSpecification.kt} | 0 .../verifier/{EpsilonVerifier.java => EpsilonVerifier.kt} | 0 .../kotlinx/lincheck/verifier/{Verifier.java => Verifier.kt} | 0 20 files changed, 0 insertions(+), 0 deletions(-) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/{LogLevel.java => LogLevel.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/{OpGroupConfig.java => OpGroupConfig.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/{Operation.java => Operation.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/{Param.java => Param.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/{ExecutionGenerator.java => ExecutionGenerator.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/{ExecutionScenario.java => ExecutionScenario.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/{RandomExecutionGenerator.java => RandomExecutionGenerator.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{ByteGen.java => ByteGen.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{DoubleGen.java => DoubleGen.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{FloatGen.java => FloatGen.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{IntGen.java => IntGen.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{LongGen.java => LongGen.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{ParameterGenerator.java => ParameterGenerator.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{ShortGen.java => ShortGen.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/{StringGen.java => StringGen.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/{StressCTest.java => StressCTest.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/{CachedVerifier.java => CachedVerifier.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/{DummySequentialSpecification.java => DummySequentialSpecification.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/{EpsilonVerifier.java => EpsilonVerifier.kt} (100%) rename src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/{Verifier.java => Verifier.kt} (100%) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt From 07a056d437446a96f3a9d621118aaaf767cb2484 Mon Sep 17 00:00:00 2001 From: krock21rus Date: Mon, 21 Dec 2020 14:56:34 +0300 Subject: [PATCH 02/72] Apply java -> kt transformations --- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 32 ++- .../kotlinx/lincheck/annotations/LogLevel.kt | 62 +++--- .../lincheck/annotations/OpGroupConfig.kt | 91 ++++----- .../kotlinx/lincheck/annotations/Operation.kt | 172 ++++++++-------- .../kotlinx/lincheck/annotations/Param.kt | 65 +++--- .../lincheck/execution/ActorGenerator.kt | 1 + .../lincheck/execution/ExecutionGenerator.kt | 71 ++++--- .../lincheck/execution/ExecutionScenario.kt | 99 ++++------ .../execution/RandomExecutionGenerator.kt | 187 ++++++++---------- .../kotlinx/lincheck/paramgen/ByteGen.kt | 22 +-- .../kotlinx/lincheck/paramgen/DoubleGen.kt | 119 ++++++----- .../kotlinx/lincheck/paramgen/FloatGen.kt | 19 +- .../kotlinx/lincheck/paramgen/IntGen.kt | 98 +++++---- .../kotlinx/lincheck/paramgen/LongGen.kt | 21 +- .../lincheck/paramgen/ParameterGenerator.kt | 26 ++- .../kotlinx/lincheck/paramgen/ShortGen.kt | 22 +-- .../kotlinx/lincheck/paramgen/StringGen.kt | 92 +++++---- .../lincheck/runner/ParallelThreadsRunner.kt | 2 +- .../runner/TestThreadExecutionGenerator.java | 5 +- .../lincheck/strategy/stress/StressCTest.kt | 155 +++++++-------- .../lincheck/verifier/EpsilonVerifier.kt | 20 +- .../verifier/SerializabilityVerifier.kt | 2 +- .../kotlinx/lincheck/verifier/Verifier.kt | 84 +++++--- .../quiescent/QuiescentConsistencyVerifier.kt | 3 +- .../verifier/linearizability/ClocksTest.kt | 4 +- 25 files changed, 699 insertions(+), 775 deletions(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index d1e5a2b76..44d4b549a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -89,24 +89,42 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { // Thus, the algorithm works in the linear time of the total number of actors. private fun LincheckFailure.minimize(testCfg: CTestConfiguration, verifier: Verifier): LincheckFailure { reporter.logScenarioMinimization(scenario) + val parallelExecution = scenario.parallelExecution.map { it.toMutableList() }.toMutableList() + val initExecution = scenario.initExecution.toMutableList() + val postExecution = scenario.postExecution.toMutableList() for (i in scenario.parallelExecution.indices) { for (j in scenario.parallelExecution[i].indices) { - val newScenario = scenario.copy() - newScenario.parallelExecution[i].removeAt(j) - if (newScenario.parallelExecution[i].isEmpty()) newScenario.parallelExecution.removeAt(i) // remove empty thread + val newParallelExecution = parallelExecution.map { it.toMutableList() }.toMutableList() + newParallelExecution[i].removeAt(j) + if (newParallelExecution[i].isEmpty()) newParallelExecution.removeAt(i) // remove empty thread + val newScenario = ExecutionScenario( + initExecution, + newParallelExecution, + postExecution + ) val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) } } for (i in scenario.initExecution.indices) { - val newScenario = scenario.copy() - newScenario.initExecution.removeAt(i) + val newInitExecution = initExecution.toMutableList() + newInitExecution.removeAt(i) + val newScenario = ExecutionScenario( + newInitExecution, + parallelExecution, + postExecution + ) val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) } for (i in scenario.postExecution.indices) { - val newScenario = scenario.copy() - newScenario.postExecution.removeAt(i) + val newPostExecution = postExecution.toMutableList() + newPostExecution.removeAt(i) + val newScenario = ExecutionScenario( + initExecution, + parallelExecution, + newPostExecution + ) val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt index abe3cd2b8..0d156a3e7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt @@ -1,42 +1,34 @@ -package org.jetbrains.kotlinx.lincheck.annotations; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.LoggingLevel; +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.annotations -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import org.jetbrains.kotlinx.lincheck.LoggingLevel +import java.lang.annotation.Inherited /** * This annotation should be added to a test class to specify the logging level. - * By default, {@link LoggingLevel#ERROR} is used. + * By default, [LoggingLevel.ERROR] is used. */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited -public @interface LogLevel { - LoggingLevel value(); -} +annotation class LogLevel(val value: LoggingLevel) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt index d448a76e7..451f0b290 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt @@ -1,63 +1,54 @@ -package org.jetbrains.kotlinx.lincheck.annotations; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.annotations -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.Inherited +import java.lang.annotation.Repeatable /** * Set some restrictions to the group with the specified name, * used during the scenario generation phase. */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Repeatable(OpGroupConfig.OpGroupConfigs.class) +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Repeatable(OpGroupConfig.OpGroupConfigs::class) @Inherited -public @interface OpGroupConfig { - /** - * Name of this group used by {@link Operation#group()}. - */ - String name() default ""; - - /** - * Set it to {@code true} for executing all actors in this group - * from one thread. This restriction allows to test single-reader - * and/or single-writer data structures and similar solutions. - */ - boolean nonParallel() default false; - +annotation class OpGroupConfig( + /** + * Name of this group used by [Operation.group]. + */ + val name: String = "", + /** + * Set it to `true` for executing all actors in this group + * from one thread. This restriction allows to test single-reader + * and/or single-writer data structures and similar solutions. + */ + val nonParallel: Boolean = false) { /** - * Holder annotation for {@link OpGroupConfig}. + * Holder annotation for [OpGroupConfig]. * Not a public API. */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) + @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited - @interface OpGroupConfigs { - OpGroupConfig[] value(); - } + annotation class OpGroupConfigs(vararg val value: OpGroupConfig) } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt index d2bbec600..a08bec781 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt @@ -1,98 +1,88 @@ -package org.jetbrains.kotlinx.lincheck.annotations; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.annotations -import kotlinx.coroutines.*; -import java.lang.annotation.*; +import kotlinx.coroutines.CancellableContinuation +import kotlin.reflect.KClass /** * Mark your method with this annotation in order * to use it in concurrent testing as an operation. */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Operation { - /** - * Binds the arguments of this operation with the specified {@link Param parameter configurations} - * by their {@link Param#name()} names. - */ - String[] params() default {}; - - /** - * Set it to {@code true} if you this operation should be called - * at most once during the test invocation; {@code false} by default. - */ - boolean runOnce() default false; - - /** - * Specifies the operation group which can add some execution restriction. - * @see OpGroupConfig#name() - */ - String group() default ""; - - /** - * Handle the specified exceptions as a result of this operation invocation. - */ - Class[] handleExceptionsAsResult() default {}; - - /** - * Specifies whether the operation can be cancelled if it suspends, - * see {@link CancellableContinuation#cancel}; {@code true} by default. - */ - boolean cancellableOnSuspension() default true; - - /** - * The operation marked with [allowExtraSuspension] is allowed to - * suspend (and, therefore, be cancelled if [cancellableOnSuspension] - * is set to `true`) even if it should not according to the sequential - * specification. The one may consider this as a relaxation of the - * dual data structures formalism. - */ - boolean allowExtraSuspension() default false; - - /** - * Specifies whether this operation is blocking. - * This way, if the test checks for a non-blocking progress guarantee, - * lincheck will not fail the test if a hang is detected on - * a running operations with this {@code blocking} marker. - */ - boolean blocking() default false; - - /** - * Specifies whether this operation invocation can lead - * to a blocking behavior of another concurrent operation. - * This way, if the test checks for a non-blocking progress guarantee, - * lincheck will not fail the test if a hang is detected - * while one of the operations marked with {@link #causesBlocking} - * is running concurrently. Note, that this operation is not - * considered as blocking until it is marked as {@link #blocking}. - */ - boolean causesBlocking() default false; - - /** - * Specifies whether this cancellable operation supports - * prompt cancellation, {@code false} by default. This parameter - * is ignored if {@link #cancellableOnSuspension} is {@code false}. - */ - boolean promptCancellation() default false; -} +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +annotation class Operation( + /** + * Binds the arguments of this operation with the specified [parameter configurations][Param] + * by their [Param.name] names. + */ + val params: Array = [], + /** + * Set it to `true` if you this operation should be called + * at most once during the test invocation; `false` by default. + */ + val runOnce: Boolean = false, + /** + * Specifies the operation group which can add some execution restriction. + * @see OpGroupConfig.name + */ + val group: String = "", + /** + * Handle the specified exceptions as a result of this operation invocation. + */ + val handleExceptionsAsResult: Array> = [], + /** + * Specifies whether the operation can be cancelled if it suspends, + * see [CancellableContinuation.cancel]; `true` by default. + */ + val cancellableOnSuspension: Boolean = true, + /** + * The operation marked with [allowExtraSuspension] is allowed to + * suspend (and, therefore, be cancelled if [cancellableOnSuspension] + * is set to `true`) even if it should not according to the sequential + * specification. The one may consider this as a relaxation of the + * dual data structures formalism. + */ + val allowExtraSuspension: Boolean = false, + /** + * Specifies whether this operation is blocking. + * This way, if the test checks for a non-blocking progress guarantee, + * **lincheck** will not fail the test if a hang is detected on + * a running operations with this `blocking` marker. + */ + val blocking: Boolean = false, + /** + * Specifies whether this operation invocation can lead + * to a blocking behavior of another concurrent operation. + * This way, if the test checks for a non-blocking progress guarantee, + * **lincheck** will not fail the test if a hang is detected + * while one of the operations marked with [causesBlocking] + * is running concurrently. Note, that this operation is not + * considered as blocking until it is marked as [blocking]. + */ + val causesBlocking: Boolean = false, + /** + * Specifies whether this cancellable operation supports + * prompt cancellation, `false` by default. This parameter + * is ignored if [cancellableOnSuspension] is `false`. + */ + val promptCancellation: Boolean = false) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt index c5c594267..9ef7a04e2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt @@ -1,70 +1,61 @@ -package org.jetbrains.kotlinx.lincheck.annotations; - /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ -import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator; +package org.jetbrains.kotlinx.lincheck.annotations -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator +import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator.Dummy +import java.lang.annotation.Inherited +import java.lang.annotation.Repeatable +import kotlin.reflect.KClass /** * Use this annotation to specify parameter generators. * @see ParameterGenerator */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.PARAMETER, ElementType.TYPE}) -@Repeatable(Param.Params.class) +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Repeatable(Param.Params::class) @Inherited -public @interface Param { +annotation class Param( /** - * If the annotation is set on a class, creates a {@link ParameterGenerator parameter generator} - * which can be used in {@link Operation operations} by this name. If is set on an operation, + * If the annotation is set on a class, creates a [parameter generator][ParameterGenerator] + * which can be used in [operations][Operation] by this name. If is set on an operation, * uses the specified named parameter generator which is created as described before. */ - String name() default ""; - + val name: String = "", /** - * Specifies the {@link ParameterGenerator} class which should be used for this parameter. + * Specifies the [ParameterGenerator] class which should be used for this parameter. */ - Class> gen() default ParameterGenerator.Dummy.class; - + val gen: KClass> = Dummy::class, /** - * Specifies the configuration for the {@link #gen() parameter generator}. + * Specifies the configuration for the [parameter generator][.gen]. */ - String conf() default ""; - + val conf: String = "" +) { /** - * Holder annotation for {@link Param}. + * Holder annotation for [Param]. * Not a public API. */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) + @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited - @interface Params { - Param[] value(); - } + annotation class Params(vararg val value: Param) } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index 3d9dc090c..f03dda4ab 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.paramgen.* import java.lang.reflect.* import kotlin.random.* +import kotlin.reflect.KClass /** * Implementations of this class generate [actors][Actor] diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt index 06dd07f89..1fded5302 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt @@ -1,46 +1,41 @@ -package org.jetbrains.kotlinx.lincheck.execution; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.execution -import org.jetbrains.kotlinx.lincheck.CTestConfiguration; -import org.jetbrains.kotlinx.lincheck.CTestStructure; +import org.jetbrains.kotlinx.lincheck.CTestConfiguration +import org.jetbrains.kotlinx.lincheck.CTestStructure /** * Implementation of this interface generates execution scenarios. - * By default, {@link RandomExecutionGenerator} is used. - *

+ * By default, [RandomExecutionGenerator] is used. + * + * * IMPORTANT! - * All implementations should have the same constructor as {@link ExecutionGenerator} has. + * All implementations should have the same constructor as [ExecutionGenerator] has. */ -public abstract class ExecutionGenerator { - protected final CTestConfiguration testConfiguration; - protected final CTestStructure testStructure; - - protected ExecutionGenerator(CTestConfiguration testConfiguration, CTestStructure testStructure) { - this.testConfiguration = testConfiguration; - this.testStructure = testStructure; - } - +abstract class ExecutionGenerator( + protected val testConfiguration: CTestConfiguration, + protected val testStructure: CTestStructure +) { /** * Generates an execution scenario according to the parameters provided by the test configuration * and the restrictions from the test structure. @@ -48,5 +43,5 @@ public abstract class ExecutionGenerator { * If the current test contains suspendable operations, the initial part of an execution * should not contain suspendable actors and the post part should be empty. */ - public abstract ExecutionScenario nextExecution(); -} + abstract fun nextExecution(): ExecutionScenario +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt index 7b5e02c79..132c13f28 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt @@ -1,9 +1,8 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,72 +15,58 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ -package org.jetbrains.kotlinx.lincheck.execution; -import org.jetbrains.kotlinx.lincheck.Actor; -import org.jetbrains.kotlinx.lincheck.strategy.Strategy; +package org.jetbrains.kotlinx.lincheck.execution -import java.util.*; -import java.util.stream.*; - -import static org.jetbrains.kotlinx.lincheck.ReporterKt.appendExecutionScenario; +import org.jetbrains.kotlinx.lincheck.Actor +import org.jetbrains.kotlinx.lincheck.appendExecutionScenario +import org.jetbrains.kotlinx.lincheck.strategy.Strategy /** * This class represents an execution scenario, which - * is generated by an {@link ExecutionGenerator} and then \ - * used by a {@link Strategy} which produces an {@link ExecutionResult}. + * is generated by an [ExecutionGenerator] and then + * used by a [Strategy] which produces an [ExecutionResult]. */ -public class ExecutionScenario { - /** - * The initial sequential part of the execution. - * It helps to produce different initial states - * before the parallel part. - * - * The initial execution part should contain only non-suspendable actors; - * otherwise, the single initial execution thread will suspend with no chance to be resumed. - */ - public final List initExecution; - /** - * The parallel part of the execution, which is used - * to find an interleaving with incorrect behaviour. - */ - public final List> parallelExecution; - /** - * The last sequential part is used to test that - * the data structure is in some correct state. - * - * If this execution scenario contains suspendable actors, the post part should be empty; - * if not, an actor could resume a previously suspended one from the parallel execution part. - */ - public final List postExecution; - - public ExecutionScenario(List initExecution, List> parallelExecution, List postExecution) { - this.initExecution = initExecution; - this.parallelExecution = parallelExecution; - this.postExecution = postExecution; - } - +class ExecutionScenario( + /** + * The initial sequential part of the execution. + * It helps to produce different initial states + * before the parallel part. + * + * The initial execution part should contain only non-suspendable actors; + * otherwise, the single initial execution thread will suspend with no chance to be resumed. + */ + val initExecution: List, + /** + * The parallel part of the execution, which is used + * to find an interleaving with incorrect behaviour. + */ + val parallelExecution: List>, + /** + * The last sequential part is used to test that + * the data structure is in some correct state. + * + * If this execution scenario contains suspendable actors, the post part should be empty; + * if not, an actor could resume a previously suspended one from the parallel execution part. + */ + val postExecution: List +) { /** * Returns the number of threads used in the parallel part of this execution. */ - public int getThreads() { - return parallelExecution.size(); - } + val threads: Int + get() = parallelExecution.size /** * Returns `true` if there is at least one suspendable actor in the generated scenario */ - public boolean hasSuspendableActors() { - return Stream.concat(parallelExecution.stream().flatMap(Collection::stream), postExecution.stream()).anyMatch(Actor::isSuspendable); - } + fun hasSuspendableActors() = parallelExecution.any { actors -> actors.any { it.isSuspendable } } || postExecution.any { it.isSuspendable } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - appendExecutionScenario(sb, this); - return sb.toString(); + override fun toString(): String { + val sb = StringBuilder() + sb.appendExecutionScenario(this) + return sb.toString() } -} +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt index 2cd8a460c..bc2f9e70f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt @@ -1,129 +1,108 @@ -package org.jetbrains.kotlinx.lincheck.execution; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.Actor; -import org.jetbrains.kotlinx.lincheck.CTestConfiguration; -import org.jetbrains.kotlinx.lincheck.CTestStructure; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Random; -import java.util.stream.Collectors; +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.execution -public class RandomExecutionGenerator extends ExecutionGenerator { - private final Random random = new Random(0); - - public RandomExecutionGenerator(CTestConfiguration testConfiguration, CTestStructure testStructure) { - super(testConfiguration, testStructure); - } +import org.jetbrains.kotlinx.lincheck.Actor +import org.jetbrains.kotlinx.lincheck.CTestConfiguration +import org.jetbrains.kotlinx.lincheck.CTestStructure +import org.jetbrains.kotlinx.lincheck.CTestStructure.OperationGroup +import kotlin.random.Random - @Override - public ExecutionScenario nextExecution() { +class RandomExecutionGenerator(testConfiguration: CTestConfiguration, testStructure: CTestStructure) : ExecutionGenerator(testConfiguration, testStructure) { + private val random = Random(0) + override fun nextExecution(): ExecutionScenario { // Create init execution part - List validActorGeneratorsForInit = testStructure.actorGenerators.stream() - .filter(ag -> !ag.getUseOnce() && !ag.isSuspendable()).collect(Collectors.toList()); - List initExecution = new ArrayList<>(); - for (int i = 0; i < testConfiguration.getActorsBefore() && !validActorGeneratorsForInit.isEmpty(); i++) { - ActorGenerator ag = validActorGeneratorsForInit.get(random.nextInt(validActorGeneratorsForInit.size())); - initExecution.add(ag.generate(0)); + val validActorGeneratorsForInit = testStructure.actorGenerators.filter { ag: ActorGenerator -> !ag.useOnce && !ag.isSuspendable } + val initExecution: MutableList = ArrayList() + run { + var i = 0 + while (i < testConfiguration.actorsBefore && validActorGeneratorsForInit.isNotEmpty()) { + val ag = validActorGeneratorsForInit[random.nextInt(validActorGeneratorsForInit.size)] + initExecution.add(ag.generate(0)) + i++ + } } // Create parallel execution part // Construct non-parallel groups and parallel one - List nonParallelGroups = testStructure.operationGroups.stream() - .filter(g -> g.nonParallel) - .collect(Collectors.toList()); - Collections.shuffle(nonParallelGroups); - List parallelGroup = new ArrayList<>(testStructure.actorGenerators); - nonParallelGroups.forEach(g -> parallelGroup.removeAll(g.actors)); - - List> parallelExecution = new ArrayList<>(); - List threadGens = new ArrayList<>(); - for (int t = 0; t < testConfiguration.getThreads(); t++) { - parallelExecution.add(new ArrayList<>()); - threadGens.add(new ThreadGen(t, testConfiguration.getActorsPerThread())); + val nonParallelGroups = testStructure.operationGroups.filter { g: OperationGroup -> g.nonParallel }.shuffled() + val parallelGroup: MutableList = ArrayList(testStructure.actorGenerators) + nonParallelGroups.forEach { g: OperationGroup -> parallelGroup.removeAll(g.actors) } + val parallelExecution: MutableList> = ArrayList() + val threadGens: MutableList = ArrayList() + for (t in 0 until testConfiguration.threads) { + parallelExecution.add(ArrayList()) + threadGens.add(ThreadGen(t, testConfiguration.actorsPerThread)) } - for (int i = 0; i < nonParallelGroups.size(); i++) { - threadGens.get(i % threadGens.size()).nonParallelActorGenerators - .addAll(nonParallelGroups.get(i).actors); + for (i in nonParallelGroups.indices) { + threadGens[i % threadGens.size].nonParallelActorGenerators + .addAll(nonParallelGroups[i]!!.actors) } - List tgs2 = new ArrayList<>(threadGens); - while (!threadGens.isEmpty()) { - for (Iterator it = threadGens.iterator(); it.hasNext(); ) { - ThreadGen threadGen = it.next(); - int aGenIndexBound = threadGen.nonParallelActorGenerators.size() + parallelGroup.size(); + val tgs2: List = ArrayList(threadGens) + while (threadGens.isNotEmpty()) { + val it = threadGens.iterator() + while (it.hasNext()) { + val threadGen = it.next() + val aGenIndexBound = threadGen.nonParallelActorGenerators.size + parallelGroup.size if (aGenIndexBound == 0) { - it.remove(); - continue; + it.remove() + continue } - int aGenIndex = random.nextInt(aGenIndexBound); - ActorGenerator agen; - if (aGenIndex < threadGen.nonParallelActorGenerators.size()) { - agen = getActorGenFromGroup(threadGen.nonParallelActorGenerators, aGenIndex); + val aGenIndex = random.nextInt(aGenIndexBound) + val agen: ActorGenerator = if (aGenIndex < threadGen.nonParallelActorGenerators.size) { + getActorGenFromGroup(threadGen.nonParallelActorGenerators, aGenIndex) } else { - agen = getActorGenFromGroup(parallelGroup, - aGenIndex - threadGen.nonParallelActorGenerators.size()); + getActorGenFromGroup(parallelGroup, + aGenIndex - threadGen.nonParallelActorGenerators.size) } - parallelExecution.get(threadGen.iThread).add(agen.generate(threadGen.iThread + 1)); - if (--threadGen.left == 0) - it.remove(); + parallelExecution[threadGen.iThread].add(agen.generate(threadGen.iThread + 1)) + if (--threadGen.left == 0) it.remove() } } - parallelExecution = parallelExecution.stream().filter(actors -> !actors.isEmpty()).collect(Collectors.toList()); + parallelExecution.retainAll { actors: List -> actors.isNotEmpty() } // Create post execution part if the parallel part does not have suspendable actors - List postExecution; - if (parallelExecution.stream().noneMatch(actors -> actors.stream().anyMatch(Actor::isSuspendable))) { - postExecution = new ArrayList<>(); - List leftActorGenerators = new ArrayList<>(parallelGroup); - for (ThreadGen threadGen : tgs2) - leftActorGenerators.addAll(threadGen.nonParallelActorGenerators); - for (int i = 0; i < testConfiguration.getActorsAfter() && !leftActorGenerators.isEmpty(); i++) { - ActorGenerator agen = getActorGenFromGroup(leftActorGenerators, random.nextInt(leftActorGenerators.size())); - postExecution.add(agen.generate(testConfiguration.getThreads() + 1)); + val postExecution: MutableList + if (parallelExecution.none { actors: List -> actors.any(Actor::isSuspendable) }) { + postExecution = ArrayList() + val leftActorGenerators: MutableList = ArrayList(parallelGroup) + for (threadGen in tgs2) leftActorGenerators.addAll(threadGen.nonParallelActorGenerators) + var i = 0 + while (i < testConfiguration.actorsAfter && leftActorGenerators.isNotEmpty()) { + val agen = getActorGenFromGroup(leftActorGenerators, random.nextInt(leftActorGenerators.size)) + postExecution.add(agen.generate(testConfiguration.threads + 1)) + i++ } } else { - postExecution = Collections.emptyList(); + postExecution = arrayListOf() } - return new ExecutionScenario(initExecution, parallelExecution, postExecution); + return ExecutionScenario(initExecution, parallelExecution, postExecution) } - private ActorGenerator getActorGenFromGroup(List aGens, int index) { - ActorGenerator aGen = aGens.get(index); - if (aGen.getUseOnce()) - aGens.remove(index); - return aGen; + private fun getActorGenFromGroup(aGens: List, index: Int): ActorGenerator { + val aGen = aGens[index] + if (aGen.useOnce) (aGens as MutableList).removeAt(index) + return aGen } - private static class ThreadGen { - final List nonParallelActorGenerators = new ArrayList<>(); - int iThread; - int left; - - ThreadGen(int iThread, int nActors) { - this.iThread = iThread; - this.left = nActors; - } + private class ThreadGen(var iThread: Int, var left: Int) { + val nonParallelActorGenerators: MutableList = ArrayList() } } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt index 9b887c8d1..dba8bd6d0 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; - /* * #%L * Lincheck @@ -10,27 +8,27 @@ package org.jetbrains.kotlinx.lincheck.paramgen; * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +package org.jetbrains.kotlinx.lincheck.paramgen -public class ByteGen implements ParameterGenerator { - private final IntGen intGen; +class ByteGen(configuration: String) : ParameterGenerator { + private val intGen: IntGen = IntGen(configuration) - public ByteGen(String configuration) { - intGen = new IntGen(configuration); - intGen.checkRange(Byte.MIN_VALUE, Byte.MAX_VALUE, "byte"); + init { + intGen.checkRange(Byte.MIN_VALUE.toInt(), Byte.MAX_VALUE.toInt(), "byte") } - public Byte generate() { - return (byte) (int) intGen.generate(); + override fun generate(): Byte { + return intGen.generate().toByte() } -} +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt index 1eab85f4d..bb978a87d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt @@ -1,71 +1,68 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Random; +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.paramgen -public class DoubleGen implements ParameterGenerator { - private static final float DEFAULT_BEGIN = -10; - private static final float DEFAULT_END = 10; - private static final float DEFAULT_STEP = 0.1f; +import kotlin.random.Random - private final Random random = new Random(0); - private final double begin; - private final double end; - private final double step; +class DoubleGen(configuration: String) : ParameterGenerator { + private val random = Random(0) + private var begin = 0.0 + private var end = 0.0 + private var step = 0.0 - public DoubleGen(String configuration) { + init { if (configuration.isEmpty()) { // use default configuration - begin = DEFAULT_BEGIN; - end = DEFAULT_END; - step = DEFAULT_STEP; - return; + begin = DEFAULT_BEGIN.toDouble() + end = DEFAULT_END.toDouble() + step = DEFAULT_STEP.toDouble() + } else { + val args = configuration.replace("\\s".toRegex(), "").split(":".toRegex()).toTypedArray() + when (args.size) { + 2 -> { + begin = args[0].toDouble() + end = args[1].toDouble() + step = DEFAULT_STEP.toDouble() + } + 3 -> { + begin = args[0].toDouble() + step = args[1].toDouble() + end = args[2].toDouble() + } + else -> throw IllegalArgumentException("Configuration should have two (begin and end) " + + "or three (begin, step and end) arguments separated by colon") + } + require((end - begin) / step < Int.MAX_VALUE) { "step is too small for specified range" } } - String[] args = configuration.replaceAll("\\s", "").split(":"); - switch (args.length) { - case 2: // begin:end - begin = Double.parseDouble(args[0]); - end = Double.parseDouble(args[1]); - step = DEFAULT_STEP; - break; - case 3: // begin:step:end - begin = Double.parseDouble(args[0]); - step = Double.parseDouble(args[1]); - end = Double.parseDouble(args[2]); - break; - default: - throw new IllegalArgumentException("Configuration should have two (begin and end) " + - "or three (begin, step and end) arguments separated by colon"); - } - if ((end - begin) / step >= Integer.MAX_VALUE) - throw new IllegalArgumentException("step is too small for specified range"); } - public Double generate() { - double delta = end - begin; - if (step == 0) // step is not defined - return begin + delta * random.nextDouble(); - int maxSteps = (int) (delta / step); - return begin + delta * random.nextInt(maxSteps + 1); + override fun generate(): Double { + val delta = end - begin + if (step == 0.0) // step is not defined + return begin + delta * random.nextDouble() + val maxSteps = (delta / step).toInt() + return begin + delta * random.nextInt(maxSteps + 1) } } + +private const val DEFAULT_BEGIN = -10f +private const val DEFAULT_END = 10f +private const val DEFAULT_STEP = 0.1f \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt index 6d2417ea9..83a23b7e2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; - /* * #%L * Lincheck @@ -10,26 +8,23 @@ package org.jetbrains.kotlinx.lincheck.paramgen; * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +package org.jetbrains.kotlinx.lincheck.paramgen -public class FloatGen implements ParameterGenerator { - private final DoubleGen doubleGen; - - public FloatGen(String configuration) { - doubleGen = new DoubleGen(configuration); - } +class FloatGen(configuration: String) : ParameterGenerator { + private val doubleGen: DoubleGen = DoubleGen(configuration) - public Float generate() { - return (float) (double) doubleGen.generate(); + override fun generate(): Float { + return doubleGen.generate().toFloat() } } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt index 59a3c7f1a..15a4f9260 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt @@ -1,63 +1,61 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Random; +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.paramgen -public class IntGen implements ParameterGenerator { - private static final int DEFAULT_BEGIN = -10; - private static final int DEFAULT_END = 10; +import kotlin.random.Random - private final Random random = new Random(0); - private final int begin; - private final int end; +class IntGen(configuration: String) : ParameterGenerator { + private val random = Random(0) + private var begin = 0 + private var end = 0 - public IntGen(String configuration) { + init { if (configuration.isEmpty()) { // use default configuration - begin = DEFAULT_BEGIN; - end = DEFAULT_END; - return; - } - String[] args = configuration.replaceAll("\\s", "").split(":"); - switch (args.length) { - case 2: // begin:end - begin = Integer.parseInt(args[0]); - end = Integer.parseInt(args[1]); - break; - default: - throw new IllegalArgumentException("Configuration should have " + - "two arguments (begin and end) separated by colon"); + begin = DEFAULT_BEGIN + end = DEFAULT_END + } else { + val args = configuration.replace("\\s".toRegex(), "").split(":".toRegex()).toTypedArray() + when (args.size) { + 2 -> { + begin = args[0].toInt() + end = args[1].toInt() + } + else -> throw IllegalArgumentException("Configuration should have " + + "two arguments (begin and end) separated by colon") + } } } - public Integer generate() { - return begin + random.nextInt(end - begin + 1); + override fun generate(): Int { + return begin + random.nextInt(end - begin + 1) } - void checkRange(int min, int max, String type) { - if (this.begin < min || this.end - 1 > max) { - throw new IllegalArgumentException("Illegal range for " - + type + " type: [" + begin + "; " + end + ")"); + fun checkRange(min: Int, max: Int, type: String) { + require(!(begin < min || end - 1 > max)) { + ("Illegal range for " + + type + " type: [" + begin + "; " + end + ")") } } } + +private const val DEFAULT_BEGIN = -10 +private const val DEFAULT_END = 10 \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt index fa9e71b09..824ace562 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; - /* * #%L * Lincheck @@ -10,26 +8,23 @@ package org.jetbrains.kotlinx.lincheck.paramgen; * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +package org.jetbrains.kotlinx.lincheck.paramgen -public class LongGen implements ParameterGenerator { - private final IntGen intGen; - - public LongGen(String configuration) { - intGen = new IntGen(configuration); - } +class LongGen(configuration: String) : ParameterGenerator { + private val intGen: IntGen = IntGen(configuration) - public Long generate() { - return (long) intGen.generate(); + override fun generate(): Long { + return intGen.generate().toLong() } -} +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt index 4c6b9530a..754f55411 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; - /* * #%L * Lincheck @@ -10,31 +8,31 @@ package org.jetbrains.kotlinx.lincheck.paramgen; * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +package org.jetbrains.kotlinx.lincheck.paramgen -import org.jetbrains.kotlinx.lincheck.annotations.Operation; +import org.jetbrains.kotlinx.lincheck.annotations.Operation /** * The implementation of this interface is used to generate parameters - * for {@link Operation operation}. - */ -public interface ParameterGenerator { - T generate(); + * for [operation][Operation]. + */ +interface ParameterGenerator { + fun generate(): T - final class Dummy implements ParameterGenerator { - @Override - public Object generate() { - throw new UnsupportedOperationException(); + class Dummy : ParameterGenerator { + override fun generate(): Any { + throw UnsupportedOperationException() } } -} +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt index bad387bfe..b75a7f091 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt @@ -1,4 +1,4 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; +package org.jetbrains.kotlinx.lincheck.paramgen /* * #%L @@ -10,27 +10,25 @@ package org.jetbrains.kotlinx.lincheck.paramgen; * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ +class ShortGen(configuration: String) : ParameterGenerator { + private val intGen: IntGen = IntGen(configuration) -public class ShortGen implements ParameterGenerator { - private final IntGen intGen; - - public ShortGen(String configuration) { - intGen = new IntGen(configuration); - intGen.checkRange(Short.MIN_VALUE, Short.MAX_VALUE, "short"); + init { + intGen.checkRange(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt(), "short") } - public Short generate() { - return (short) (int) intGen.generate(); + override fun generate(): Short { + return intGen.generate().toShort() } -} +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt index 292b7ce26..92840c27d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt @@ -1,57 +1,55 @@ -package org.jetbrains.kotlinx.lincheck.paramgen; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import java.util.Random; +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.paramgen -public class StringGen implements ParameterGenerator { - private static final int DEFAULT_MAX_WORD_LENGTH = 15; - private static final String DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ "; +import java.util.* - private final Random random = new Random(0); - private final int maxWordLength; - private final String alphabet; +class StringGen(configuration: String) : ParameterGenerator { + private val random = Random(0) + private var maxWordLength = 0 + private var alphabet: String? = null - public StringGen(String configuration) { + init { if (configuration.isEmpty()) { // use default configuration - maxWordLength = DEFAULT_MAX_WORD_LENGTH; - alphabet = DEFAULT_ALPHABET; - return; - } - int firstCommaIndex = configuration.indexOf(':'); - if (firstCommaIndex < 0) { // maxWordLength only - maxWordLength = Integer.parseInt(configuration); - alphabet = DEFAULT_ALPHABET; - } else { // maxWordLength:alphabet - maxWordLength = Integer.parseInt(configuration.substring(0, firstCommaIndex)); - alphabet = configuration.substring(firstCommaIndex + 1); + maxWordLength = DEFAULT_MAX_WORD_LENGTH + alphabet = DEFAULT_ALPHABET + } else { + val firstCommaIndex = configuration.indexOf(':') + if (firstCommaIndex < 0) { // maxWordLength only + maxWordLength = configuration.toInt() + alphabet = DEFAULT_ALPHABET + } else { // maxWordLength:alphabet + maxWordLength = configuration.substring(0, firstCommaIndex).toInt() + alphabet = configuration.substring(firstCommaIndex + 1) + } } } - public String generate() { - char[] cs = new char[random.nextInt(maxWordLength)]; - for (int i = 0; i < cs.length; i++) - cs[i] = alphabet.charAt(random.nextInt(alphabet.length())); - return new String(cs); + override fun generate(): String { + val cs = CharArray(random.nextInt(maxWordLength)) + for (i in cs.indices) cs[i] = alphabet!![random.nextInt(alphabet!!.length)] + return String(cs) } } + +private const val DEFAULT_MAX_WORD_LENGTH = 15 +private const val DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ " \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 0d199b9be..4cc630481 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -142,7 +142,7 @@ internal open class ParallelThreadsRunner( } private fun reset() { - testInstance = testClass.newInstance() + testInstance = testClass.getDeclaredConstructor().newInstance() testThreadExecutions.forEachIndexed { t, ex -> ex.testInstance = testInstance val threads = scenario.threads diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java index a68fd33b6..cbb746eb8 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java @@ -31,6 +31,7 @@ import org.objectweb.asm.commons.TryCatchBlockSorter; import org.objectweb.asm.util.CheckClassAdapter; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; @@ -112,11 +113,11 @@ public static TestThreadExecution create(Runner runner, int iThread, List Class clz = runner.getClassLoader().defineClass(className, generateClass(internalClassName, getType(runner.getTestClass()), iThread, actors, objArgs, completions, scenarioContainsSuspendableActors)); try { - TestThreadExecution execution = clz.newInstance(); + TestThreadExecution execution = clz.getDeclaredConstructor().newInstance(); execution.runner = runner; execution.objArgs = objArgs.toArray(); return execution; - } catch (InstantiationException | IllegalAccessException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw new IllegalStateException("Cannot initialize generated execution class", e); } } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.kt index eb0964309..595b67d3a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTest.kt @@ -1,138 +1,129 @@ -package org.jetbrains.kotlinx.lincheck.strategy.stress; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.CTestConfiguration; -import org.jetbrains.kotlinx.lincheck.annotations.Operation; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario; -import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator; -import org.jetbrains.kotlinx.lincheck.verifier.DummySequentialSpecification; -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier; -import org.jetbrains.kotlinx.lincheck.verifier.Verifier; - -import java.lang.annotation.*; +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.strategy.stress + +import org.jetbrains.kotlinx.lincheck.CTestConfiguration +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.execution.ExecutionGenerator +import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario +import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator +import org.jetbrains.kotlinx.lincheck.verifier.DummySequentialSpecification +import org.jetbrains.kotlinx.lincheck.verifier.Verifier +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier +import java.lang.annotation.Inherited +import java.lang.annotation.Repeatable +import kotlin.reflect.KClass /** * This annotation configures concurrent test using stress strategy. */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Repeatable(StressCTest.StressCTests.class) +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Repeatable(StressCTest.StressCTests::class) @Inherited -public @interface StressCTest { +annotation class StressCTest( /** * The number of different test scenarios to be executed */ - int iterations() default CTestConfiguration.DEFAULT_ITERATIONS; - + val iterations: Int = CTestConfiguration.DEFAULT_ITERATIONS, /** - * Run each test scenario {@code invocations} times. + * Run each test scenario `invocations` times. */ - int invocationsPerIteration() default StressCTestConfiguration.DEFAULT_INVOCATIONS; - + val invocationsPerIteration: Int = StressCTestConfiguration.DEFAULT_INVOCATIONS, /** * Use the specified number of threads for the parallel part of an execution. - *

+ * + * * Note, that the the actual number of threads can be less due to some restrictions - * like {@link Operation#runOnce()}. + * like [Operation.runOnce]. * - * @see ExecutionScenario#parallelExecution + * @see ExecutionScenario.parallelExecution */ - int threads() default CTestConfiguration.DEFAULT_THREADS; - + val threads: Int = CTestConfiguration.DEFAULT_THREADS, /** * Generate the specified number of operations for each thread of the parallel part of an execution. - *

+ * + * * Note, that the the actual number of operations can be less due to some restrictions - * like {@link Operation#runOnce()}. + * like [Operation.runOnce]. * - * @see ExecutionScenario#parallelExecution + * @see ExecutionScenario.parallelExecution */ - int actorsPerThread() default CTestConfiguration.DEFAULT_ACTORS_PER_THREAD; - + val actorsPerThread: Int = CTestConfiguration.DEFAULT_ACTORS_PER_THREAD, /** * Generate the specified number of operation for the initial sequential part of an execution. - *

+ * + * * Note, that the the actual number of operations can be less due to some restrictions - * like {@link Operation#runOnce()}. + * like [Operation.runOnce]. * - * @see ExecutionScenario#initExecution + * @see ExecutionScenario.initExecution */ - int actorsBefore() default CTestConfiguration.DEFAULT_ACTORS_BEFORE; - + val actorsBefore: Int = CTestConfiguration.DEFAULT_ACTORS_BEFORE, /** * Generate the specified number of operation for the last sequential part of an execution. - *

+ * + * * Note, that the the actual number of operations can be less due to some restrictions - * like {@link Operation#runOnce()}. + * like [Operation.runOnce]. * - * @see ExecutionScenario#postExecution + * @see ExecutionScenario.postExecution */ - int actorsAfter() default CTestConfiguration.DEFAULT_ACTORS_AFTER; - + val actorsAfter: Int = CTestConfiguration.DEFAULT_ACTORS_AFTER, /** * Use the specified execution generator. */ - Class generator() default RandomExecutionGenerator.class; - + val generator: KClass = RandomExecutionGenerator::class, /** * Use the specified verifier. */ - Class verifier() default LinearizabilityVerifier.class; - + val verifier: KClass = LinearizabilityVerifier::class, /** * Require correctness check of test instance state equivalency relation, which is defined by the user. * Essentially, it checks whether two new instances of the test class are equal. - * If the check fails, an {@link IllegalStateException} is thrown. + * If the check fails, an [IllegalStateException] is thrown. */ - boolean requireStateEquivalenceImplCheck() default true; - + val requireStateEquivalenceImplCheck: Boolean = true, /** * If this feature is enabled and an invalid interleaving has been found, * *lincheck* tries to minimize the corresponding scenario in order to * construct a smaller one so that the test fails on it as well. * Enabled by default. */ - boolean minimizeFailedScenario() default true; - + val minimizeFailedScenario: Boolean = true, /** * The specified class defines the sequential behavior of the testing data structure; - * it is used by {@link Verifier} to build a labeled transition system, + * it is used by [Verifier] to build a labeled transition system, * and should have the same methods as the testing data structure. * * By default, the provided concurrent implementation is used in a sequential way. */ - Class sequentialSpecification() default DummySequentialSpecification.class; - + val sequentialSpecification: KClass<*> = DummySequentialSpecification::class +) { /** - * Holder annotation for {@link StressCTest}. + * Holder annotation for [StressCTest]. */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) + @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited - @interface StressCTests { - StressCTest[] value(); - } -} - + annotation class StressCTests(vararg val value: StressCTest) +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt index 234da2f33..effe15a03 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt @@ -19,22 +19,16 @@ * . * #L% */ -package org.jetbrains.kotlinx.lincheck.verifier; +package org.jetbrains.kotlinx.lincheck.verifier -import org.jetbrains.kotlinx.lincheck.execution.*; +import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult +import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario /** * This verifier does nothing and could be used for performance benchmarking. */ -public class EpsilonVerifier implements Verifier { +class EpsilonVerifier(sequentialSpecification: Class<*>) : Verifier { + override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean = true // Always correct results :) - public EpsilonVerifier(Class sequentialSpecification) {} - - @Override - public boolean verifyResults(ExecutionScenario scenario, ExecutionResult results) { - return true; // Always correct results :) - } - - @Override - public void checkStateEquivalenceImplementation() {} -} + override fun checkStateEquivalenceImplementation() {} +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt index fd605c052..fb2f3bdf5 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* * This verifier checks that the specified results could be happen in serializable execution. * It just tries to find any operations sequence which execution produces the same results. */ -public class SerializabilityVerifier( +class SerializabilityVerifier( sequentialSpecification: Class ) : CachedVerifier() { private val linerizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt index 03146030d..5caf971aa 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt @@ -1,49 +1,69 @@ -package org.jetbrains.kotlinx.lincheck.verifier; - /* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.verifier -import org.jetbrains.kotlinx.lincheck.execution.*; -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier; +import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult +import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario +import kotlin.collections.HashSet +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier /** * Implementation of this interface verifies that execution is correct with respect to the algorithm contract. - * By default, it checks for linearizability (see {@link LinearizabilityVerifier}). - *

+ * By default, it checks for linearizability (see [LinearizabilityVerifier]). + * + * * IMPORTANT! - * All implementations should have {@code (Class sequentialSpecification)} constructor, + * All implementations should have `(Class sequentialSpecification)` constructor, * which takes the scenario to be tested and the correct sequential implementation of the testing data structure. */ -public interface Verifier { +interface Verifier { /** * Verifies the specified results for correctness. - * Returns {@code true} if results are possible, {@code false} otherwise. + * Returns `true` if results are possible, `false` otherwise. */ - boolean verifyResults(ExecutionScenario scenario, ExecutionResult results); + fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean /** * Verifiers which use sequential implementation instances as states (or parts of them) - * should check whether {@link #equals(Object)} and {@link #hashCode()} methods are implemented + * should check whether [equals] and [hashCode] methods are implemented * correctly. */ - void checkStateEquivalenceImplementation(); + fun checkStateEquivalenceImplementation() +} + +/** + * This verifier cached the already verified results in a hash table, + * and look into this hash table at first. In case of many invocations + * with the same scenario, this optimization improves the verification + * phase significantly. + */ +abstract class CachedVerifier : Verifier { + private val previousResults: MutableMap> = HashMap() + override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean { + val newResult = previousResults.computeIfAbsent(scenario) { s: ExecutionScenario -> HashSet() }.add(results) + return if (!newResult) true else verifyResultsImpl(scenario, results) + } + + abstract fun verifyResultsImpl(scenario: ExecutionScenario, results: ExecutionResult): Boolean } + +internal class DummySequentialSpecification private constructor() // This dummy class should not be created \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index b9738f483..33b679d2e 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* -import java.util.* import kotlin.collections.ArrayList /** @@ -37,7 +36,7 @@ import kotlin.collections.ArrayList */ class QuiescentConsistencyVerifier(sequentialSpecification: Class<*>) : Verifier { private val linearizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) - private val scenarioMapping: MutableMap = WeakHashMap() + private val scenarioMapping: MutableMap = HashMap() override fun checkStateEquivalenceImplementation() = linearizabilityVerifier.checkStateEquivalenceImplementation() diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt index 99579f8bc..14ee1674d 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/ClocksTest.kt @@ -67,11 +67,11 @@ class ClocksTestScenarioGenerator(testCfg: CTestConfiguration, testStructure: CT override fun nextExecution() = ExecutionScenario( emptyList(), listOf( - listOf( + listOf( Actor(method = ClocksTest::a.javaMethod!!, arguments = emptyList()), Actor(method = ClocksTest::b.javaMethod!!, arguments = emptyList()) ), - listOf( + listOf( Actor(method = ClocksTest::c.javaMethod!!, arguments = emptyList()), Actor(method = ClocksTest::d.javaMethod!!, arguments = emptyList()) ) From ec0217dce89673207c8e9fdffde78b893134d553 Mon Sep 17 00:00:00 2001 From: krock21rus Date: Mon, 21 Dec 2020 16:19:00 +0300 Subject: [PATCH 03/72] Remove CachedVerifier and DummeSequentialSpecification kt files --- .../lincheck/verifier/CachedVerifier.kt | 46 ------------------- .../verifier/DummySequentialSpecification.kt | 26 ----------- 2 files changed, 72 deletions(-) delete mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.kt delete mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.kt deleted file mode 100644 index 25c56b962..000000000 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.jetbrains.kotlinx.lincheck.verifier; - -/* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.execution.*; - -import java.util.*; - -/** - * This verifier cached the already verified results in a hash table, - * and look into this hash table at first. In case of many invocations - * with the same scenario, this optimization improves the verification - * phase significantly. - */ -public abstract class CachedVerifier implements Verifier { - private final Map> previousResults = new WeakHashMap<>(); - - @Override - public boolean verifyResults(ExecutionScenario scenario, ExecutionResult results) { - boolean newResult = previousResults.computeIfAbsent(scenario, s -> new HashSet<>()).add(results); - if (!newResult) return true; - return verifyResultsImpl(scenario, results); - } - - public abstract boolean verifyResultsImpl(ExecutionScenario scenario, ExecutionResult results); -} diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.kt deleted file mode 100644 index a14037775..000000000 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/DummySequentialSpecification.kt +++ /dev/null @@ -1,26 +0,0 @@ -/*- - * #%L - * Lincheck - * %% - * Copyright (C) 2019 JetBrains s.r.o. - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -package org.jetbrains.kotlinx.lincheck.verifier; - -public class DummySequentialSpecification { - private DummySequentialSpecification() {} // This dummy class should not be created -} From 672bff0cd87796ab03b29b648f7f43a7b845dc84 Mon Sep 17 00:00:00 2001 From: krock21rus Date: Tue, 22 Dec 2020 14:48:27 +0300 Subject: [PATCH 04/72] Move some parts to common --- build.gradle.kts | 17 ++ .../org/jetbrains/kotlinx/lincheck/Actor.kt | 60 +++++ .../kotlinx/lincheck/annotations/LogLevel.kt | 29 +++ .../lincheck/annotations/OpGroupConfig.kt | 37 +++ .../kotlinx/lincheck/annotations/Operation.kt | 0 .../kotlinx/lincheck/annotations/Param.kt | 57 ++++ .../annotations/StateRepresentation.kt | 0 .../kotlinx/lincheck/annotations/Validate.kt | 0 .../lincheck/paramgen/ParameterGenerator.kt | 10 +- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 36 +-- .../kotlinx/lincheck/CTestStructure.java | 244 ------------------ .../kotlinx/lincheck/CTestStructure.kt | 209 +++++++++++++++ .../CancellabilitySupportTransformer.kt | 2 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 2 +- .../kotlinx/lincheck/annotations/LogLevel.kt | 2 +- .../lincheck/annotations/OpGroupConfig.kt | 8 +- .../kotlinx/lincheck/annotations/Param.kt | 10 +- .../lincheck/execution/ActorGenerator.kt | 4 +- .../lincheck/runner/ParallelThreadsRunner.kt | 1 + .../kotlinx/lincheck/runner/Runner.kt | 1 + .../runner/TestThreadExecutionGenerator.java | 4 +- .../strategy/managed/ManagedStrategy.kt | 1 + .../stress/StressCTestConfiguration.kt | 1 + .../test/FailedScenarioMinimizationTest.kt | 2 - .../AFUCallRepresentationTest.kt | 1 - .../representation/StateRepresentationTest.kt | 1 - .../test/runner/CancellationHandlingTest.kt | 2 - .../runner/TestThreadExecutionHelperTest.java | 4 +- .../transformation/SerializableValueTests.kt | 1 - .../test/verifier/CustomScenarioDSL.kt | 2 +- .../linearizability/SkipListMapTest.kt | 2 - 31 files changed, 457 insertions(+), 293 deletions(-) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt (100%) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt (100%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt (100%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt (90%) delete mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.java create mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt diff --git a/build.gradle.kts b/build.gradle.kts index 1bf64376f..b80965b45 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,23 @@ kotlin { } sourceSets { + val commonMain by getting { + kotlin.srcDir("src/common/main") + val kotlinVersion: String by project + val kotlinxCoroutinesVersion: String by project + val asmVersion: String by project + val reflectionsVersion: String by project + dependencies { + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") + api("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion") + api("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion") + api("org.ow2.asm:asm-commons:$asmVersion") + api("org.ow2.asm:asm-util:$asmVersion") + api("org.reflections:reflections:$reflectionsVersion") + } + } + val jvmMain by getting { kotlin.srcDir("src/jvm/main") diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt new file mode 100644 index 000000000..078b2f9d6 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -0,0 +1,60 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.annotations.Operation + +expect class Method + +expect fun Method.isSuspendable(): Boolean + +expect class HandledException + +/** + * The actor entity describe the operation with its parameters + * which is executed during the testing. + * + * @see Operation + */ +expect class Actor constructor( + method: Method, + arguments: List, + handledExceptions: List = emptyList(), + cancelOnSuspension: Boolean = false, + allowExtraSuspension: Boolean = false, + blocking: Boolean = false, + causesBlocking: Boolean = false, + promptCancellation: Boolean = false, + isSuspendable: Boolean = method.isSuspendable() +) { + override fun toString(): String + val handlesExceptions: Boolean + val method: Method + val arguments: List + val handledExceptions: List + val cancelOnSuspension: Boolean + val allowExtraSuspension: Boolean + val blocking: Boolean + val causesBlocking: Boolean + val promptCancellation: Boolean + val isSuspendable: Boolean + +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt new file mode 100644 index 000000000..c544200ae --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt @@ -0,0 +1,29 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.annotations + +/** + * This annotation should be added to a test class to specify the logging level. + * By default, [LoggingLevel.ERROR] is used. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +expect annotation class LogLevel \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt new file mode 100644 index 000000000..a52f2dc3f --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt @@ -0,0 +1,37 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.annotations + +/** + * Set some restrictions to the group with the specified name, + * used during the scenario generation phase. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +expect annotation class OpGroupConfig(val name: String, val nonParallel: Boolean) { + /** + * Holder annotation for [OpGroupConfig]. + * Not a public API. + */ + @Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) + annotation class OpGroupConfigs(vararg val value: OpGroupConfig) +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt new file mode 100644 index 000000000..e61383419 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt @@ -0,0 +1,57 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.annotations + +import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator +import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator.Dummy +import kotlin.reflect.KClass + +/** + * Use this annotation to specify parameter generators. + * @see ParameterGenerator + */ +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Repeatable +expect annotation class Param constructor( + /** + * If the annotation is set on a class, creates a [parameter generator][ParameterGenerator] + * which can be used in [operations][Operation] by this name. If is set on an operation, + * uses the specified named parameter generator which is created as described before. + */ + val name: String, + /** + * Specifies the [ParameterGenerator] class which should be used for this parameter. + */ + val gen: KClass>, + /** + * Specifies the configuration for the [parameter generator][.gen]. + */ + val conf: String +) { + /** + * Holder annotation for [Param]. + * Not a public API. + */ + @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) + annotation class Params constructor(vararg val value: Param) +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt similarity index 90% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt index 754f55411..5df6d9821 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt @@ -1,9 +1,8 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,8 +15,7 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.paramgen diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 208a3fd5d..99bff7947 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -22,8 +22,14 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.annotations.* -import java.lang.reflect.Method -import kotlin.reflect.jvm.* +import kotlin.reflect.jvm.kotlinFunction + +actual typealias Method = java.lang.reflect.Method + +/** + * Handled exception, represents Class + */ +actual data class HandledException(val exceptionClass: Class) /** * The actor entity describe the operation with its parameters @@ -31,18 +37,18 @@ import kotlin.reflect.jvm.* * * @see Operation */ -data class Actor @JvmOverloads constructor( - val method: Method, - val arguments: List, - val handledExceptions: List> = emptyList(), - val cancelOnSuspension: Boolean = false, - val allowExtraSuspension: Boolean = false, - val blocking: Boolean = false, - val causesBlocking: Boolean = false, - val promptCancellation: Boolean = false, +actual data class Actor @JvmOverloads actual constructor( + actual val method: Method, + actual val arguments: List, + actual val handledExceptions: List, + actual val cancelOnSuspension: Boolean, + actual val allowExtraSuspension: Boolean, + actual val blocking: Boolean, + actual val causesBlocking: Boolean, + actual val promptCancellation: Boolean, // we have to specify `isSuspendable` property explicitly for transformed classes since // `isSuspendable` implementation produces a circular dependency and, therefore, fails. - val isSuspendable: Boolean = method.isSuspendable() + actual val isSuspendable: Boolean ) { init { if (promptCancellation) require(cancelOnSuspension) { @@ -50,13 +56,13 @@ data class Actor @JvmOverloads constructor( } } - override fun toString() = method.name + + actual override fun toString() = method.name + arguments.joinToString(prefix = "(", postfix = ")", separator = ", ") { it.toString() } + (if (cancelOnSuspension) " + " else "") + (if (promptCancellation) "prompt_" else "") + (if (cancelOnSuspension) "cancel" else "") - val handlesExceptions = handledExceptions.isNotEmpty() + actual val handlesExceptions = handledExceptions.isNotEmpty() } -fun Method.isSuspendable(): Boolean = kotlinFunction?.isSuspend ?: false \ No newline at end of file +actual fun Method.isSuspendable(): Boolean = kotlinFunction?.isSuspend ?: false \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.java deleted file mode 100644 index 9e4184cc6..000000000 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.java +++ /dev/null @@ -1,244 +0,0 @@ -package org.jetbrains.kotlinx.lincheck; - -/* - * #%L - * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.jetbrains.kotlinx.lincheck.annotations.*; -import org.jetbrains.kotlinx.lincheck.execution.*; -import org.jetbrains.kotlinx.lincheck.paramgen.*; -import org.jetbrains.kotlinx.lincheck.strategy.stress.*; - -import java.lang.reflect.*; -import java.util.*; -import java.util.stream.*; - -import static org.jetbrains.kotlinx.lincheck.ActorKt.*; - -/** - * Contains information about the provided operations (see {@link Operation}). - * Several {@link StressCTest tests} can refer to one structure - * (i.e. one test class could have several {@link StressCTest} annotations) - */ -public class CTestStructure { - public final List actorGenerators; - public final List operationGroups; - public final List validationFunctions; - public final Method stateRepresentation; - - private CTestStructure(List actorGenerators, List operationGroups, - List validationFunctions, Method stateRepresentation) { - this.actorGenerators = actorGenerators; - this.operationGroups = operationGroups; - this.validationFunctions = validationFunctions; - this.stateRepresentation = stateRepresentation; - } - - /** - * Constructs {@link CTestStructure} for the specified test class. - */ - public static CTestStructure getFromTestClass(Class testClass) { - Map> namedGens = new HashMap<>(); - Map groupConfigs = new HashMap<>(); - List actorGenerators = new ArrayList<>(); - List validationFunctions = new ArrayList<>(); - List stateRepresentations = new ArrayList<>(); - Class clazz = testClass; - while (clazz != null) { - readTestStructureFromClass(clazz, namedGens, groupConfigs, actorGenerators, validationFunctions, stateRepresentations); - clazz = clazz.getSuperclass(); - } - if (stateRepresentations.size() > 1) { - throw new IllegalStateException("Several functions marked with " + StateRepresentation.class.getSimpleName() + - " were found, while at most one should be specified: " + - stateRepresentations.stream().map(Method::getName).collect(Collectors.joining(", "))); - } - Method stateRepresentation = null; - if (!stateRepresentations.isEmpty()) - stateRepresentation = stateRepresentations.get(0); - // Create StressCTest class configuration - return new CTestStructure(actorGenerators, new ArrayList<>(groupConfigs.values()), validationFunctions, stateRepresentation); - } - - private static void readTestStructureFromClass(Class clazz, Map> namedGens, - Map groupConfigs, - List actorGenerators, - List validationFunctions, - List stateRepresentations) { - // Read named parameter paramgen (declared for class) - for (Param paramAnn : clazz.getAnnotationsByType(Param.class)) { - if (paramAnn.name().isEmpty()) { - throw new IllegalArgumentException("@Param name in class declaration cannot be empty"); - } - namedGens.put(paramAnn.name(), createGenerator(paramAnn)); - } - // Create map for default (not named) gens - Map, ParameterGenerator> defaultGens = createDefaultGenerators(); - // Read group configurations - for (OpGroupConfig opGroupConfigAnn: clazz.getAnnotationsByType(OpGroupConfig.class)) { - groupConfigs.put(opGroupConfigAnn.name(), new OperationGroup(opGroupConfigAnn.name(), - opGroupConfigAnn.nonParallel())); - } - // Create actor paramgen - for (Method m : getDeclaredMethodSorted(clazz)) { - // Operation - if (m.isAnnotationPresent(Operation.class)) { - Operation opAnn = m.getAnnotation(Operation.class); - boolean isSuspendableMethod = isSuspendable(m); - // Check that params() in @Operation is empty or has the same size as the method - if (opAnn.params().length > 0 && opAnn.params().length != m.getParameterCount()) { - throw new IllegalArgumentException("Invalid count of paramgen for " + m.toString() - + " method in @Operation"); - } - // Construct list of parameter paramgen - final List> gens = new ArrayList<>(); - int nParameters = m.getParameterCount() - (isSuspendableMethod ? 1 : 0); - for (int i = 0; i < nParameters; i++) { - String nameInOperation = opAnn.params().length > 0 ? opAnn.params()[i] : null; - gens.add(getOrCreateGenerator(m, m.getParameters()[i], nameInOperation, namedGens, defaultGens)); - } - // Get list of handled exceptions if they are presented - List> handledExceptions = Arrays.asList(opAnn.handleExceptionsAsResult()); - ActorGenerator actorGenerator = new ActorGenerator(m, gens, handledExceptions, opAnn.runOnce(), - opAnn.cancellableOnSuspension(), opAnn.allowExtraSuspension(), opAnn.blocking(), opAnn.causesBlocking(), - opAnn.promptCancellation()); - actorGenerators.add(actorGenerator); - // Get list of groups and add this operation to specified ones - String opGroup = opAnn.group(); - if (!opGroup.isEmpty()) { - OperationGroup operationGroup = groupConfigs.get(opGroup); - if (operationGroup == null) - throw new IllegalStateException("Operation group " + opGroup + " is not configured"); - operationGroup.actors.add(actorGenerator); - } - } - if (m.isAnnotationPresent(Validate.class)) { - if (m.getParameterCount() != 0) - throw new IllegalStateException("Validation function " + m.getName() + " should not have parameters"); - validationFunctions.add(m); - } - - if (m.isAnnotationPresent(StateRepresentation.class)) { - if (m.getParameterCount() != 0) - throw new IllegalStateException("State representation function " + m.getName() + " should not have parameters"); - if (m.getReturnType() != String.class) - throw new IllegalStateException("State representation function " + m.getName() + " should have String return type"); - stateRepresentations.add(m); - } - } - } - - /** - * Sort methods by name to make scenario generation deterministic. - */ - private static Method[] getDeclaredMethodSorted(Class clazz) { - Method[] methods = clazz.getDeclaredMethods(); - Comparator comparator = Comparator - // compare by method name - .comparing(Method::getName) - // then compare by parameter class names - .thenComparing(m -> Arrays.stream(m.getParameterTypes()).map(Class::getName).collect(Collectors.joining(":"))); - Arrays.sort(methods, comparator); - return methods; - } - - private static ParameterGenerator getOrCreateGenerator(Method m, Parameter p, String nameInOperation, - Map> namedGens, Map, ParameterGenerator> defaultGens) - { - // Read @Param annotation on the parameter - Param paramAnn = p.getAnnotation(Param.class); - // If this annotation not presented use named generator based on name presented in @Operation or parameter name. - if (paramAnn == null) { - // If name in @Operation is presented, return the generator with this name, - // otherwise return generator with parameter's name - String name = nameInOperation != null ? nameInOperation : - (p.isNamePresent() ? p.getName() : null); - if (name != null) - return checkAndGetNamedGenerator(namedGens, name); - // Parameter generator is not specified, try to create a default one - ParameterGenerator defaultGenerator = defaultGens.get(p.getType()); - if (defaultGenerator != null) - return defaultGenerator; - // Cannot create default parameter generator, throw an exception - throw new IllegalStateException("Generator for parameter \"" + p + "\" in method \"" - + m.getName() + "\" should be specified."); - } - // If the @Param annotation is presented check it's correctness firstly - if (!paramAnn.name().isEmpty() && !(paramAnn.gen() == ParameterGenerator.Dummy.class)) - throw new IllegalStateException("@Param should have either name or gen with optionally configuration"); - // If @Param annotation specifies generator's name then return the specified generator - if (!paramAnn.name().isEmpty()) - return checkAndGetNamedGenerator(namedGens, paramAnn.name()); - // Otherwise create new parameter generator - return createGenerator(paramAnn); - } - - private static ParameterGenerator createGenerator(Param paramAnn) { - try { - return paramAnn.gen().getConstructor(String.class).newInstance(paramAnn.conf()); - } catch (Exception e) { - throw new IllegalStateException("Cannot create parameter gen", e); - } - } - - private static Map, ParameterGenerator> createDefaultGenerators() { - Map, ParameterGenerator> defaultGens = new HashMap<>(); - defaultGens.put(byte.class, new ByteGen("")); - defaultGens.put(Byte.class, defaultGens.get(byte.class)); - defaultGens.put(short.class, new ShortGen("")); - defaultGens.put(Short.class, defaultGens.get(short.class)); - defaultGens.put(int.class, new IntGen("")); - defaultGens.put(Integer.class, defaultGens.get(int.class)); - defaultGens.put(long.class, new LongGen("")); - defaultGens.put(Long.class, defaultGens.get(long.class)); - defaultGens.put(float.class, new FloatGen("")); - defaultGens.put(Float.class, defaultGens.get(float.class)); - defaultGens.put(double.class, new DoubleGen("")); - defaultGens.put(Double.class, defaultGens.get(double.class)); - defaultGens.put(String.class, new StringGen("")); - return defaultGens; - } - - private static ParameterGenerator checkAndGetNamedGenerator(Map> namedGens, String name) { - return Objects.requireNonNull(namedGens.get(name), "Unknown generator name: \"" + name + "\""); - } - - public static class OperationGroup { - public final String name; - public final boolean nonParallel; - public final List actors; - - public OperationGroup(String name, boolean nonParallel) { - this.name = name; - this.nonParallel = nonParallel; - this.actors = new ArrayList<>(); - } - - @Override - public String toString() { - return "OperationGroup{" + - "name='" + name + '\'' + - ", nonParallel=" + nonParallel + - ", actors=" + actors + - '}'; - } - } -} diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt new file mode 100644 index 000000000..d16241a8b --- /dev/null +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -0,0 +1,209 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.execution.ActorGenerator +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator.Dummy +import java.lang.Exception +import java.lang.reflect.Method +import java.lang.reflect.Parameter +import java.util.* +import java.util.stream.Collectors +import kotlin.collections.HashMap + +/** + * Contains information about the provided operations (see [Operation]). + * Several [tests][StressCTest] can refer to one structure + * (i.e. one test class could have several [StressCTest] annotations) + */ +class CTestStructure private constructor(val actorGenerators: List, val operationGroups: List, + val validationFunctions: List, val stateRepresentation: Method?) { + class OperationGroup(val name: String, val nonParallel: Boolean) { + val actors: MutableList + override fun toString(): String { + return "OperationGroup{" + + "name='" + name + '\'' + + ", nonParallel=" + nonParallel + + ", actors=" + actors + + '}' + } + + init { + actors = ArrayList() + } + } + + companion object { + /** + * Constructs [CTestStructure] for the specified test class. + */ + fun getFromTestClass(testClass: Class<*>?): CTestStructure { + val namedGens: MutableMap> = HashMap() + val groupConfigs: MutableMap = HashMap() + val actorGenerators: MutableList = ArrayList() + val validationFunctions: MutableList = ArrayList() + val stateRepresentations: MutableList = ArrayList() + var clazz = testClass + while (clazz != null) { + readTestStructureFromClass(clazz, namedGens, groupConfigs, actorGenerators, validationFunctions, stateRepresentations) + clazz = clazz.superclass + } + check(stateRepresentations.size <= 1) { + "Several functions marked with " + StateRepresentation::class.java.simpleName + + " were found, while at most one should be specified: " + + stateRepresentations.stream().map { obj: Method -> obj.name }.collect(Collectors.joining(", ")) + } + var stateRepresentation: Method? = null + if (!stateRepresentations.isEmpty()) stateRepresentation = stateRepresentations[0] + // Create StressCTest class configuration + return CTestStructure(actorGenerators, ArrayList(groupConfigs.values), validationFunctions, stateRepresentation) + } + + private fun readTestStructureFromClass(clazz: Class<*>, namedGens: MutableMap>, + groupConfigs: MutableMap, + actorGenerators: MutableList, + validationFunctions: MutableList, + stateRepresentations: MutableList) { + // Read named parameter paramgen (declared for class) + for (paramAnn in clazz.getAnnotationsByType(Param::class.java)) { + require(!paramAnn.name.isEmpty()) { "@Param name in class declaration cannot be empty" } + namedGens[paramAnn.name] = createGenerator(paramAnn) + } + // Create map for default (not named) gens + val defaultGens = createDefaultGenerators() + // Read group configurations + for (opGroupConfigAnn in clazz.getAnnotationsByType(OpGroupConfig::class.java)) { + groupConfigs[opGroupConfigAnn.name] = OperationGroup(opGroupConfigAnn.name, + opGroupConfigAnn.nonParallel) + } + // Create actor paramgen + for (m in getDeclaredMethodSorted(clazz)) { + // Operation + if (m.isAnnotationPresent(Operation::class.java)) { + val opAnn = m.getAnnotation(Operation::class.java) + val isSuspendableMethod = m.isSuspendable() + // Check that params() in @Operation is empty or has the same size as the method + require(!(opAnn.params.isNotEmpty() && opAnn.params.size != m.parameterCount)) { + ("Invalid count of paramgen for " + m.toString() + + " method in @Operation") + } + // Construct list of parameter paramgen + val gens: MutableList> = ArrayList() + val nParameters = m.parameterCount - if (isSuspendableMethod) 1 else 0 + for (i in 0 until nParameters) { + val nameInOperation = if (opAnn.params.isNotEmpty()) opAnn.params[i] else null + gens.add(getOrCreateGenerator(m, m.parameters[i], nameInOperation, namedGens, defaultGens)) + } + // Get list of handled exceptions if they are presented + val handledExceptions: List = opAnn.handleExceptionsAsResult.map { HandledException(it.java) } + val actorGenerator = ActorGenerator(m, gens, handledExceptions, opAnn.runOnce, + opAnn.cancellableOnSuspension, opAnn.allowExtraSuspension, opAnn.blocking, opAnn.causesBlocking, + opAnn.promptCancellation) + actorGenerators.add(actorGenerator) + // Get list of groups and add this operation to specified ones + val opGroup = opAnn.group + if (!opGroup.isEmpty()) { + val operationGroup = groupConfigs[opGroup] + ?: throw IllegalStateException("Operation group $opGroup is not configured") + operationGroup.actors.add(actorGenerator) + } + } + if (m.isAnnotationPresent(Validate::class.java)) { + check(m.parameterCount == 0) { "Validation function " + m.name + " should not have parameters" } + validationFunctions.add(m) + } + if (m.isAnnotationPresent(StateRepresentation::class.java)) { + check(m.parameterCount == 0) { "State representation function " + m.name + " should not have parameters" } + check(m.returnType == String::class.java) { "State representation function " + m.name + " should have String return type" } + stateRepresentations.add(m) + } + } + } + + /** + * Sort methods by name to make scenario generation deterministic. + */ + private fun getDeclaredMethodSorted(clazz: Class<*>): Array { + val methods = clazz.declaredMethods + val comparator = Comparator // compare by method name + .comparing { obj: Method -> obj.name } // then compare by parameter class names + .thenComparing { m: Method -> Arrays.stream(m.parameterTypes).map { obj: Class<*> -> obj.name }.collect(Collectors.joining(":")) } + Arrays.sort(methods, comparator) + return methods + } + + private fun getOrCreateGenerator(m: Method, p: Parameter, nameInOperation: String?, + namedGens: Map>, defaultGens: Map?, ParameterGenerator<*>>): ParameterGenerator<*> { + // Read @Param annotation on the parameter + val paramAnn = p.getAnnotation(Param::class.java) + // If this annotation not presented use named generator based on name presented in @Operation or parameter name. + if (paramAnn == null) { + // If name in @Operation is presented, return the generator with this name, + // otherwise return generator with parameter's name + val name = nameInOperation ?: if (p.isNamePresent) p.name else null + if (name != null) return checkAndGetNamedGenerator(namedGens, name) + // Parameter generator is not specified, try to create a default one + val parameterType = p.type + val defaultGenerator = defaultGens[parameterType] + if (defaultGenerator != null) return defaultGenerator + throw IllegalStateException("Generator for parameter \"" + p + "\" in method \"" + + m.name + "\" should be specified.") + } + // If the @Param annotation is presented check it's correctness firstly + check(!(!paramAnn.name.isEmpty() && paramAnn.gen != Dummy::class)) { "@Param should have either name or gen with optionally configuration" } + // If @Param annotation specifies generator's name then return the specified generator + return if (!paramAnn.name.isEmpty()) checkAndGetNamedGenerator(namedGens, paramAnn.name) else createGenerator(paramAnn) + // Otherwise create new parameter generator + } + + private fun createGenerator(paramAnn: Param): ParameterGenerator<*> { + return try { + paramAnn.gen.java.getConstructor(String::class.java).newInstance(paramAnn.conf) + } catch (e: Exception) { + throw IllegalStateException("Cannot create parameter gen", e) + } + } + + private fun createDefaultGenerators(): Map?, ParameterGenerator<*>> { + val defaultGens: MutableMap?, ParameterGenerator<*>> = HashMap() + defaultGens[Byte::class.javaPrimitiveType] = ByteGen("") + defaultGens[Byte::class.javaObjectType] = defaultGens[Byte::class.javaPrimitiveType]!! + defaultGens[Short::class.javaPrimitiveType] = ShortGen("") + defaultGens[Short::class.javaObjectType] = defaultGens[Short::class.javaPrimitiveType]!! + defaultGens[Int::class.javaPrimitiveType] = IntGen("") + defaultGens[Int::class.javaObjectType] = defaultGens[Int::class.javaPrimitiveType]!! + defaultGens[Long::class.javaPrimitiveType] = LongGen("") + defaultGens[Long::class.javaObjectType] = defaultGens[Long::class.javaPrimitiveType]!! + defaultGens[Float::class.javaPrimitiveType] = FloatGen("") + defaultGens[Float::class.javaObjectType] = defaultGens[Float::class.javaPrimitiveType]!! + defaultGens[Double::class.javaPrimitiveType] = DoubleGen("") + defaultGens[Double::class.javaObjectType] = defaultGens[Double::class.javaPrimitiveType]!! + defaultGens[String::class.javaObjectType] = StringGen("") + return defaultGens + } + + private fun checkAndGetNamedGenerator(namedGens: Map>, name: String): ParameterGenerator<*> { + return Objects.requireNonNull(namedGens[name], "Unknown generator name: \"$name\"")!! + } + } +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt index 17e7259f9..9e0d48d15 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CancellabilitySupportTransformer.kt @@ -47,5 +47,5 @@ private class CancellabilitySupportMethodTransformer(access: Int, methodName: St } } -private val storeCancellableContMethod = Method.getMethod(::storeCancellableContinuation.javaMethod) +private val storeCancellableContMethod = org.objectweb.asm.commons.Method.getMethod(::storeCancellableContinuation.javaMethod) private val storeCancellableContOwnerType = Type.getType(::storeCancellableContinuation.javaMethod!!.declaringClass) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 2773f10a2..753304ee1 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -63,7 +63,7 @@ internal fun executeActor( } catch (invE: Throwable) { val eClass = (invE.cause ?: invE).javaClass.normalize() for (ec in actor.handledExceptions) { - if (ec.isAssignableFrom(eClass)) + if (ec.exceptionClass.isAssignableFrom(eClass)) return ExceptionResult.create(eClass) } throw IllegalStateException("Invalid exception as a result of $actor", invE) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt index 0d156a3e7..c68c3ec4d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt @@ -31,4 +31,4 @@ import java.lang.annotation.Inherited @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited -annotation class LogLevel(val value: LoggingLevel) \ No newline at end of file +actual annotation class LogLevel(val value: LoggingLevel) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt index 451f0b290..be548a045 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt @@ -32,17 +32,17 @@ import java.lang.annotation.Repeatable @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Repeatable(OpGroupConfig.OpGroupConfigs::class) @Inherited -annotation class OpGroupConfig( +actual annotation class OpGroupConfig actual constructor( /** * Name of this group used by [Operation.group]. */ - val name: String = "", + actual val name: String = "", /** * Set it to `true` for executing all actors in this group * from one thread. This restriction allows to test single-reader * and/or single-writer data structures and similar solutions. */ - val nonParallel: Boolean = false) { + actual val nonParallel: Boolean = false) { /** * Holder annotation for [OpGroupConfig]. * Not a public API. @@ -50,5 +50,5 @@ annotation class OpGroupConfig( @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited - annotation class OpGroupConfigs(vararg val value: OpGroupConfig) + actual annotation class OpGroupConfigs actual constructor(actual vararg val value: OpGroupConfig) } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt index 9ef7a04e2..b203091d7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt @@ -34,21 +34,21 @@ import kotlin.reflect.KClass @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Repeatable(Param.Params::class) @Inherited -annotation class Param( +actual annotation class Param actual constructor( /** * If the annotation is set on a class, creates a [parameter generator][ParameterGenerator] * which can be used in [operations][Operation] by this name. If is set on an operation, * uses the specified named parameter generator which is created as described before. */ - val name: String = "", + actual val name: String = "", /** * Specifies the [ParameterGenerator] class which should be used for this parameter. */ - val gen: KClass> = Dummy::class, + actual val gen: KClass> = Dummy::class, /** * Specifies the configuration for the [parameter generator][.gen]. */ - val conf: String = "" + actual val conf: String = "" ) { /** * Holder annotation for [Param]. @@ -57,5 +57,5 @@ annotation class Param( @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited - annotation class Params(vararg val value: Param) + actual annotation class Params actual constructor(actual vararg val value: Param) } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index f03dda4ab..2b23ff443 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -22,8 +22,8 @@ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.paramgen.* -import java.lang.reflect.* import kotlin.random.* import kotlin.reflect.KClass @@ -34,7 +34,7 @@ import kotlin.reflect.KClass class ActorGenerator( private val method: Method, private val parameterGenerators: List>, - private val handledExceptions: List>, + private val handledExceptions: List, val useOnce: Boolean, cancellableOnSuspension: Boolean, private val allowExtraSuspension: Boolean, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 4cc630481..22ad4e9bc 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -23,6 +23,7 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.FixedActiveThreadsExecutor.TestThread import org.jetbrains.kotlinx.lincheck.runner.UseClocks.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index ff5ffd6f6..41ac490ee 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -22,6 +22,7 @@ package org.jetbrains.kotlinx.lincheck.runner import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.strategy.* import org.objectweb.asm.* import java.lang.reflect.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java index cbb746eb8..8acfd834a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java @@ -191,8 +191,8 @@ private static void generateRun(ClassVisitor cv, Type testType, int iThread, Lis Label actorCatchBlockEnd = mv.newLabel(); if (actor.getHandlesExceptions()) { handledExceptionHandler = mv.newLabel(); - for (Class ec : actor.getHandledExceptions()) - mv.visitTryCatchBlock(actorCatchBlockStart, actorCatchBlockEnd, handledExceptionHandler, getType(ec).getInternalName()); + for (HandledException ec : actor.getHandledExceptions()) + mv.visitTryCatchBlock(actorCatchBlockStart, actorCatchBlockEnd, handledExceptionHandler, getType(ec.getExceptionClass()).getInternalName()); } // Catch those exceptions that has not been caught yet Label unexpectedExceptionHandler = mv.newLabel(); diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 9fcddcdbc..8d86dc5a8 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -22,6 +22,7 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index a81d55991..d58b94de0 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -22,6 +22,7 @@ package org.jetbrains.kotlinx.lincheck.strategy.stress import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import java.lang.reflect.* diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt index ae176311b..94a2fac33 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/FailedScenarioMinimizationTest.kt @@ -22,9 +22,7 @@ package org.jetbrains.kotlinx.lincheck.test import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.annotations.Operation -import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.junit.* diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt index 9df303e0a..e3b3e27d7 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt @@ -24,7 +24,6 @@ package org.jetbrains.kotlinx.lincheck.test.representation import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.junit.* import java.util.concurrent.atomic.* diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/StateRepresentationTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/StateRepresentationTest.kt index 2a0072a27..f0f7e8e4c 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/StateRepresentationTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/StateRepresentationTest.kt @@ -21,7 +21,6 @@ */ package org.jetbrains.kotlinx.lincheck.test.representation -import kotlinx.atomicfu.* import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.annotations.StateRepresentation import org.jetbrains.kotlinx.lincheck.appendFailure diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt index fd5ef6bcc..23e2b349e 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/CancellationHandlingTest.kt @@ -24,9 +24,7 @@ package org.jetbrains.kotlinx.lincheck.test.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.test.AbstractLincheckTest -import org.junit.* import java.util.concurrent.atomic.* class CancellationHandlingTest : AbstractLincheckTest() { diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java index 0f1e933e6..4386f2bbd 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java @@ -96,8 +96,8 @@ public void testActorExceptionHandling() throws Exception { asList( new Actor(ArrayDeque.class.getMethod("addLast", Object.class), asList(1)), new Actor(Queue.class.getMethod("remove"), emptyList()), - new Actor(Queue.class.getMethod("remove"), emptyList(), asList(NoSuchElementException.class)), - new Actor(Queue.class.getMethod("remove"), emptyList(), asList(Exception.class, NoSuchElementException.class)) + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(new HandledException(NoSuchElementException.class))), + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(new HandledException(Exception.class), new HandledException((NoSuchElementException.class)))) ), emptyList(), false); ex.testInstance = new ArrayDeque<>(); ex.results = new Result[4]; diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/transformation/SerializableValueTests.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/transformation/SerializableValueTests.kt index 354321963..8f63ab35e 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/transformation/SerializableValueTests.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/transformation/SerializableValueTests.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator import org.jetbrains.kotlinx.lincheck.test.AbstractLincheckTest import java.io.Serializable -import java.util.* import java.util.concurrent.atomic.* class SerializableResultTest : AbstractLincheckTest() { diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt index c9f687099..0868a3692 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt @@ -74,7 +74,7 @@ fun actor(function: KFunction<*>, vararg args: Any?, cancelOnSuspension: Boolean return Actor( method = method, arguments = args.toList(), - handledExceptions = (method.exceptionTypes as Array>).toList(), + handledExceptions = (method.exceptionTypes as Array>).toList().map { HandledException(it) }, cancelOnSuspension = cancelOnSuspension ) } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt index 696604ce3..96b329766 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SkipListMapTest.kt @@ -21,11 +21,9 @@ */ package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability -import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.test.* -import org.junit.* import java.util.concurrent.* @Param(name = "value", gen = IntGen::class, conf = "1:5") From 5f1b013750806d3228a63e967bdc99d3b42ebe51 Mon Sep 17 00:00:00 2001 From: krock21rus Date: Tue, 22 Dec 2020 15:25:32 +0300 Subject: [PATCH 05/72] Add Boolean default gen to defaultGens --- src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index d16241a8b..bcdd73acb 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -186,6 +186,8 @@ class CTestStructure private constructor(val actorGenerators: List?, ParameterGenerator<*>> { val defaultGens: MutableMap?, ParameterGenerator<*>> = HashMap() + defaultGens[Boolean::class.javaPrimitiveType] = BooleanGen("") + defaultGens[Boolean::class.javaObjectType] = defaultGens[Boolean::class.javaPrimitiveType]!! defaultGens[Byte::class.javaPrimitiveType] = ByteGen("") defaultGens[Byte::class.javaObjectType] = defaultGens[Byte::class.javaPrimitiveType]!! defaultGens[Short::class.javaPrimitiveType] = ShortGen("") From ec07ae34b70afeeafedbdbcd3f5fcf87428ddcd2 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 9 Jan 2021 01:48:24 +0300 Subject: [PATCH 06/72] Move some parts to common --- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 29 +---- .../kotlinx/lincheck/CTestConfiguration.kt | 7 ++ .../kotlinx/lincheck/CTestStructure.kt | 45 ++++++++ .../org/jetbrains/kotlinx/lincheck/Result.kt | 86 ++++++++++++++ .../lincheck/execution/ActorGenerator.kt | 10 ++ .../lincheck/execution/ExecutionGenerator.kt | 0 .../lincheck/execution/ExecutionScenario.kt | 105 ++++++++++++++++++ .../org/jetbrains/kotlinx/lincheck/Actor.kt | 33 +++--- .../kotlinx/lincheck/CTestConfiguration.kt | 16 +-- .../kotlinx/lincheck/CTestStructure.kt | 25 ++--- .../jetbrains/kotlinx/lincheck/JvmHacks.kt | 33 ++++++ .../org/jetbrains/kotlinx/lincheck/Options.kt | 5 +- .../jetbrains/kotlinx/lincheck/Reporter.kt | 53 +-------- .../org/jetbrains/kotlinx/lincheck/Result.kt | 82 ++------------ .../org/jetbrains/kotlinx/lincheck/Utils.kt | 3 +- .../lincheck/execution/ActorGenerator.kt | 28 ++--- .../lincheck/execution/ExecutionScenario.kt | 52 +-------- .../execution/RandomExecutionGenerator.kt | 5 +- .../lincheck/runner/ParallelThreadsRunner.kt | 1 - .../kotlinx/lincheck/runner/Runner.kt | 2 +- .../runner/TestThreadExecutionGenerator.java | 4 +- .../kotlinx/lincheck/strategy/Strategy.kt | 1 - .../managed/ManagedCTestConfiguration.kt | 4 +- .../strategy/managed/ManagedStrategy.kt | 1 - .../strategy/managed/TraceReporter.kt | 5 +- .../ModelCheckingCTestConfiguration.kt | 3 +- .../modelchecking/ModelCheckingStrategy.kt | 12 +- .../stress/StressCTestConfiguration.kt | 4 +- .../lincheck/verifier/AbstractLTSVerifier.kt | 1 - .../quiescent/QuiescentConsistencyVerifier.kt | 3 +- .../runner/TestThreadExecutionHelperTest.java | 4 +- .../test/verifier/CustomScenarioDSL.kt | 2 +- 32 files changed, 376 insertions(+), 288 deletions(-) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt (100%) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt create mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 078b2f9d6..1842fd4cd 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -22,39 +22,12 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.annotations.Operation -expect class Method - -expect fun Method.isSuspendable(): Boolean - -expect class HandledException - /** * The actor entity describe the operation with its parameters * which is executed during the testing. * * @see Operation */ -expect class Actor constructor( - method: Method, - arguments: List, - handledExceptions: List = emptyList(), - cancelOnSuspension: Boolean = false, - allowExtraSuspension: Boolean = false, - blocking: Boolean = false, - causesBlocking: Boolean = false, - promptCancellation: Boolean = false, - isSuspendable: Boolean = method.isSuspendable() -) { +expect class Actor { override fun toString(): String - val handlesExceptions: Boolean - val method: Method - val arguments: List - val handledExceptions: List - val cancelOnSuspension: Boolean - val allowExtraSuspension: Boolean - val blocking: Boolean - val causesBlocking: Boolean - val promptCancellation: Boolean - val isSuspendable: Boolean - } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt new file mode 100644 index 000000000..95fe4bab7 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -0,0 +1,7 @@ +package org.jetbrains.kotlinx.lincheck + +/** + * Abstract configuration for different lincheck modes. + */ +expect abstract class CTestConfiguration { +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt new file mode 100644 index 000000000..b780846a6 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -0,0 +1,45 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.execution.* + +/** + * Contains information about the provided operations (see [Operation]). + * Several [tests][StressCTest] can refer to one structure + * (i.e. one test class could have several [StressCTest] annotations) + */ +expect class CTestStructure + +class OperationGroup(val name: String, val nonParallel: Boolean) { + val actors: MutableList + override fun toString(): String { + return "OperationGroup{" + + "name='" + name + '\'' + + ", nonParallel=" + nonParallel + + ", actors=" + actors + + '}' + } + + init { + actors = ArrayList() + } +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt new file mode 100644 index 000000000..7c6bb3f07 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt @@ -0,0 +1,86 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +@file:JvmName("ResultKtCommon") +package org.jetbrains.kotlinx.lincheck + +import kotlin.coroutines.* +import kotlin.jvm.* + +/** + * The instance of this class represents a result of actor invocation. + * + *

If the actor invocation suspended the thread and did not get the final result yet + * though it can be resumed later, then the {@link Type#NO_RESULT no_result result type} is used. + * + * [wasSuspended] is true if before getting this result the actor invocation suspended the thread. + * If result is [NoResult] and [wasSuspended] is true it means that + * the execution thread was suspended without any chance to be resumed, + * meaning that all other esealedxecution threads completed their execution or were suspended too. + */ +abstract class Result { + abstract val wasSuspended: Boolean + protected val wasSuspendedPrefix: String get() = (if (wasSuspended) "SUSPENDED + " else "") +} + +/** + * Type of result used if the actor invocation does not return value. + */ +object VoidResult : Result() { + override val wasSuspended get() = false + override fun toString() = wasSuspendedPrefix + VOID +} + +object SuspendedVoidResult : Result() { + override val wasSuspended get() = true + override fun toString() = wasSuspendedPrefix + VOID +} + +private const val VOID = "void" + +object Cancelled : Result() { + override val wasSuspended get() = true + override fun toString() = wasSuspendedPrefix + "CANCELLED" +} + +/** + * Type of result used if the actor invocation suspended the thread and did not get the final result yet + * though it can be resumed later + */ +object NoResult : Result() { + override val wasSuspended get() = false + override fun toString() = "-" +} + +object Suspended : Result() { + override val wasSuspended get() = true + override fun toString() = "S" +} + +/** + * Type of result used for verification. + * Resuming thread writes result of the suspension point and continuation to be executed in the resumed thread into [contWithSuspensionPointRes]. + */ +internal data class ResumedResult(val contWithSuspensionPointRes: Pair?, kotlin.Result>) : Result() { + override val wasSuspended: Boolean get() = true + + lateinit var resumedActor: Actor + lateinit var by: Actor +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt new file mode 100644 index 000000000..31415159a --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -0,0 +1,10 @@ +package org.jetbrains.kotlinx.lincheck.execution + +import kotlin.random.* + +expect class ActorGenerator { + override fun toString(): String + val isSuspendable: Boolean +} + +internal val DETERMINISTIC_RANDOM = Random(42) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt new file mode 100644 index 000000000..0ef739225 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt @@ -0,0 +1,105 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ +@file:JvmName("ExecutionScenarioKtCommon") +package org.jetbrains.kotlinx.lincheck.execution + +import org.jetbrains.kotlinx.lincheck.Actor +import kotlin.jvm.* + +/** + * This class represents an execution scenario, which + * is generated by an [ExecutionGenerator] and then + * used by a [Strategy] which produces an [ExecutionResult]. + */ +class ExecutionScenario( + /** + * The initial sequential part of the execution. + * It helps to produce different initial states + * before the parallel part. + * + * The initial execution part should contain only non-suspendable actors; + * otherwise, the single initial execution thread will suspend with no chance to be resumed. + */ + val initExecution: List, + /** + * The parallel part of the execution, which is used + * to find an interleaving with incorrect behaviour. + */ + val parallelExecution: List>, + /** + * The last sequential part is used to test that + * the data structure is in some correct state. + * + * If this execution scenario contains suspendable actors, the post part should be empty; + * if not, an actor could resume a previously suspended one from the parallel execution part. + */ + val postExecution: List +) { + override fun toString(): String { + val sb = StringBuilder() + sb.appendExecutionScenario(this) + return sb.toString() + } +} + +/** + * Returns the number of threads used in the parallel part of this execution. + */ +val ExecutionScenario.threads: Int + get() = parallelExecution.size + +internal fun printInColumnsCustom( + groupedObjects: List>, + joinColumns: (List) -> String +): String { + val nRows = groupedObjects.map { it.size }.max() ?: 0 + val nColumns = groupedObjects.size + val rows = (0 until nRows).map { rowIndex -> + (0 until nColumns) + .map { groupedObjects[it] } + .map { it.getOrNull(rowIndex)?.toString().orEmpty() } // print empty strings for empty cells + } + val columnWidths: List = (0 until nColumns).map { columnIndex -> + (0 until nRows).map { rowIndex -> rows[rowIndex][columnIndex].length }.max() ?: 0 + } + return (0 until nRows) + .map { rowIndex -> rows[rowIndex].mapIndexed { columnIndex, cell -> cell.padEnd(columnWidths[columnIndex]) } } + .map { rowCells -> joinColumns(rowCells) } + .joinToString(separator = "\n") +} + +internal fun printInColumns(groupedObjects: List>) = printInColumnsCustom(groupedObjects) { it.joinToString(separator = " | ", prefix = "| ", postfix = " |") } + +internal fun StringBuilder.appendExecutionScenario(scenario: ExecutionScenario): StringBuilder { + if (scenario.initExecution.isNotEmpty()) { + appendLine("Execution scenario (init part):") + appendLine(scenario.initExecution) + } + if (scenario.parallelExecution.isNotEmpty()) { + appendLine("Execution scenario (parallel part):") + append(printInColumns(scenario.parallelExecution)) + appendLine() + } + if (scenario.postExecution.isNotEmpty()) { + appendLine("Execution scenario (post part):") + append(scenario.postExecution) + } + return this +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 99bff7947..356725db9 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -22,34 +22,29 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.annotations.* +import java.lang.reflect.* import kotlin.reflect.jvm.kotlinFunction -actual typealias Method = java.lang.reflect.Method - -/** - * Handled exception, represents Class - */ -actual data class HandledException(val exceptionClass: Class) - /** * The actor entity describe the operation with its parameters * which is executed during the testing. * * @see Operation */ -actual data class Actor @JvmOverloads actual constructor( - actual val method: Method, - actual val arguments: List, - actual val handledExceptions: List, - actual val cancelOnSuspension: Boolean, - actual val allowExtraSuspension: Boolean, - actual val blocking: Boolean, - actual val causesBlocking: Boolean, - actual val promptCancellation: Boolean, +actual data class Actor @JvmOverloads constructor( + val method: Method, + val arguments: List, + val handledExceptions: List> = emptyList(), + val cancelOnSuspension: Boolean = false, + val allowExtraSuspension: Boolean = false, + val blocking: Boolean = false, + val causesBlocking: Boolean = false, + val promptCancellation: Boolean = false, // we have to specify `isSuspendable` property explicitly for transformed classes since // `isSuspendable` implementation produces a circular dependency and, therefore, fails. - actual val isSuspendable: Boolean + val isSuspendable: Boolean = method.isSuspendable() ) { + init { if (promptCancellation) require(cancelOnSuspension) { "`promptCancellation` cannot be set to `true` if `cancelOnSuspension` is `false`" @@ -62,7 +57,7 @@ actual data class Actor @JvmOverloads actual constructor( (if (promptCancellation) "prompt_" else "") + (if (cancelOnSuspension) "cancel" else "") - actual val handlesExceptions = handledExceptions.isNotEmpty() + val handlesExceptions = handledExceptions.isNotEmpty() } -actual fun Method.isSuspendable(): Boolean = kotlinFunction?.isSuspend ?: false \ No newline at end of file +fun Method.isSuspendable(): Boolean = kotlinFunction?.isSuspend ?: false \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 658973d6a..2d9a0295c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -32,19 +32,20 @@ import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* import java.lang.reflect.* +import kotlin.reflect.* /** * Abstract configuration for different lincheck modes. */ -abstract class CTestConfiguration( +actual abstract class CTestConfiguration( val testClass: Class<*>, val iterations: Int, val threads: Int, val actorsPerThread: Int, val actorsBefore: Int, val actorsAfter: Int, - val generatorClass: Class, - val verifierClass: Class, + val generatorClass: KClass, + val verifierClass: KClass, val requireStateEquivalenceImplCheck: Boolean, val minimizeFailedScenario: Boolean, val sequentialSpecification: Class<*>?, @@ -52,14 +53,15 @@ abstract class CTestConfiguration( ) { abstract fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, stateRepresentationMethod: Method?, verifier: Verifier): Strategy + companion object { const val DEFAULT_ITERATIONS = 100 const val DEFAULT_THREADS = 2 const val DEFAULT_ACTORS_PER_THREAD = 5 const val DEFAULT_ACTORS_BEFORE = 5 const val DEFAULT_ACTORS_AFTER = 5 - val DEFAULT_EXECUTION_GENERATOR: Class = RandomExecutionGenerator::class.java - val DEFAULT_VERIFIER: Class = LinearizabilityVerifier::class.java + val DEFAULT_EXECUTION_GENERATOR: KClass = RandomExecutionGenerator::class + val DEFAULT_VERIFIER: KClass = LinearizabilityVerifier::class const val DEFAULT_MINIMIZE_ERROR = true const val DEFAULT_TIMEOUT_MS: Long = 10000 } @@ -70,7 +72,7 @@ internal fun createFromTestClassAnnotations(testClass: Class<*>): List StressCTestConfiguration(testClass, ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator.java, ann.verifier.java, ann.invocationsPerIteration, + ann.generator, ann.verifier, ann.invocationsPerIteration, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification.java, testClass), DEFAULT_TIMEOUT_MS ) @@ -79,7 +81,7 @@ internal fun createFromTestClassAnnotations(testClass: Class<*>): List ModelCheckingCTestConfiguration(testClass, ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator.java, ann.verifier.java, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, + ann.generator, ann.verifier, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, ann.invocationsPerIteration, ManagedCTestConfiguration.DEFAULT_GUARANTEES, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification.java, testClass), DEFAULT_TIMEOUT_MS, DEFAULT_ELIMINATE_LOCAL_OBJECTS, DEFAULT_VERBOSE_TRACE diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index bcdd73acb..f123f1b8f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -36,23 +36,12 @@ import kotlin.collections.HashMap * Several [tests][StressCTest] can refer to one structure * (i.e. one test class could have several [StressCTest] annotations) */ -class CTestStructure private constructor(val actorGenerators: List, val operationGroups: List, - val validationFunctions: List, val stateRepresentation: Method?) { - class OperationGroup(val name: String, val nonParallel: Boolean) { - val actors: MutableList - override fun toString(): String { - return "OperationGroup{" + - "name='" + name + '\'' + - ", nonParallel=" + nonParallel + - ", actors=" + actors + - '}' - } - - init { - actors = ArrayList() - } - } - +actual class CTestStructure private constructor( + val actorGenerators: List, + val operationGroups: List, + val validationFunctions: List, + val stateRepresentation: Method? +) { companion object { /** * Constructs [CTestStructure] for the specified test class. @@ -115,7 +104,7 @@ class CTestStructure private constructor(val actorGenerators: List = opAnn.handleExceptionsAsResult.map { HandledException(it.java) } + val handledExceptions: List> = opAnn.handleExceptionsAsResult.map { it.java } val actorGenerator = ActorGenerator(m, gens, handledExceptions, opAnn.runOnce, opAnn.cancellableOnSuspension, opAnn.allowExtraSuspension, opAnn.blocking, opAnn.causesBlocking, opAnn.promptCancellation) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt new file mode 100644 index 000000000..1a690e298 --- /dev/null +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt @@ -0,0 +1,33 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import java.lang.reflect.* +import kotlin.reflect.* +import kotlin.reflect.jvm.* + +fun KClass.getConstructor(vararg args: KParameter) : KFunction { + return this.constructors.find { it.parameters == args.toList() }!! +} + +fun KClass.getConstructor(vararg args: Class<*>) : Constructor { + return this.java.getConstructor(*args) +} diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt index 3a733dfdc..b6662f177 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -24,6 +24,7 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.reflect.* /** * Abstract class for test options. @@ -101,14 +102,14 @@ abstract class Options, CTEST : CTestConfiguration> { * Use the specified execution generator. */ fun executionGenerator(executionGenerator: Class): OPT = applyAndCast { - this.executionGenerator = executionGenerator + this.executionGenerator = executionGenerator.kotlin } /** * Use the specified verifier. */ fun verifier(verifier: Class): OPT = applyAndCast { - this.verifier = verifier + this.verifier = verifier.kotlin } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt index ea16f183f..59d2787a0 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt @@ -1,23 +1,21 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck @@ -57,28 +55,6 @@ enum class LoggingLevel { INFO, ERROR } -internal fun printInColumnsCustom( - groupedObjects: List>, - joinColumns: (List) -> String -): String { - val nRows = groupedObjects.map { it.size }.max() ?: 0 - val nColumns = groupedObjects.size - val rows = (0 until nRows).map { rowIndex -> - (0 until nColumns) - .map { groupedObjects[it] } - .map { it.getOrNull(rowIndex)?.toString().orEmpty() } // print empty strings for empty cells - } - val columnWidths: List = (0 until nColumns).map { columnIndex -> - (0 until nRows).map { rowIndex -> rows[rowIndex][columnIndex].length }.max() ?: 0 - } - return (0 until nRows) - .map { rowIndex -> rows[rowIndex].mapIndexed { columnIndex, cell -> cell.padEnd(columnWidths[columnIndex]) } } - .map { rowCells -> joinColumns(rowCells) } - .joinToString(separator = "\n") -} - -private fun printInColumns(groupedObjects: List>) = printInColumnsCustom(groupedObjects) { it.joinToString(separator = " | ", prefix = "| ", postfix = " |") } - private class ActorWithResult(val actorRepresentation: String, val spacesAfterActor: Int, val resultRepresentation: String, val spacesAfterResult: Int, val clockRepresentation: String) { @@ -125,23 +101,6 @@ private fun uniteActorsAndResultsAligned(actors: List, results: List appendIncorrectResultsFailure(failure) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt index 65bb36dd0..8495d9b35 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt @@ -1,14 +1,8 @@ -package org.jetbrains.kotlinx.lincheck - -import java.io.Serializable -import kotlin.coroutines.* - /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -21,25 +15,13 @@ import kotlin.coroutines.* * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ -/** - * The instance of this class represents a result of actor invocation. - * - *

If the actor invocation suspended the thread and did not get the final result yet - * though it can be resumed later, then the {@link Type#NO_RESULT no_result result type} is used. - * - * [wasSuspended] is true if before getting this result the actor invocation suspended the thread. - * If result is [NoResult] and [wasSuspended] is true it means that - * the execution thread was suspended without any chance to be resumed, - * meaning that all other execution threads completed their execution or were suspended too. - */ -sealed class Result { - abstract val wasSuspended: Boolean - protected val wasSuspendedPrefix: String get() = (if (wasSuspended) "SUSPENDED + " else "") -} +package org.jetbrains.kotlinx.lincheck + +import java.io.Serializable +import kotlin.coroutines.* /** * Type of result used if the actor invocation returns any value. @@ -78,26 +60,6 @@ class ValueResult @JvmOverloads constructor(val value: Any?, override val wasSus override fun hashCode(): Int = if (wasSuspended) 0 else 1 // we cannot use the value here } -/** - * Type of result used if the actor invocation does not return value. - */ -object VoidResult : Result() { - override val wasSuspended get() = false - override fun toString() = wasSuspendedPrefix + VOID -} - -object SuspendedVoidResult : Result() { - override val wasSuspended get() = true - override fun toString() = wasSuspendedPrefix + VOID -} - -private const val VOID = "void" - -object Cancelled : Result() { - override val wasSuspended get() = true - override fun toString() = wasSuspendedPrefix + "CANCELLED" -} - /** * Type of result used if the actor invocation fails with the specified in {@link Operation#handleExceptionsAsResult()} exception [tClazz]. */ @@ -111,31 +73,7 @@ data class ExceptionResult private constructor(val tClazz: Class, fun create(tClazz: Class, wasSuspended: Boolean = false) = ExceptionResult(tClazz.normalize(), wasSuspended) } } + // for byte-code generation @JvmSynthetic -fun createExceptionResult(tClazz: Class) = ExceptionResult.create(tClazz, false) - -/** - * Type of result used if the actor invocation suspended the thread and did not get the final result yet - * though it can be resumed later - */ -object NoResult : Result() { - override val wasSuspended get() = false - override fun toString() = "-" -} - -object Suspended : Result() { - override val wasSuspended get() = true - override fun toString() = "S" -} - -/** - * Type of result used for verification. - * Resuming thread writes result of the suspension point and continuation to be executed in the resumed thread into [contWithSuspensionPointRes]. - */ -internal data class ResumedResult(val contWithSuspensionPointRes: Pair?, kotlin.Result>) : Result() { - override val wasSuspended: Boolean get() = true - - lateinit var resumedActor: Actor - lateinit var by: Actor -} \ No newline at end of file +fun createExceptionResult(tClazz: Class) = ExceptionResult.create(tClazz, false) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 52d0a16cc..be5f08cee 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -36,6 +36,7 @@ import java.lang.reflect.Method import java.util.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* +import kotlin.reflect.* import kotlin.reflect.full.* import kotlin.reflect.jvm.* @@ -63,7 +64,7 @@ internal fun executeActor( } catch (invE: Throwable) { val eClass = (invE.cause ?: invE).javaClass.normalize() for (ec in actor.handledExceptions) { - if (ec.exceptionClass.isAssignableFrom(eClass)) + if (ec.isAssignableFrom(eClass)) return ExceptionResult.create(eClass) } throw IllegalStateException("Invalid exception as a result of $actor", invE) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index 2b23ff443..f25a1da55 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -1,29 +1,27 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.paramgen.* +import java.lang.reflect.* import kotlin.random.* import kotlin.reflect.KClass @@ -31,10 +29,10 @@ import kotlin.reflect.KClass * Implementations of this class generate [actors][Actor] * using [parameter generators][ParameterGenerator]. */ -class ActorGenerator( +actual class ActorGenerator( private val method: Method, private val parameterGenerators: List>, - private val handledExceptions: List, + private val handledExceptions: List>, val useOnce: Boolean, cancellableOnSuspension: Boolean, private val allowExtraSuspension: Boolean, @@ -63,8 +61,6 @@ class ActorGenerator( ) } - val isSuspendable: Boolean get() = method.isSuspendable() - override fun toString() = method.toString() + actual val isSuspendable: Boolean get() = method.isSuspendable() + actual override fun toString() = method.toString() } - -private val DETERMINISTIC_RANDOM = Random(42) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt index 132c13f28..33386553c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt @@ -1,7 +1,7 @@ /* * Lincheck * - * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * Copyright (C) 2019 - 2021 JetBrains s.r.o. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as @@ -20,53 +20,9 @@ package org.jetbrains.kotlinx.lincheck.execution -import org.jetbrains.kotlinx.lincheck.Actor -import org.jetbrains.kotlinx.lincheck.appendExecutionScenario -import org.jetbrains.kotlinx.lincheck.strategy.Strategy +import org.jetbrains.kotlinx.lincheck.* /** - * This class represents an execution scenario, which - * is generated by an [ExecutionGenerator] and then - * used by a [Strategy] which produces an [ExecutionResult]. + * Returns `true` if there is at least one suspendable actor in the generated scenario */ -class ExecutionScenario( - /** - * The initial sequential part of the execution. - * It helps to produce different initial states - * before the parallel part. - * - * The initial execution part should contain only non-suspendable actors; - * otherwise, the single initial execution thread will suspend with no chance to be resumed. - */ - val initExecution: List, - /** - * The parallel part of the execution, which is used - * to find an interleaving with incorrect behaviour. - */ - val parallelExecution: List>, - /** - * The last sequential part is used to test that - * the data structure is in some correct state. - * - * If this execution scenario contains suspendable actors, the post part should be empty; - * if not, an actor could resume a previously suspended one from the parallel execution part. - */ - val postExecution: List -) { - /** - * Returns the number of threads used in the parallel part of this execution. - */ - val threads: Int - get() = parallelExecution.size - - /** - * Returns `true` if there is at least one suspendable actor in the generated scenario - */ - fun hasSuspendableActors() = parallelExecution.any { actors -> actors.any { it.isSuspendable } } || postExecution.any { it.isSuspendable } - - override fun toString(): String { - val sb = StringBuilder() - sb.appendExecutionScenario(this) - return sb.toString() - } -} \ No newline at end of file +fun ExecutionScenario.hasSuspendableActors() = parallelExecution.any { actors -> actors.any { it.isSuspendable } } || postExecution.any { it.isSuspendable } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt index bc2f9e70f..4d15f5258 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt @@ -21,10 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck.execution -import org.jetbrains.kotlinx.lincheck.Actor -import org.jetbrains.kotlinx.lincheck.CTestConfiguration -import org.jetbrains.kotlinx.lincheck.CTestStructure -import org.jetbrains.kotlinx.lincheck.CTestStructure.OperationGroup +import org.jetbrains.kotlinx.lincheck.* import kotlin.random.Random class RandomExecutionGenerator(testConfiguration: CTestConfiguration, testStructure: CTestStructure) : ExecutionGenerator(testConfiguration, testStructure) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 57b365167..5b990f814 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -24,7 +24,6 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.CancellationResult.* -import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.FixedActiveThreadsExecutor.TestThread import org.jetbrains.kotlinx.lincheck.runner.UseClocks.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index 41ac490ee..48b270bb3 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -22,12 +22,12 @@ package org.jetbrains.kotlinx.lincheck.runner import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.strategy.* import org.objectweb.asm.* import java.lang.reflect.* import java.util.concurrent.atomic.* import org.jetbrains.kotlinx.lincheck.annotations.StateRepresentation +import org.jetbrains.kotlinx.lincheck.execution.* import java.io.* /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java index 8acfd834a..51e6211a0 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java @@ -191,8 +191,8 @@ private static void generateRun(ClassVisitor cv, Type testType, int iThread, Lis Label actorCatchBlockEnd = mv.newLabel(); if (actor.getHandlesExceptions()) { handledExceptionHandler = mv.newLabel(); - for (HandledException ec : actor.getHandledExceptions()) - mv.visitTryCatchBlock(actorCatchBlockStart, actorCatchBlockEnd, handledExceptionHandler, getType(ec.getExceptionClass()).getInternalName()); + for (Class ec : actor.getHandledExceptions()) + mv.visitTryCatchBlock(actorCatchBlockStart, actorCatchBlockEnd, handledExceptionHandler, getType(ec).getInternalName()); } // Catch those exceptions that has not been caught yet Label unexpectedExceptionHandler = mv.newLabel(); diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt index 384836aa8..a3a6b9a42 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -23,7 +23,6 @@ package org.jetbrains.kotlinx.lincheck.strategy import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.commons.Remapper /** * Implementation of this class describes how to run the generated execution. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt index df577ae9c..f3ee8ad90 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt @@ -21,10 +21,10 @@ */ package org.jetbrains.kotlinx.lincheck.strategy.managed -import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.reflect.* /** * A common configuration for managed strategies. @@ -32,7 +32,7 @@ import org.jetbrains.kotlinx.lincheck.verifier.* abstract class ManagedCTestConfiguration( testClass: Class<*>, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: Class, verifierClass: Class, + generatorClass: KClass, verifierClass: KClass, val checkObstructionFreedom: Boolean, val hangingDetectionThreshold: Int, val invocationsPerIteration: Int, val guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, sequentialSpecification: Class<*>?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 67fe2a269..de3c70d88 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -23,7 +23,6 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.CancellationResult.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt index 492fe9af3..ed829f5e3 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt @@ -22,10 +22,7 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult -import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario -import org.jetbrains.kotlinx.lincheck.execution.parallelResults -import org.jetbrains.kotlinx.lincheck.printInColumnsCustom +import org.jetbrains.kotlinx.lincheck.execution.* import java.util.* import kotlin.math.min diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index 9c0673ebc..a9ab7841e 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -26,12 +26,13 @@ import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* import org.jetbrains.kotlinx.lincheck.verifier.* import java.lang.reflect.* +import kotlin.reflect.* /** * Configuration for [random search][ModelCheckingStrategy] strategy. */ class ModelCheckingCTestConfiguration(testClass: Class<*>, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, - actorsAfter: Int, generatorClass: Class, verifierClass: Class, + actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, invocationsPerIteration: Int, guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, sequentialSpecification: Class<*>?, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt index 9da818ce9..1073ceff0 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt @@ -44,12 +44,12 @@ import kotlin.random.* * than the number of all possible interleavings on the current depth level. */ internal class ModelCheckingStrategy( - testCfg: ModelCheckingCTestConfiguration, - testClass: Class<*>, - scenario: ExecutionScenario, - validationFunctions: List, - stateRepresentation: Method?, - verifier: Verifier + testCfg: ModelCheckingCTestConfiguration, + testClass: Class<*>, + scenario: ExecutionScenario, + validationFunctions: List, + stateRepresentation: Method?, + verifier: Verifier ) : ManagedStrategy(testClass, scenario, verifier, validationFunctions, stateRepresentation, testCfg) { // The number of invocations that the strategy is eligible to use to search for an incorrect execution. private val maxInvocations = testCfg.invocationsPerIteration diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index d58b94de0..f557f4b01 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -22,16 +22,16 @@ package org.jetbrains.kotlinx.lincheck.strategy.stress import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.Method import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import java.lang.reflect.* +import kotlin.reflect.* /** * Configuration for [stress][StressStrategy] strategy. */ class StressCTestConfiguration(testClass: Class<*>, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: Class, verifierClass: Class, + generatorClass: KClass, verifierClass: KClass, val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, sequentialSpecification: Class<*>?, timeoutMs: Long ) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt index da4baf1f9..199d4d7a3 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt @@ -23,7 +23,6 @@ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* -import java.util.ArrayList /** * An abstraction for verifiers which use the labeled transition system (LTS) under the hood. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 33b679d2e..4564d7829 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -47,7 +47,8 @@ class QuiescentConsistencyVerifier(sequentialSpecification: Class<*>) : Verifier return linearizabilityVerifier.verifyResults(convertedScenario, convertedResults) } - private val ExecutionScenario.converted: ExecutionScenario get() = scenarioMapping.computeIfAbsent(this) { + private val ExecutionScenario.converted: ExecutionScenario + get() = scenarioMapping.computeIfAbsent(this) { val parallelExecutionConverted = ArrayList>() repeat(threads) { parallelExecutionConverted.add(ArrayList()) diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java index 4386f2bbd..9a9a959ac 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java @@ -96,8 +96,8 @@ public void testActorExceptionHandling() throws Exception { asList( new Actor(ArrayDeque.class.getMethod("addLast", Object.class), asList(1)), new Actor(Queue.class.getMethod("remove"), emptyList()), - new Actor(Queue.class.getMethod("remove"), emptyList(), asList(new HandledException(NoSuchElementException.class))), - new Actor(Queue.class.getMethod("remove"), emptyList(), asList(new HandledException(Exception.class), new HandledException((NoSuchElementException.class)))) + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(NoSuchElementException.class)), + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(Exception.class, (NoSuchElementException.class))) ), emptyList(), false); ex.testInstance = new ArrayDeque<>(); ex.results = new Result[4]; diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt index 0868a3692..c9f687099 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt @@ -74,7 +74,7 @@ fun actor(function: KFunction<*>, vararg args: Any?, cancelOnSuspension: Boolean return Actor( method = method, arguments = args.toList(), - handledExceptions = (method.exceptionTypes as Array>).toList().map { HandledException(it) }, + handledExceptions = (method.exceptionTypes as Array>).toList(), cancelOnSuspension = cancelOnSuspension ) } From de4f0dda3e2858769627b2ac7a42e31482cca7cb Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 9 Jan 2021 03:45:55 +0300 Subject: [PATCH 07/72] Move some parts(#2) to common --- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 1 + .../kotlinx/lincheck/CTestConfiguration.kt | 9 ++ .../kotlinx/lincheck/CTestStructure.kt | 5 +- .../lincheck/execution/ActorGenerator.kt | 3 + .../lincheck/execution/ExecutionResult.kt | 1 + .../kotlinx/lincheck/execution/HBClock.kt | 19 ++- .../execution/RandomExecutionGenerator.kt | 38 +++--- .../kotlinx/lincheck/paramgen/BooleanGen.kt | 2 +- .../kotlinx/lincheck/paramgen/ByteGen.kt | 10 +- .../kotlinx/lincheck/paramgen/DoubleGen.kt | 38 +++--- .../kotlinx/lincheck/paramgen/FloatGen.kt | 10 +- .../kotlinx/lincheck/paramgen/IntGen.kt | 38 +++--- .../kotlinx/lincheck/paramgen/LongGen.kt | 10 +- .../kotlinx/lincheck/paramgen/ShortGen.kt | 54 ++++++++ .../kotlinx/lincheck/paramgen/StringGen.kt | 42 +++---- .../kotlinx/lincheck/paramgen/ThreadIdGen.kt | 16 ++- .../lincheck/runner/InvocationResult.kt | 61 +++++++++ .../kotlinx/lincheck/runner/Runner.kt | 116 ++++++++++++++++++ .../lincheck/strategy/LincheckFailure.kt} | 22 ++-- .../kotlinx/lincheck/strategy/Strategy.kt | 40 ++++++ .../org/jetbrains/kotlinx/lincheck/Actor.kt | 2 +- .../kotlinx/lincheck/CTestConfiguration.kt | 12 +- .../kotlinx/lincheck/CTestStructure.kt | 4 +- .../lincheck/execution/ActorGenerator.kt | 6 +- .../lincheck/runner/InvocationResult.kt | 53 +------- .../lincheck/runner/ParallelThreadsRunner.kt | 1 + .../kotlinx/lincheck/runner/Runner.kt | 30 ++--- .../lincheck/strategy/LincheckFailure.kt | 4 +- .../kotlinx/lincheck/strategy/Strategy.kt | 8 +- 29 files changed, 438 insertions(+), 217 deletions(-) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt (99%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt (90%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt (85%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/BooleanGen.kt (95%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt (89%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt (69%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt (88%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt (62%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt (88%) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt (60%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/paramgen/ThreadIdGen.kt (89%) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt rename src/{jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt => common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt} (63%) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 1842fd4cd..3a7686807 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -29,5 +29,6 @@ import org.jetbrains.kotlinx.lincheck.annotations.Operation * @see Operation */ expect class Actor { + val isSuspendable: Boolean override fun toString(): String } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 95fe4bab7..18299251b 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -1,7 +1,16 @@ package org.jetbrains.kotlinx.lincheck +import org.jetbrains.kotlinx.lincheck.execution.* +import kotlin.reflect.* + /** * Abstract configuration for different lincheck modes. */ expect abstract class CTestConfiguration { + val iterations: Int + val threads: Int + val actorsPerThread: Int + val actorsBefore: Int + val actorsAfter: Int + val generatorClass: KClass } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index b780846a6..cd4e26e17 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -27,7 +27,10 @@ import org.jetbrains.kotlinx.lincheck.execution.* * Several [tests][StressCTest] can refer to one structure * (i.e. one test class could have several [StressCTest] annotations) */ -expect class CTestStructure +expect class CTestStructure { + val actorGenerators: List + val operationGroups: List +} class OperationGroup(val name: String, val nonParallel: Boolean) { val actors: MutableList diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index 31415159a..9827b524e 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -1,10 +1,13 @@ package org.jetbrains.kotlinx.lincheck.execution +import org.jetbrains.kotlinx.lincheck.* import kotlin.random.* expect class ActorGenerator { override fun toString(): String val isSuspendable: Boolean + val useOnce: Boolean + fun generate(threadId: Int): Actor } internal val DETERMINISTIC_RANDOM = Random(42) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt similarity index 99% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt index ddcc5da87..bafc5f432 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt @@ -19,6 +19,7 @@ * . * #L% */ + package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt similarity index 90% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt index 9f8bb31ef..8a4a0366c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt @@ -1,24 +1,23 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ + package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.Result @@ -34,7 +33,7 @@ data class HBClock(val clock: IntArray) { override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false + if (other != null && this::class != other::class) return false other as HBClock return clock.contentEquals(other.clock) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt similarity index 85% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt index 4d15f5258..d9e2b17db 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt @@ -1,24 +1,22 @@ /* -* #%L -* Lincheck -* %% -* Copyright (C) 2015 - 2018 Devexperts, LLC -* %% -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Lesser Public License for more details. -* -* You should have received a copy of the GNU General Lesser Public -* License along with this program. If not, see -* . -* #L% -*/ + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/BooleanGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/BooleanGen.kt similarity index 95% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/BooleanGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/BooleanGen.kt index 5ed2a13c6..81255f888 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/BooleanGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/BooleanGen.kt @@ -1,7 +1,7 @@ /* * Lincheck * - * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * Copyright (C) 2019 - 2021 JetBrains s.r.o. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt similarity index 89% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt index dba8bd6d0..42019099b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ByteGen.kt @@ -1,9 +1,8 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,8 +15,7 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.paramgen diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt similarity index 69% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt index bb978a87d..743cdaa5c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/DoubleGen.kt @@ -1,24 +1,22 @@ /* -* #%L -* Lincheck -* %% -* Copyright (C) 2015 - 2018 Devexperts, LLC -* %% -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Lesser Public License for more details. -* -* You should have received a copy of the GNU General Lesser Public -* License along with this program. If not, see -* . -* #L% -*/ + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ package org.jetbrains.kotlinx.lincheck.paramgen import kotlin.random.Random diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt similarity index 88% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt index 83a23b7e2..124c039d2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/FloatGen.kt @@ -1,9 +1,8 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,8 +15,7 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.paramgen diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt similarity index 62% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt index 15a4f9260..d27e7eb3a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/IntGen.kt @@ -1,24 +1,22 @@ /* -* #%L -* Lincheck -* %% -* Copyright (C) 2015 - 2018 Devexperts, LLC -* %% -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Lesser Public License for more details. -* -* You should have received a copy of the GNU General Lesser Public -* License along with this program. If not, see -* . -* #L% -*/ + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ package org.jetbrains.kotlinx.lincheck.paramgen import kotlin.random.Random diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt similarity index 88% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt index 824ace562..d2d7719b3 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/LongGen.kt @@ -1,9 +1,8 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,8 +15,7 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.paramgen diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt new file mode 100644 index 000000000..ac70e154d --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt @@ -0,0 +1,54 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.paramgen + +/* + * #%L + * Lincheck + * %% + * Copyright (C) 2015 - 2018 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +class ShortGen(configuration: String) : ParameterGenerator { + private val intGen: IntGen = IntGen(configuration) + + init { + intGen.checkRange(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt(), "short") + } + + override fun generate(): Short { + return intGen.generate().toShort() + } +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt similarity index 60% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt index 92840c27d..181daaa60 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/StringGen.kt @@ -1,27 +1,25 @@ /* -* #%L -* Lincheck -* %% -* Copyright (C) 2015 - 2018 Devexperts, LLC -* %% -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Lesser Public License for more details. -* -* You should have received a copy of the GNU General Lesser Public -* License along with this program. If not, see -* . -* #L% -*/ + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ package org.jetbrains.kotlinx.lincheck.paramgen -import java.util.* +import kotlin.random.* class StringGen(configuration: String) : ParameterGenerator { private val random = Random(0) @@ -47,7 +45,7 @@ class StringGen(configuration: String) : ParameterGenerator { override fun generate(): String { val cs = CharArray(random.nextInt(maxWordLength)) for (i in cs.indices) cs[i] = alphabet!![random.nextInt(alphabet!!.length)] - return String(cs) + return cs.concatToString() } } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ThreadIdGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ThreadIdGen.kt similarity index 89% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ThreadIdGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ThreadIdGen.kt index 9745350c7..b748dce4c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ThreadIdGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ThreadIdGen.kt @@ -1,23 +1,21 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.paramgen diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt new file mode 100644 index 000000000..b89b5e4cd --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt @@ -0,0 +1,61 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import org.jetbrains.kotlinx.lincheck.execution.* + +/** + * Represents results for invocations, see [Runner.run]. + */ +open class InvocationResult + +/** + * The invocation completed successfully, the output [results] are provided. + */ +class CompletedInvocationResult( + val results: ExecutionResult +) : InvocationResult() + +/** + * The invocation has completed with an unexpected exception. + */ +class UnexpectedExceptionInvocationResult( + val exception: Throwable +) : InvocationResult() + +/** + * The invocation successfully completed, but the + * [validation function][org.jetbrains.kotlinx.lincheck.annotations.Validate] + * check failed. + */ +class ValidationFailureInvocationResult( + val scenario: ExecutionScenario, + val functionName: String, + val exception: Throwable +) : InvocationResult() + +/** + * Obstruction freedom check is requested, + * but an invocation that hangs has been found. + */ +class ObstructionFreedomViolationInvocationResult( + val reason: String +) : InvocationResult() \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt new file mode 100644 index 000000000..930b45a62 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -0,0 +1,116 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import org.jetbrains.kotlinx.lincheck.execution.* + +/** + * Runner determines how to run your concurrent test. In order to support techniques + * like fibers, it may require code transformation, so that [createTransformer] should + * provide the corresponding transformer and [needsTransformation] should return `true`. + */ +expect abstract class Runner { + protected var scenario: ExecutionScenario + + /** + * This method is a part of `Runner` initialization and should be invoked after this runner + * creation. It is separated from the constructor to perform the strategy initialization at first. + */ + open fun initialize() + + /** + * Returns the current state representation of the test instance constructed via + * the function marked with [StateRepresentation] annotation, or `null` + * if no such function is provided. + * + * Please note, that it is unsafe to call this method concurrently with the running scenario. + * However, it is fine to call it if the execution is paused somewhere in the middle. + */ + open fun constructStateRepresentation(): String? + + /** + * This method should return `true` if code transformation + * is required for this runner; returns `false` by default. + */ + open fun needsTransformation(): Boolean + + /** + * This method is invoked by every test thread as the first operation. + * @param iThread number of invoking thread + */ + open fun onStart(iThread: Int) + + /** + * This method is invoked by every test thread as the last operation + * if no exception has been thrown. + * @param iThread number of invoking thread + */ + open fun onFinish(iThread: Int) + + /** + * This method is invoked by the corresponding test thread + * when an unexpected exception is thrown. + */ + open fun onFailure(iThread: Int, e: Throwable) + + /** + * This method is invoked by the corresponding test thread + * when the current coroutine suspends. + * @param iThread number of invoking thread + */ + open fun afterCoroutineSuspended(iThread: Int) + + /** + * This method is invoked by the corresponding test thread + * when the current coroutine is resumed. + */ + open fun afterCoroutineResumed(iThread: Int) + + /** + * This method is invoked by the corresponding test thread + * when the current coroutine is cancelled. + */ + open fun afterCoroutineCancelled(iThread: Int) + + /** + * Returns `true` if the coroutine corresponding to + * the actor `actorId` in the thread `iThread` is resumed. + */ + open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean + + /** + * Is invoked before each actor execution from the specified thread. + * The invocations are inserted into the generated code. + */ + fun onActorStart(iThread: Int) + + /** + * Closes the resources used in this runner. + */ + fun close() + + /** + * @return whether all scenario threads are completed or suspended + * Used by generated code. + */ + val isParallelExecutionCompleted: Boolean + +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt similarity index 63% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt index b75a7f091..1ed19aa3b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/paramgen/ShortGen.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt @@ -1,34 +1,26 @@ -package org.jetbrains.kotlinx.lincheck.paramgen - -/* +/*- * #%L * Lincheck * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC + * Copyright (C) 2019 - 2020 JetBrains s.r.o. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ -class ShortGen(configuration: String) : ParameterGenerator { - private val intGen: IntGen = IntGen(configuration) - - init { - intGen.checkRange(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt(), "short") - } +package org.jetbrains.kotlinx.lincheck.strategy - override fun generate(): Short { - return intGen.generate().toShort() - } +expect open class LincheckFailure{ + override fun toString(): String } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt new file mode 100644 index 000000000..f9e805d15 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -0,0 +1,40 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.strategy + +import org.jetbrains.kotlinx.lincheck.execution.* + +/** + * Implementation of this class describes how to run the generated execution. + * + * Note that strategy can run execution several times. For strategy creating + * [.createStrategy] method is used. It is impossible to add a new strategy + * without any code change. + */ +expect abstract class Strategy protected constructor(scenario: ExecutionScenario) { + abstract fun run(): LincheckFailure? + + /** + * Is invoked before each actor execution. + */ + open fun onActorStart(iThread: Int) + val scenario: ExecutionScenario +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 356725db9..cc6404d38 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -42,7 +42,7 @@ actual data class Actor @JvmOverloads constructor( val promptCancellation: Boolean = false, // we have to specify `isSuspendable` property explicitly for transformed classes since // `isSuspendable` implementation produces a circular dependency and, therefore, fails. - val isSuspendable: Boolean = method.isSuspendable() + actual val isSuspendable: Boolean = method.isSuspendable() ) { init { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 2d9a0295c..ae0b374b9 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -39,12 +39,12 @@ import kotlin.reflect.* */ actual abstract class CTestConfiguration( val testClass: Class<*>, - val iterations: Int, - val threads: Int, - val actorsPerThread: Int, - val actorsBefore: Int, - val actorsAfter: Int, - val generatorClass: KClass, + actual val iterations: Int, + actual val threads: Int, + actual val actorsPerThread: Int, + actual val actorsBefore: Int, + actual val actorsAfter: Int, + actual val generatorClass: KClass, val verifierClass: KClass, val requireStateEquivalenceImplCheck: Boolean, val minimizeFailedScenario: Boolean, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index f123f1b8f..1ed9394fe 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -37,8 +37,8 @@ import kotlin.collections.HashMap * (i.e. one test class could have several [StressCTest] annotations) */ actual class CTestStructure private constructor( - val actorGenerators: List, - val operationGroups: List, + actual val actorGenerators: List, + actual val operationGroups: List, val validationFunctions: List, val stateRepresentation: Method? ) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index f25a1da55..76ce97019 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -22,8 +22,6 @@ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.paramgen.* import java.lang.reflect.* -import kotlin.random.* -import kotlin.reflect.KClass /** * Implementations of this class generate [actors][Actor] @@ -33,7 +31,7 @@ actual class ActorGenerator( private val method: Method, private val parameterGenerators: List>, private val handledExceptions: List>, - val useOnce: Boolean, + actual val useOnce: Boolean, cancellableOnSuspension: Boolean, private val allowExtraSuspension: Boolean, private val blocking: Boolean, @@ -43,7 +41,7 @@ actual class ActorGenerator( private val cancellableOnSuspension = cancellableOnSuspension && isSuspendable private val promptCancellation = cancellableOnSuspension && promptCancellation - fun generate(threadId: Int): Actor { + actual fun generate(threadId: Int): Actor { val parameters = parameterGenerators .map { it.generate() } .map { if (it === THREAD_ID_TOKEN) threadId else it } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt index b572f4e43..00188f78c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt @@ -1,9 +1,8 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,54 +15,14 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% - */ -package org.jetbrains.kotlinx.lincheck.runner - -import org.jetbrains.kotlinx.lincheck.execution.* - -/** - * Represents results for invocations, see [Runner.run]. + * */ -sealed class InvocationResult -/** - * The invocation completed successfully, the output [results] are provided. - */ -class CompletedInvocationResult( - val results: ExecutionResult -) : InvocationResult() +package org.jetbrains.kotlinx.lincheck.runner /** * Indicates that the invocation has run into deadlock or livelock. */ class DeadlockInvocationResult( val threadDump: Map> -) : InvocationResult() - -/** - * The invocation has completed with an unexpected exception. - */ -class UnexpectedExceptionInvocationResult( - val exception: Throwable -) : InvocationResult() - -/** - * The invocation successfully completed, but the - * [validation function][org.jetbrains.kotlinx.lincheck.annotations.Validate] - * check failed. - */ -class ValidationFailureInvocationResult( - val scenario: ExecutionScenario, - val functionName: String, - val exception: Throwable -) : InvocationResult() - -/** - * Obstruction freedom check is requested, - * but an invocation that hangs has been found. - */ -class ObstructionFreedomViolationInvocationResult( - val reason: String ) : InvocationResult() \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 5b990f814..3c254e470 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -19,6 +19,7 @@ * . * #L% */ + package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index 48b270bb3..41fdb697a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -35,13 +35,13 @@ import java.io.* * like fibers, it may require code transformation, so that [createTransformer] should * provide the corresponding transformer and [needsTransformation] should return `true`. */ -abstract class Runner protected constructor( +actual abstract class Runner protected constructor( protected val strategy: Strategy, private val _testClass: Class<*>, // will be transformed later protected val validationFunctions: List, protected val stateRepresentationFunction: Method? ) : Closeable { - protected var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` + protected actual var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` protected lateinit var testClass: Class<*> // not available before `initialize` call @Suppress("LeakingThis") val classLoader: ExecutionClassLoader = if (needsTransformation() || strategy.needsTransformation()) TransformationClassLoader(strategy, this) @@ -52,7 +52,7 @@ abstract class Runner protected constructor( * This method is a part of `Runner` initialization and should be invoked after this runner * creation. It is separated from the constructor to perform the strategy initialization at first. */ - open fun initialize() { + actual open fun initialize() { scenario = strategy.scenario.convertForLoader(classLoader) testClass = loadClass(_testClass.typeName) } @@ -65,7 +65,7 @@ abstract class Runner protected constructor( * Please note, that it is unsafe to call this method concurrently with the running scenario. * However, it is fine to call it if the execution is paused somewhere in the middle. */ - open fun constructStateRepresentation(): String? = null + actual open fun constructStateRepresentation(): String? = null /** * Loads the specified class via this runner' class loader. @@ -84,7 +84,7 @@ abstract class Runner protected constructor( * This method should return `true` if code transformation * is required for this runner; returns `false` by default. */ - open fun needsTransformation(): Boolean = false + actual open fun needsTransformation(): Boolean = false /** * Runs the next invocation. @@ -95,63 +95,63 @@ abstract class Runner protected constructor( * This method is invoked by every test thread as the first operation. * @param iThread number of invoking thread */ - open fun onStart(iThread: Int) {} + actual open fun onStart(iThread: Int) {} /** * This method is invoked by every test thread as the last operation * if no exception has been thrown. * @param iThread number of invoking thread */ - open fun onFinish(iThread: Int) {} + actual open fun onFinish(iThread: Int) {} /** * This method is invoked by the corresponding test thread * when an unexpected exception is thrown. */ - open fun onFailure(iThread: Int, e: Throwable) {} + actual open fun onFailure(iThread: Int, e: Throwable) {} /** * This method is invoked by the corresponding test thread * when the current coroutine suspends. * @param iThread number of invoking thread */ - open fun afterCoroutineSuspended(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + actual open fun afterCoroutineSuspended(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") /** * This method is invoked by the corresponding test thread * when the current coroutine is resumed. */ - open fun afterCoroutineResumed(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + actual open fun afterCoroutineResumed(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") /** * This method is invoked by the corresponding test thread * when the current coroutine is cancelled. */ - open fun afterCoroutineCancelled(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + actual open fun afterCoroutineCancelled(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") /** * Returns `true` if the coroutine corresponding to * the actor `actorId` in the thread `iThread` is resumed. */ - open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean = throw UnsupportedOperationException("Coroutines are not supported") + actual open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean = throw UnsupportedOperationException("Coroutines are not supported") /** * Is invoked before each actor execution from the specified thread. * The invocations are inserted into the generated code. */ - fun onActorStart(iThread: Int) { + actual fun onActorStart(iThread: Int) { strategy.onActorStart(iThread) } /** * Closes the resources used in this runner. */ - override fun close() {} + actual override fun close() {} /** * @return whether all scenario threads are completed or suspended * Used by generated code. */ - val isParallelExecutionCompleted: Boolean + actual val isParallelExecutionCompleted: Boolean get() = completedOrSuspendedThreads.get() == scenario.threads } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt index 3248c2c09..b442ce85d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt @@ -26,11 +26,11 @@ import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* -sealed class LincheckFailure( +actual open class LincheckFailure( val scenario: ExecutionScenario, val trace: Trace? ) { - override fun toString() = StringBuilder().appendFailure(this).toString() + actual override fun toString() = StringBuilder().appendFailure(this).toString() } internal class IncorrectResultsFailure( diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt index a3a6b9a42..681b339f1 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -31,18 +31,18 @@ import org.objectweb.asm.ClassVisitor * [.createStrategy] method is used. It is impossible to add a new strategy * without any code change. */ -abstract class Strategy protected constructor( - val scenario: ExecutionScenario +actual abstract class Strategy protected actual constructor( + actual val scenario: ExecutionScenario ) { open fun needsTransformation() = false open fun createTransformer(cv: ClassVisitor): ClassVisitor { throw UnsupportedOperationException("$javaClass strategy does not transform classes") } - abstract fun run(): LincheckFailure? + actual abstract fun run(): LincheckFailure? /** * Is invoked before each actor execution. */ - open fun onActorStart(iThread: Int) {} + actual open fun onActorStart(iThread: Int) {} } From 39c055e98e125bfc9156972b7c9a7c7c74fd96d3 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 9 Jan 2021 05:48:45 +0300 Subject: [PATCH 08/72] Move some parts to common(3), change sequentialSpecification internal type from Class to KClass --- .../lincheck/execution/ExecutionGenerator.kt | 38 +++++++++---------- .../kotlinx/lincheck/verifier/Verifier.kt | 11 +++++- .../lincheck/verifier/VerifierState.kt | 16 ++++---- .../kotlinx/lincheck/CTestConfiguration.kt | 6 +-- .../jetbrains/kotlinx/lincheck/JvmHacks.kt | 4 -- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 10 +++-- .../org/jetbrains/kotlinx/lincheck/Options.kt | 4 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 4 +- .../managed/ManagedCTestConfiguration.kt | 2 +- .../ModelCheckingCTestConfiguration.kt | 2 +- .../stress/StressCTestConfiguration.kt | 2 +- .../lincheck/verifier/AbstractLTSVerifier.kt | 3 +- .../lincheck/verifier/EpsilonVerifier.kt | 3 +- .../kotlinx/lincheck/verifier/LTS.kt | 10 +++-- .../verifier/SerializabilityVerifier.kt | 19 +++++----- .../LinearizabilityVerifier.kt | 3 +- .../quiescent/QuiescentConsistencyVerifier.kt | 6 ++- .../AFUCallRepresentationTest.kt | 1 - .../SuspendTraceReportingTest.kt | 1 - .../test/verifier/CustomScenarioDSL.kt | 4 +- 20 files changed, 79 insertions(+), 70 deletions(-) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt (90%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt (92%) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt index 1fded5302..51ca66a4e 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionGenerator.kt @@ -1,24 +1,22 @@ /* -* #%L -* Lincheck -* %% -* Copyright (C) 2015 - 2018 Devexperts, LLC -* %% -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Lesser Public License for more details. -* -* You should have received a copy of the GNU General Lesser Public -* License along with this program. If not, see -* . -* #L% -*/ + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.CTestConfiguration diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt similarity index 90% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt index 5caf971aa..fa2af5de7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt @@ -24,7 +24,6 @@ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario import kotlin.collections.HashSet -import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier /** * Implementation of this interface verifies that execution is correct with respect to the algorithm contract. @@ -50,6 +49,16 @@ interface Verifier { fun checkStateEquivalenceImplementation() } +internal inline fun Map.computeIfAbsent(key: K, defaultValue: (K) -> V): V { + val value = get(key) + if (value == null && !containsKey(key)) { + return defaultValue(key) + } else { + @Suppress("UNCHECKED_CAST") + return value as V + } +} + /** * This verifier cached the already verified results in a hash table, * and look into this hash table at first. In case of many invocations diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt similarity index 92% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt index b02bb7fec..5c19cf23d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/VerifierState.kt @@ -1,23 +1,21 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.verifier diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index ae0b374b9..1b8899916 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -48,7 +48,7 @@ actual abstract class CTestConfiguration( val verifierClass: KClass, val requireStateEquivalenceImplCheck: Boolean, val minimizeFailedScenario: Boolean, - val sequentialSpecification: Class<*>?, + val sequentialSpecification: KClass<*>?, val timeoutMs: Long ) { abstract fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, @@ -74,7 +74,7 @@ internal fun createFromTestClassAnnotations(testClass: Class<*>): List = testClass.getAnnotationsByType(ModelCheckingCTest::class.java) @@ -83,7 +83,7 @@ internal fun createFromTestClassAnnotations(testClass: Class<*>): List KClass.getConstructor(vararg args: KParameter) : KFunction { - return this.constructors.find { it.parameters == args.toList() }!! -} - fun KClass.getConstructor(vararg args: Class<*>) : Constructor { return this.java.getConstructor(*args) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 44d4b549a..4efc97e6d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -26,7 +26,10 @@ import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* +import java.lang.reflect.* import kotlin.reflect.* +import kotlin.reflect.KTypeProjection.Companion.STAR +import kotlin.reflect.full.* /** * This class runs concurrent tests. @@ -176,9 +179,10 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { private fun CTestConfiguration.createVerifier() = - verifierClass.getConstructor(Class::class.java).newInstance(sequentialSpecification).also { - if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() - } + verifierClass.constructors.find { it.parameters.size == 1 && it.parameters[0].type == KClass::class.createType(listOf(STAR)) }!! + .call(sequentialSpecification).also { + if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() + } private fun CTestConfiguration.createExecutionGenerator() = generatorClass.getConstructor( diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt index b6662f177..9c7097e8c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -40,7 +40,7 @@ abstract class Options, CTEST : CTestConfiguration> { protected var verifier = CTestConfiguration.DEFAULT_VERIFIER protected var requireStateEquivalenceImplementationCheck = true protected var minimizeFailedScenario = CTestConfiguration.DEFAULT_MINIMIZE_ERROR - protected var sequentialSpecification: Class<*>? = null + protected var sequentialSpecification: KClass<*>? = null protected var timeoutMs: Long = CTestConfiguration.DEFAULT_TIMEOUT_MS /** @@ -148,7 +148,7 @@ abstract class Options, CTEST : CTestConfiguration> { * By default, the provided concurrent implementation is used in a sequential way. */ fun sequentialSpecification(clazz: Class<*>?): OPT = applyAndCast { - sequentialSpecification = clazz + sequentialSpecification = clazz?.kotlin } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index be5f08cee..0e61e3df4 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -41,8 +41,8 @@ import kotlin.reflect.full.* import kotlin.reflect.jvm.* -fun chooseSequentialSpecification(sequentialSpecificationByUser: Class<*>?, testClass: Class<*>): Class<*> = - if (sequentialSpecificationByUser === DummySequentialSpecification::class.java || sequentialSpecificationByUser == null) testClass +fun chooseSequentialSpecification(sequentialSpecificationByUser: KClass<*>?, testClass: Class<*>): KClass<*> = + if (sequentialSpecificationByUser === DummySequentialSpecification::class || sequentialSpecificationByUser == null) testClass.kotlin else sequentialSpecificationByUser internal fun executeActor(testInstance: Any, actor: Actor) = executeActor(testInstance, actor, null) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt index f3ee8ad90..5be0de581 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt @@ -35,7 +35,7 @@ abstract class ManagedCTestConfiguration( generatorClass: KClass, verifierClass: KClass, val checkObstructionFreedom: Boolean, val hangingDetectionThreshold: Int, val invocationsPerIteration: Int, val guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: Class<*>?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean + sequentialSpecification: KClass<*>?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean ) : CTestConfiguration( testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index a9ab7841e..de2230eb2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -35,7 +35,7 @@ class ModelCheckingCTestConfiguration(testClass: Class<*>, iterations: Int, thre actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, invocationsPerIteration: Int, guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: Class<*>?, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean + sequentialSpecification: KClass<*>?, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean ) : ManagedCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs, eliminateLocalObjects, verboseTrace) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index f557f4b01..ad297aa9a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -33,7 +33,7 @@ import kotlin.reflect.* class StressCTestConfiguration(testClass: Class<*>, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: Class<*>?, timeoutMs: Long + sequentialSpecification: KClass<*>?, timeoutMs: Long ) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs) { override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt index 199d4d7a3..0d1253adf 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt @@ -23,6 +23,7 @@ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* +import kotlin.reflect.* /** * An abstraction for verifiers which use the labeled transition system (LTS) under the hood. @@ -33,7 +34,7 @@ import org.jetbrains.kotlinx.lincheck.execution.* * the next possible transitions using [VerifierContext.nextContext] function. This verifier * uses depth-first search to find a proper path. */ -abstract class AbstractLTSVerifier(protected val sequentialSpecification: Class<*>) : CachedVerifier() { +abstract class AbstractLTSVerifier(protected val sequentialSpecification: KClass<*>) : CachedVerifier() { abstract val lts: LTS abstract fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult): VerifierContext diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt index effe15a03..9fef72b70 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt @@ -23,11 +23,12 @@ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario +import kotlin.reflect.* /** * This verifier does nothing and could be used for performance benchmarking. */ -class EpsilonVerifier(sequentialSpecification: Class<*>) : Verifier { +class EpsilonVerifier(sequentialSpecification: KClass<*>) : Verifier { override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean = true // Always correct results :) override fun checkStateEquivalenceImplementation() {} diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index 6bfdb6715..93f07256b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -31,6 +31,8 @@ import java.util.* import kotlin.collections.HashMap import kotlin.coroutines.* import kotlin.math.* +import kotlin.reflect.* +import kotlin.reflect.full.* typealias RemappingFunction = IntArray typealias ResumedTickets = Set @@ -55,10 +57,10 @@ typealias ResumedTickets = Set * Practically, Kotlin implementation of such operations via suspend functions is supported. */ -class LTS(sequentialSpecification: Class<*>) { +class LTS(sequentialSpecification: KClass<*>) { // we should transform the specification with `CancellabilitySupportClassTransformer` - private val sequentialSpecification: Class<*> = TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv)} - .loadClass(sequentialSpecification.name)!! + private val sequentialSpecification: KClass<*> = TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv)} + .loadClass(sequentialSpecification.java.name)!!.kotlin /** * Cache with all LTS states in order to reuse the equivalent ones. @@ -279,7 +281,7 @@ class LTS(sequentialSpecification: Class<*>) { ).intern(null) { _, _ -> initialState } } - private fun createInitialStateInstance() = sequentialSpecification.newInstance() + private fun createInitialStateInstance() = sequentialSpecification.getConstructor().newInstance() fun checkStateEquivalenceImplementation() { val i1 = createInitialStateInstance() diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt index fb2f3bdf5..9ac2f9bd0 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt @@ -1,35 +1,34 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* +import kotlin.reflect.* /** * This verifier checks that the specified results could be happen in serializable execution. * It just tries to find any operations sequence which execution produces the same results. */ class SerializabilityVerifier( - sequentialSpecification: Class + sequentialSpecification: KClass ) : CachedVerifier() { private val linerizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt index 2d3e7beb7..685f55dc4 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt @@ -24,6 +24,7 @@ package org.jetbrains.kotlinx.lincheck.verifier.linearizability import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.reflect.* /** * This verifier checks that the specified results could happen if the testing operations are linearizable. @@ -34,7 +35,7 @@ import org.jetbrains.kotlinx.lincheck.verifier.* * This verifier is based on [AbstractLTSVerifier] and caches the already processed results * for performance improvement (see [CachedVerifier]). */ -class LinearizabilityVerifier(sequentialSpecification: Class<*>) : AbstractLTSVerifier(sequentialSpecification) { +class LinearizabilityVerifier(sequentialSpecification: KClass<*>) : AbstractLTSVerifier(sequentialSpecification) { override val lts: LTS = LTS(sequentialSpecification = sequentialSpecification) override fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult) = diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 4564d7829..7863be13f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -25,7 +25,9 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* +import java.util.* import kotlin.collections.ArrayList +import kotlin.reflect.* /** * This verifier tests for quiescent consistency. @@ -34,9 +36,9 @@ import kotlin.collections.ArrayList * However, we believe that quiescent points do not occur * in practice while supporting them complicates the implementation. */ -class QuiescentConsistencyVerifier(sequentialSpecification: Class<*>) : Verifier { +class QuiescentConsistencyVerifier(sequentialSpecification: KClass<*>) : Verifier { private val linearizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) - private val scenarioMapping: MutableMap = HashMap() + private val scenarioMapping: MutableMap = WeakHashMap() override fun checkStateEquivalenceImplementation() = linearizabilityVerifier.checkStateEquivalenceImplementation() diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt index 88631ea8a..4cc2d825b 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/AFUCallRepresentationTest.kt @@ -24,7 +24,6 @@ package org.jetbrains.kotlinx.lincheck.test.representation import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.test.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.junit.* diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/SuspendTraceReportingTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/SuspendTraceReportingTest.kt index 933484bdb..60caac2db 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/SuspendTraceReportingTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/SuspendTraceReportingTest.kt @@ -21,7 +21,6 @@ */ package org.jetbrains.kotlinx.lincheck.test.representation -import kotlinx.coroutines.* import kotlinx.coroutines.sync.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.Operation diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt index c9f687099..3b8ea35f8 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt @@ -25,7 +25,7 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import java.lang.IllegalStateException -import kotlin.reflect.KFunction +import kotlin.reflect.* import kotlin.reflect.jvm.javaMethod /** @@ -58,7 +58,7 @@ fun verify( correct: Boolean ) { val (scenario, results) = scenarioWithResults(block) - val verifier = verifierClass.getConstructor(Class::class.java).newInstance(testClass) + val verifier = verifierClass.getConstructor(KClass::class.java).newInstance(testClass.kotlin) val res = verifier.verifyResults(scenario, results) assert(res == correct) } From c60f93ca704f44ff7f8397e5ef93078310ebb951 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 9 Jan 2021 06:07:23 +0300 Subject: [PATCH 09/72] Fix tests, KClass -> KClass<*> --- .../main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt | 2 +- .../kotlinx/lincheck/verifier/SerializabilityVerifier.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt index fa2af5de7..df22015c2 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt @@ -31,7 +31,7 @@ import kotlin.collections.HashSet * * * IMPORTANT! - * All implementations should have `(Class sequentialSpecification)` constructor, + * All implementations should have `(sequentialSpecification: KClass<*>)` constructor, * which takes the scenario to be tested and the correct sequential implementation of the testing data structure. */ interface Verifier { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt index 9ac2f9bd0..4399134f5 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt @@ -28,7 +28,7 @@ import kotlin.reflect.* * It just tries to find any operations sequence which execution produces the same results. */ class SerializabilityVerifier( - sequentialSpecification: KClass + sequentialSpecification: KClass<*> ) : CachedVerifier() { private val linerizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) From 1a11040f4c6d13cc33c082777625bc0c9867b02d Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 19 Jan 2021 16:00:07 +0300 Subject: [PATCH 10/72] move ValueResult and ExceptionResult in common --- .../org/jetbrains/kotlinx/lincheck/Result.kt | 9 +++++++ .../jetbrains/kotlinx/lincheck/ValueResult.kt | 10 +++++++ .../org/jetbrains/kotlinx/lincheck/Result.kt | 26 +++++-------------- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 6 ++--- .../ParallelThreadsRunnerExceptionTest.kt | 6 ++--- .../runner/TestThreadExecutionHelperTest.java | 4 +-- 6 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt index 7c6bb3f07..6c86b20b8 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Result.kt @@ -23,6 +23,7 @@ package org.jetbrains.kotlinx.lincheck import kotlin.coroutines.* import kotlin.jvm.* +import kotlin.reflect.* /** * The instance of this class represents a result of actor invocation. @@ -40,6 +41,14 @@ abstract class Result { protected val wasSuspendedPrefix: String get() = (if (wasSuspended) "SUSPENDED + " else "") } +/** + * Type of result used if the actor invocation fails with the specified in {@link Operation#handleExceptionsAsResult()} exception [tClazz]. + */ +@Suppress("DataClassPrivateConstructor") +data class ExceptionResult internal constructor(val tClazz: KClass, override val wasSuspended: Boolean) : Result() { + override fun toString() = wasSuspendedPrefix + tClazz.simpleName +} + /** * Type of result used if the actor invocation does not return value. */ diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt new file mode 100644 index 000000000..bf891446b --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt @@ -0,0 +1,10 @@ +package org.jetbrains.kotlinx.lincheck + +/** + * Type of result used if the actor invocation returns any value. + */ +expect class ValueResult { + override fun equals(other: Any?): Boolean + override fun hashCode(): Int + val value: Any? +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt index 8495d9b35..7e770bd02 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Result.kt @@ -20,13 +20,13 @@ package org.jetbrains.kotlinx.lincheck +import org.jetbrains.kotlinx.lincheck.* import java.io.Serializable -import kotlin.coroutines.* /** * Type of result used if the actor invocation returns any value. */ -class ValueResult @JvmOverloads constructor(val value: Any?, override val wasSuspended: Boolean = false) : Result() { +actual class ValueResult @JvmOverloads constructor(actual val value: Any?, override val wasSuspended: Boolean = false) : Result() { private val valueClassTransformed: Boolean get() = value?.javaClass?.classLoader is TransformationClassLoader private val serializedObject: ByteArray by lazy(LazyThreadSafetyMode.NONE) { check(value is Serializable) { @@ -45,7 +45,7 @@ class ValueResult @JvmOverloads constructor(val value: Any?, override val wasSus override fun toString() = wasSuspendedPrefix + "$value" - override fun equals(other: Any?): Boolean { + actual override fun equals(other: Any?): Boolean { // Check that the classes are equal by names // since they can be loaded via different class loaders. if (javaClass.name != other?.javaClass?.name) return false @@ -57,23 +57,9 @@ class ValueResult @JvmOverloads constructor(val value: Any?, override val wasSus else serializedObject.contentEquals(other.serializedObject) } - override fun hashCode(): Int = if (wasSuspended) 0 else 1 // we cannot use the value here -} - -/** - * Type of result used if the actor invocation fails with the specified in {@link Operation#handleExceptionsAsResult()} exception [tClazz]. - */ -@Suppress("DataClassPrivateConstructor") -data class ExceptionResult private constructor(val tClazz: Class, override val wasSuspended: Boolean) : Result() { - override fun toString() = wasSuspendedPrefix + tClazz.simpleName - - companion object { - @Suppress("UNCHECKED_CAST") - @JvmOverloads - fun create(tClazz: Class, wasSuspended: Boolean = false) = ExceptionResult(tClazz.normalize(), wasSuspended) - } + actual override fun hashCode(): Int = if (wasSuspended) 0 else 1 // we cannot use the value here } // for byte-code generation -@JvmSynthetic -fun createExceptionResult(tClazz: Class) = ExceptionResult.create(tClazz, false) \ No newline at end of file +@JvmOverloads +fun createExceptionResult(tClazz: Class, wasSuspended: Boolean = false) = ExceptionResult(tClazz.normalize().kotlin, false) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 0e61e3df4..f5e24c16a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -65,7 +65,7 @@ internal fun executeActor( val eClass = (invE.cause ?: invE).javaClass.normalize() for (ec in actor.handledExceptions) { if (ec.isAssignableFrom(eClass)) - return ExceptionResult.create(eClass) + return createExceptionResult(eClass) } throw IllegalStateException("Invalid exception as a result of $actor", invE) } catch (e: Exception) { @@ -140,7 +140,7 @@ private fun Class.getMethod(name: String, parameterTypes: Array if (wasSuspended) SuspendedVoidResult else VoidResult - res != null && res is Throwable -> ExceptionResult.create(res.javaClass, wasSuspended) + res != null && res is Throwable -> createExceptionResult(res.javaClass, wasSuspended) res === COROUTINE_SUSPENDED -> Suspended res is kotlin.Result -> res.toLinCheckResult(wasSuspended) else -> ValueResult(res, wasSuspended) @@ -154,7 +154,7 @@ private fun kotlin.Result.toLinCheckResult(wasSuspended: Boolean) = is Throwable -> ValueResult(value::class.java, wasSuspended) else -> ValueResult(value, wasSuspended) } - } else ExceptionResult.create(exceptionOrNull()!!.let { it::class.java }, wasSuspended) + } else createExceptionResult(exceptionOrNull()!!.let { it::class.java }, wasSuspended) inline fun Throwable.catch(vararg exceptions: Class<*>, block: () -> R): R { if (exceptions.any { this::class.java.isAssignableFrom(it) }) { diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt index 2cdf28cb5..9927c438b 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt @@ -101,7 +101,7 @@ class ParallelThreadsRunnerExceptionTest { parallel { thread { operation( - actor(susWithoutException), ExceptionResult.create(SuspendResumeScenarios.TestException::class.java, wasSuspended = true) + actor(susWithoutException), createExceptionResult(SuspendResumeScenarios.TestException::class.java, wasSuspended = true) ) } thread { @@ -126,7 +126,7 @@ class ParallelThreadsRunnerExceptionTest { parallel { thread { operation( - actor(susResumeThrow), ExceptionResult.create(SuspendResumeScenarios.TestException::class.java, wasSuspended = true) + actor(susResumeThrow), createExceptionResult(SuspendResumeScenarios.TestException::class.java, wasSuspended = true) ) } thread { @@ -149,7 +149,7 @@ class ParallelThreadsRunnerExceptionTest { val (scenario, expectedResults) = scenarioWithResults { parallel { thread { - operation(actor(susThrow), ExceptionResult.create(SuspendResumeScenarios.TestException::class.java)) + operation(actor(susThrow), createExceptionResult(SuspendResumeScenarios.TestException::class.java)) } } } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java index 9a9a959ac..787399b16 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java @@ -108,8 +108,8 @@ public void testActorExceptionHandling() throws Exception { Assert.assertArrayEquals(new Result[]{ VoidResult.INSTANCE, new ValueResult(1), - ExceptionResult.Companion.create(NoSuchElementException.class), - ExceptionResult.Companion.create(NoSuchElementException.class) + ResultKt.createExceptionResult(NoSuchElementException.class), + ResultKt.createExceptionResult(NoSuchElementException.class) }, ex.results); } } \ No newline at end of file From 1f0f96855cb629da558cdf4e33e86fe0cc504c43 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 1 Feb 2021 16:07:51 +0300 Subject: [PATCH 11/72] Move LincheckFailure, InvocationResult, and Reporter to common --- .../kotlinx/lincheck/CommonReporter.kt | 159 ++++++++++++++++++ .../lincheck/runner/InvocationResult.kt | 9 + .../lincheck/strategy/LincheckFailure.kt | 52 +++++- .../jetbrains/kotlinx/lincheck/Reporter.kt | 145 +--------------- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 4 +- .../lincheck/runner/InvocationResult.kt | 7 +- .../lincheck/strategy/LincheckFailure.kt | 65 +------ .../lincheck/strategy/managed/TracePoint.kt | 2 - .../strategy/managed/TraceReporter.kt | 1 + .../test/representation/ThreadDumpTest.kt | 2 +- 10 files changed, 237 insertions(+), 209 deletions(-) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt new file mode 100644 index 000000000..3b2375365 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt @@ -0,0 +1,159 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.LoggingLevel.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import kotlin.jvm.* + +class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel) { + fun logIteration(iteration: Int, maxIterations: Int, scenario: ExecutionScenario) = log(INFO) { + appendLine("\n= Iteration $iteration / $maxIterations =") + appendExecutionScenario(scenario) + } + + fun logFailedIteration(failure: LincheckFailure) = log(INFO) { + appendFailure(failure) + } + + fun logScenarioMinimization(scenario: ExecutionScenario) = log(INFO) { + appendLine("\nInvalid interleaving found, trying to minimize the scenario below:") + appendExecutionScenario(scenario) + } + + private inline fun log(logLevel: LoggingLevel, crossinline msg: StringBuilder.() -> Unit): Unit { // TODO fix this with some mutex + if (this.logLevel > logLevel) return + val sb = StringBuilder() + msg(sb) + println(sb) + } +} + +@JvmField val DEFAULT_LOG_LEVEL = ERROR +enum class LoggingLevel { + INFO, ERROR +} + +private class ActorWithResult(val actorRepresentation: String, val spacesAfterActor: Int, + val resultRepresentation: String, val spacesAfterResult: Int, + val clockRepresentation: String) { + override fun toString(): String = + actorRepresentation + ":" + " ".repeat(spacesAfterActor) + resultRepresentation + + " ".repeat(spacesAfterResult) + clockRepresentation +} + +private fun uniteActorsAndResultsLinear(actors: List, results: List): List { + require(actors.size == results.size) { + "Different numbers of actors and matching results found (${actors.size} != ${results.size})" + } + return actors.indices.map { + ActorWithResult("${actors[it]}", 1, "${results[it]}", 0, "") + } +} + +private fun uniteParallelActorsAndResults(actors: List>, results: List>): List> { + require(actors.size == results.size) { + "Different numbers of threads and matching results found (${actors.size} != ${results.size})" + } + return actors.mapIndexed { id, threadActors -> uniteActorsAndResultsAligned(threadActors, results[id]) } +} + +private fun uniteActorsAndResultsAligned(actors: List, results: List): List { + require(actors.size == results.size) { + "Different numbers of actors and matching results found (${actors.size} != ${results.size})" + } + val actorRepresentations = actors.map { it.toString() } + val resultRepresentations = results.map { it.result.toString() } + val maxActorLength = actorRepresentations.map { it.length }.max()!! + val maxResultLength = resultRepresentations.map { it.length }.max()!! + return actors.indices.map { i -> + val actorRepr = actorRepresentations[i] + val resultRepr = resultRepresentations[i] + val clock = results[i].clockOnStart + val spacesAfterActor = maxActorLength - actorRepr.length + 1 + val spacesAfterResultToAlign = maxResultLength - resultRepr.length + if (clock.empty) { + ActorWithResult(actorRepr, spacesAfterActor, resultRepr, spacesAfterResultToAlign, "") + } else { + ActorWithResult(actorRepr, spacesAfterActor, resultRepr, spacesAfterResultToAlign + 1, clock.toString()) + } + } +} + +expect internal fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilder + +internal fun StringBuilder.appendUnexpectedExceptionFailure(failure: UnexpectedExceptionFailure): StringBuilder { + appendLine("= The execution failed with an unexpected exception =") + appendExecutionScenario(failure.scenario) + appendLine() + appendException(failure.exception) + return this +} + +expect internal fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDumpFailure): StringBuilder + +internal fun StringBuilder.appendIncorrectResultsFailure(failure: IncorrectResultsFailure): StringBuilder { + appendLine("= Invalid execution results =") + if (failure.scenario.initExecution.isNotEmpty()) { + appendLine("Init part:") + appendLine(uniteActorsAndResultsLinear(failure.scenario.initExecution, failure.results.initResults)) + } + if (failure.results.afterInitStateRepresentation != null) + appendLine("STATE: ${failure.results.afterInitStateRepresentation}") + appendLine("Parallel part:") + val parallelExecutionData = uniteParallelActorsAndResults(failure.scenario.parallelExecution, failure.results.parallelResultsWithClock) + append(printInColumns(parallelExecutionData)) + if (failure.results.afterParallelStateRepresentation != null) { + appendLine() + append("STATE: ${failure.results.afterParallelStateRepresentation}") + } + if (failure.scenario.postExecution.isNotEmpty()) { + appendLine() + appendLine("Post part:") + append(uniteActorsAndResultsLinear(failure.scenario.postExecution, failure.results.postResults)) + } + if (failure.results.afterPostStateRepresentation != null && failure.scenario.postExecution.isNotEmpty()) { + appendLine() + append("STATE: ${failure.results.afterPostStateRepresentation}") + } + if (failure.results.parallelResultsWithClock.flatten().any { !it.clockOnStart.empty }) + appendLine("\n---\nvalues in \"[..]\" brackets indicate the number of completed operations \n" + + "in each of the parallel threads seen at the beginning of the current operation\n---") + return this +} + +internal fun StringBuilder.appendValidationFailure(failure: ValidationFailure): StringBuilder { + appendLine("= Validation function ${failure.functionName} has failed =") + appendExecutionScenario(failure.scenario) + appendException(failure.exception) + return this +} + +internal fun StringBuilder.appendObstructionFreedomViolationFailure(failure: ObstructionFreedomViolationFailure): StringBuilder { + appendLine("= ${failure.reason} =") + appendExecutionScenario(failure.scenario) + return this +} + +private fun StringBuilder.appendException(t: Throwable) { + appendLine(t.stackTraceToString()) +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt index b89b5e4cd..32e5c5d75 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt @@ -22,6 +22,8 @@ package org.jetbrains.kotlinx.lincheck.runner import org.jetbrains.kotlinx.lincheck.execution.* +expect class ThreadDump + /** * Represents results for invocations, see [Runner.run]. */ @@ -58,4 +60,11 @@ class ValidationFailureInvocationResult( */ class ObstructionFreedomViolationInvocationResult( val reason: String +) : InvocationResult() + +/** + * Indicates that the invocation has run into deadlock or livelock. + */ +class DeadlockInvocationResult( + val threadDump: ThreadDump ) : InvocationResult() \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt index 1ed19aa3b..38e213a36 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt @@ -21,6 +21,54 @@ */ package org.jetbrains.kotlinx.lincheck.strategy -expect open class LincheckFailure{ - override fun toString(): String +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.runner.* + +expect class Trace + +open class LincheckFailure( + val scenario: ExecutionScenario, + val trace: Trace? +) { + override fun toString() = StringBuilder().appendFailure(this).toString() +} + +internal class IncorrectResultsFailure( + scenario: ExecutionScenario, + val results: ExecutionResult, + trace: Trace? = null +) : LincheckFailure(scenario, trace) + +internal class DeadlockWithDumpFailure( + scenario: ExecutionScenario, + val threadDump: ThreadDump, + trace: Trace? = null +) : LincheckFailure(scenario, trace) + +internal class UnexpectedExceptionFailure( + scenario: ExecutionScenario, + val exception: Throwable, + trace: Trace? = null +) : LincheckFailure(scenario, trace) + +internal class ValidationFailure( + scenario: ExecutionScenario, + val functionName: String, + val exception: Throwable, + trace: Trace? = null +) : LincheckFailure(scenario, trace) + +internal class ObstructionFreedomViolationFailure( + scenario: ExecutionScenario, + val reason: String, + trace: Trace? = null +) : LincheckFailure(scenario, trace) + +internal fun InvocationResult.toLincheckFailure(scenario: ExecutionScenario, trace: Trace? = null) = when (this) { + is DeadlockInvocationResult -> DeadlockWithDumpFailure(scenario, threadDump, trace) + is UnexpectedExceptionInvocationResult -> UnexpectedExceptionFailure(scenario, exception, trace) + is ValidationFailureInvocationResult -> ValidationFailure(scenario, functionName, exception, trace) + is ObstructionFreedomViolationInvocationResult -> ObstructionFreedomViolationFailure(scenario, reason, trace) + else -> error("Unexpected invocation result type: ${this::class.simpleName}") } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt index 59d2787a0..e383133cc 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt @@ -20,88 +20,12 @@ package org.jetbrains.kotlinx.lincheck -import org.jetbrains.kotlinx.lincheck.LoggingLevel.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* -import java.io.* -class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel, val out: PrintStream = System.out) { - fun logIteration(iteration: Int, maxIterations: Int, scenario: ExecutionScenario) = log(INFO) { - appendln("\n= Iteration $iteration / $maxIterations =") - appendExecutionScenario(scenario) - } - - fun logFailedIteration(failure: LincheckFailure) = log(INFO) { - appendFailure(failure) - } - - fun logScenarioMinimization(scenario: ExecutionScenario) = log(INFO) { - appendln("\nInvalid interleaving found, trying to minimize the scenario below:") - appendExecutionScenario(scenario) - } - - private inline fun log(logLevel: LoggingLevel, crossinline msg: StringBuilder.() -> Unit): Unit = synchronized(this) { - if (this.logLevel > logLevel) return - val sb = StringBuilder() - msg(sb) - out.println(sb) - } -} - -@JvmField val DEFAULT_LOG_LEVEL = ERROR -enum class LoggingLevel { - INFO, ERROR -} - -private class ActorWithResult(val actorRepresentation: String, val spacesAfterActor: Int, - val resultRepresentation: String, val spacesAfterResult: Int, - val clockRepresentation: String) { - override fun toString(): String = - actorRepresentation + ":" + " ".repeat(spacesAfterActor) + resultRepresentation + - " ".repeat(spacesAfterResult) + clockRepresentation -} - -private fun uniteActorsAndResultsLinear(actors: List, results: List): List { - require(actors.size == results.size) { - "Different numbers of actors and matching results found (${actors.size} != ${results.size})" - } - return actors.indices.map { - ActorWithResult("${actors[it]}", 1, "${results[it]}", 0, "") - } -} - -private fun uniteParallelActorsAndResults(actors: List>, results: List>): List> { - require(actors.size == results.size) { - "Different numbers of threads and matching results found (${actors.size} != ${results.size})" - } - return actors.mapIndexed { id, threadActors -> uniteActorsAndResultsAligned(threadActors, results[id]) } -} - -private fun uniteActorsAndResultsAligned(actors: List, results: List): List { - require(actors.size == results.size) { - "Different numbers of actors and matching results found (${actors.size} != ${results.size})" - } - val actorRepresentations = actors.map { it.toString() } - val resultRepresentations = results.map { it.result.toString() } - val maxActorLength = actorRepresentations.map { it.length }.max()!! - val maxResultLength = resultRepresentations.map { it.length }.max()!! - return actors.indices.map { i -> - val actorRepr = actorRepresentations[i] - val resultRepr = resultRepresentations[i] - val clock = results[i].clockOnStart - val spacesAfterActor = maxActorLength - actorRepr.length + 1 - val spacesAfterResultToAlign = maxResultLength - resultRepr.length - if (clock.empty) { - ActorWithResult(actorRepr, spacesAfterActor, resultRepr, spacesAfterResultToAlign, "") - } else { - ActorWithResult(actorRepr, spacesAfterActor, resultRepr, spacesAfterResultToAlign + 1, clock.toString()) - } - } -} - -internal fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilder { +internal actual fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilder { when (failure) { is IncorrectResultsFailure -> appendIncorrectResultsFailure(failure) is DeadlockWithDumpFailure -> appendDeadlockWithDumpFailure(failure) @@ -111,30 +35,22 @@ internal fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilde } val results = if (failure is IncorrectResultsFailure) failure.results else null if (failure.trace != null) { - appendln() - appendln("= The following interleaving leads to the error =") + appendLine() + appendLine("= The following interleaving leads to the error =") appendTrace(failure.scenario, results, failure.trace) if (failure is DeadlockWithDumpFailure) { - appendln() + appendLine() append("All threads are in deadlock") } } return this } -private fun StringBuilder.appendUnexpectedExceptionFailure(failure: UnexpectedExceptionFailure): StringBuilder { - appendln("= The execution failed with an unexpected exception =") - appendExecutionScenario(failure.scenario) - appendln() - appendException(failure.exception) - return this -} - -private fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDumpFailure): StringBuilder { +internal actual fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDumpFailure): StringBuilder { appendLine("= The execution has hung, see the thread dump =") appendExecutionScenario(failure.scenario) appendLine() - for ((t, stackTrace) in failure.threadDump) { + for ((t, stackTrace) in failure.threadDump.dump) { val threadNumber = if (t is FixedActiveThreadsExecutor.TestThread) t.iThread.toString() else "?" appendLine("Thread-$threadNumber:") stackTrace.map { @@ -142,53 +58,4 @@ private fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDum }.forEach { appendLine("\t$it") } } return this -} - -private fun StringBuilder.appendIncorrectResultsFailure(failure: IncorrectResultsFailure): StringBuilder { - appendln("= Invalid execution results =") - if (failure.scenario.initExecution.isNotEmpty()) { - appendln("Init part:") - appendln(uniteActorsAndResultsLinear(failure.scenario.initExecution, failure.results.initResults)) - } - if (failure.results.afterInitStateRepresentation != null) - appendln("STATE: ${failure.results.afterInitStateRepresentation}") - appendln("Parallel part:") - val parallelExecutionData = uniteParallelActorsAndResults(failure.scenario.parallelExecution, failure.results.parallelResultsWithClock) - append(printInColumns(parallelExecutionData)) - if (failure.results.afterParallelStateRepresentation != null) { - appendln() - append("STATE: ${failure.results.afterParallelStateRepresentation}") - } - if (failure.scenario.postExecution.isNotEmpty()) { - appendln() - appendln("Post part:") - append(uniteActorsAndResultsLinear(failure.scenario.postExecution, failure.results.postResults)) - } - if (failure.results.afterPostStateRepresentation != null && failure.scenario.postExecution.isNotEmpty()) { - appendln() - append("STATE: ${failure.results.afterPostStateRepresentation}") - } - if (failure.results.parallelResultsWithClock.flatten().any { !it.clockOnStart.empty }) - appendln("\n---\nvalues in \"[..]\" brackets indicate the number of completed operations \n" + - "in each of the parallel threads seen at the beginning of the current operation\n---") - return this -} - -private fun StringBuilder.appendValidationFailure(failure: ValidationFailure): StringBuilder { - appendln("= Validation function ${failure.functionName} has failed =") - appendExecutionScenario(failure.scenario) - appendException(failure.exception) - return this -} - -private fun StringBuilder.appendObstructionFreedomViolationFailure(failure: ObstructionFreedomViolationFailure): StringBuilder { - appendln("= ${failure.reason} =") - appendExecutionScenario(failure.scenario) - return this -} - -private fun StringBuilder.appendException(t: Throwable) { - val sw = StringWriter() - t.printStackTrace(PrintWriter(sw)) - appendln(sw.toString()) } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index f5e24c16a..461efedba 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -302,9 +302,9 @@ private class CustomObjectInputStream(val loader: ClassLoader, inputStream: Inpu * Collects the current thread dump and keeps only those * threads that are related to the specified [runner]. */ -internal fun collectThreadDump(runner: Runner) = Thread.getAllStackTraces().filter { (t, _) -> +internal fun collectThreadDump(runner: Runner) = ThreadDump(Thread.getAllStackTraces().filter { (t, _) -> t is FixedActiveThreadsExecutor.TestThread && t.runnerHash == runner.hashCode() -} +}) /** * This method helps to encapsulate remapper logic from strategy interface. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt index 00188f78c..9564f1f6d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt @@ -20,9 +20,4 @@ package org.jetbrains.kotlinx.lincheck.runner -/** - * Indicates that the invocation has run into deadlock or livelock. - */ -class DeadlockInvocationResult( - val threadDump: Map> -) : InvocationResult() \ No newline at end of file +actual class ThreadDump(val dump: Map>) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt index b442ce85d..8d4954630 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/LincheckFailure.kt @@ -1,73 +1,24 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.strategy -import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.execution.* -import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* -actual open class LincheckFailure( - val scenario: ExecutionScenario, - val trace: Trace? -) { - actual override fun toString() = StringBuilder().appendFailure(this).toString() -} - -internal class IncorrectResultsFailure( - scenario: ExecutionScenario, - val results: ExecutionResult, - trace: Trace? = null -) : LincheckFailure(scenario, trace) - -internal class DeadlockWithDumpFailure( - scenario: ExecutionScenario, - val threadDump: Map>, - trace: Trace? = null -) : LincheckFailure(scenario, trace) - -internal class UnexpectedExceptionFailure( - scenario: ExecutionScenario, - val exception: Throwable, - trace: Trace? = null -) : LincheckFailure(scenario, trace) - -internal class ValidationFailure( - scenario: ExecutionScenario, - val functionName: String, - val exception: Throwable, - trace: Trace? = null -) : LincheckFailure(scenario, trace) - -internal class ObstructionFreedomViolationFailure( - scenario: ExecutionScenario, - val reason: String, - trace: Trace? = null -) : LincheckFailure(scenario, trace) - -internal fun InvocationResult.toLincheckFailure(scenario: ExecutionScenario, trace: Trace? = null) = when (this) { - is DeadlockInvocationResult -> DeadlockWithDumpFailure(scenario, threadDump, trace) - is UnexpectedExceptionInvocationResult -> UnexpectedExceptionFailure(scenario, exception, trace) - is ValidationFailureInvocationResult -> ValidationFailure(scenario, functionName, exception, trace) - is ObstructionFreedomViolationInvocationResult -> ObstructionFreedomViolationFailure(scenario, reason, trace) - else -> error("Unexpected invocation result type: ${this.javaClass.simpleName}") -} \ No newline at end of file +actual data class Trace(val trace: List, val verboseTrace: Boolean) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt index e4f88546f..54e3106dc 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt @@ -27,8 +27,6 @@ import java.math.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* -data class Trace(val trace: List, val verboseTrace: Boolean) - /** * Essentially, a trace is a list of trace points, which represent * interleaving events, such as code location passing or thread switches, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt index ed829f5e3..ea3c56133 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt @@ -23,6 +23,7 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.strategy.* import java.util.* import kotlin.math.min diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/ThreadDumpTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/ThreadDumpTest.kt index 295815654..9fb8681e6 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/ThreadDumpTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/representation/ThreadDumpTest.kt @@ -42,7 +42,7 @@ class ThreadDumpTest { .invocationTimeout(100) val failure = options.checkImpl(DeadlockOnSynchronizedTest::class.java) check(failure is DeadlockWithDumpFailure) { "${DeadlockWithDumpFailure::class.simpleName} was expected but ${failure?.javaClass} was obtained"} - check(failure.threadDump.size == 2) { "thread dump for 2 threads expected, but for ${failure.threadDump.size} threads was detected"} + check(failure.threadDump.dump.size == 2) { "thread dump for 2 threads expected, but for ${failure.threadDump.dump.size} threads was detected"} } } } From 945cc27559f905cb597c9b67fb94b9fbfe76bc9e Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sun, 7 Feb 2021 00:29:38 +0300 Subject: [PATCH 12/72] abstract SequentialSpecification, move LTS --- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 3 + .../kotlinx/lincheck/CTestStructure.kt | 6 ++ .../kotlinx/lincheck/CommonReporter.kt | 5 +- .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 94 +++++++++++++++++++ .../kotlinx/lincheck/verifier/LTS.kt | 64 +++++++------ .../kotlinx/lincheck/verifier/Verifier.kt | 2 +- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 4 +- .../kotlinx/lincheck/CTestConfiguration.kt | 6 +- .../kotlinx/lincheck/CTestStructure.kt | 24 +++-- .../lincheck/ExecutionClassLoader.java | 4 +- .../jetbrains/kotlinx/lincheck/JvmHacks.kt | 1 - .../jetbrains/kotlinx/lincheck/LinChecker.kt | 4 +- .../lincheck/TransformationClassLoader.java | 2 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 72 ++------------ .../lincheck/runner/ParallelThreadsRunner.kt | 4 +- .../kotlinx/lincheck/runner/Runner.kt | 4 +- .../managed/ManagedCTestConfiguration.kt | 2 +- .../strategy/managed/ManagedStrategy.kt | 10 +- .../ModelCheckingCTestConfiguration.kt | 9 +- .../modelchecking/ModelCheckingStrategy.kt | 5 +- .../stress/StressCTestConfiguration.kt | 8 +- .../strategy/stress/StressStrategy.kt | 5 +- .../lincheck/verifier/AbstractLTSVerifier.kt | 2 +- .../lincheck/verifier/EpsilonVerifier.kt | 3 +- .../kotlinx/lincheck/verifier/JvmLTS.kt | 29 ++++++ .../verifier/SerializabilityVerifier.kt | 3 +- .../LinearizabilityVerifier.kt | 2 +- .../quiescent/QuiescentConsistencyVerifier.kt | 2 +- .../test/verifier/CustomScenarioDSL.kt | 2 +- 29 files changed, 239 insertions(+), 142 deletions(-) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt (92%) create mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 3a7686807..b603fe445 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -30,5 +30,8 @@ import org.jetbrains.kotlinx.lincheck.annotations.Operation */ expect class Actor { val isSuspendable: Boolean + val allowExtraSuspension: Boolean + val promptCancellation: Boolean + override fun toString(): String } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index cd4e26e17..b149c662f 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -22,6 +22,12 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.execution.* +expect class ValidationFunction + +expect val ValidationFunction.name: String + +expect class StateRepresentationFunction + /** * Contains information about the provided operations (see [Operation]). * Several [tests][StressCTest] can refer to one structure diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt index 3b2375365..78b56daf7 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt @@ -23,9 +23,10 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.LoggingLevel.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* +import kotlinx.atomicfu.locks.* import kotlin.jvm.* -class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel) { +class Reporter @JvmOverloads constructor(private val logLevel: LoggingLevel) : SynchronizedObject() { fun logIteration(iteration: Int, maxIterations: Int, scenario: ExecutionScenario) = log(INFO) { appendLine("\n= Iteration $iteration / $maxIterations =") appendExecutionScenario(scenario) @@ -40,7 +41,7 @@ class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel) { appendExecutionScenario(scenario) } - private inline fun log(logLevel: LoggingLevel, crossinline msg: StringBuilder.() -> Unit): Unit { // TODO fix this with some mutex + private inline fun log(logLevel: LoggingLevel, crossinline msg: StringBuilder.() -> Unit) = synchronized(this) { if (this.logLevel > logLevel) return val sb = StringBuilder() msg(sb) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt new file mode 100644 index 000000000..61dd565af --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -0,0 +1,94 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck + +import kotlinx.coroutines.* +import kotlin.coroutines.* + +expect class SequentialSpecification { + fun getInitialState(): Any +} + +object CancellableContinuationHolder { + var storedLastCancellableCont: CancellableContinuation<*>? = null +} + +expect fun storeCancellableContinuation(cont: CancellableContinuation<*>) + +internal fun executeActor(testInstance: Any, actor: Actor) = executeActor(testInstance, actor, null) +internal expect fun executeActor( + instance: Any, + actor: Actor, + completion: Continuation? +): Result + +internal expect fun createLincheckResult(res: Any?, wasSuspended: Boolean = false): Result + +internal expect fun executeValidationFunction(instance: Any, validationFunction: ValidationFunction): Throwable? +internal inline fun executeValidationFunctions(instance: Any, validationFunctions: List, + onError: (functionName: String, exception: Throwable) -> Unit) { + for (f in validationFunctions) { + val validationException = executeValidationFunction(instance, f) + if (validationException != null) { + onError(f.name, validationException) + return + } + } +} + +/** + * Returns `true` if the continuation was cancelled by [CancellableContinuation.cancel]. + */ +fun kotlin.Result.cancelledByLincheck() = exceptionOrNull() === cancellationByLincheckException + +private val cancellationByLincheckException = Exception("Cancelled by lincheck") + +internal enum class CancellationResult { CANCELLED_BEFORE_RESUMPTION, CANCELLED_AFTER_RESUMPTION, CANCELLATION_FAILED } + +internal class StoreExceptionHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler { + var exception: Throwable? = null + + override fun handleException(context: CoroutineContext, exception: Throwable) { + this.exception = exception + } +} + +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +internal fun CancellableContinuation.cancelByLincheck(promptCancellation: Boolean): CancellationResult { + val exceptionHandler = context[CoroutineExceptionHandler] as StoreExceptionHandler + exceptionHandler.exception = null + val cancelled = cancel(cancellationByLincheckException) + exceptionHandler.exception?.let { + throw it.cause!! // let's throw the original exception, ignoring the internal coroutines details + } + return when { + cancelled -> CancellationResult.CANCELLED_BEFORE_RESUMPTION + promptCancellation -> { + context[Job]!!.cancel() // we should always put a job into the context for prompt cancellation + CancellationResult.CANCELLED_AFTER_RESUMPTION + } + else -> CancellationResult.CANCELLATION_FAILED + } +} + +internal val String.canonicalClassName get() = this.replace('/', '.') +internal val String.internalClassName get() = this.replace('.', '/') \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt similarity index 92% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index 93f07256b..bee112461 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -1,9 +1,8 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,8 +15,7 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.verifier @@ -27,16 +25,16 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.CancellableContinuationHolder.storedLastCancellableCont import org.jetbrains.kotlinx.lincheck.verifier.LTS.* import org.jetbrains.kotlinx.lincheck.verifier.OperationType.* -import java.util.* import kotlin.collections.HashMap import kotlin.coroutines.* import kotlin.math.* import kotlin.reflect.* -import kotlin.reflect.full.* typealias RemappingFunction = IntArray typealias ResumedTickets = Set +expect fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification): SequentialSpecification + /** * Common interface for different labeled transition systems, which several correctness formalisms use. * Lincheck widely uses LTS-based formalisms for verification, see [AbstractLTSVerifier] implementations as examples. @@ -57,10 +55,9 @@ typealias ResumedTickets = Set * Practically, Kotlin implementation of such operations via suspend functions is supported. */ -class LTS(sequentialSpecification: KClass<*>) { +class LTS(sequentialSpecification: SequentialSpecification) { // we should transform the specification with `CancellabilitySupportClassTransformer` - private val sequentialSpecification: KClass<*> = TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv)} - .loadClass(sequentialSpecification.java.name)!!.kotlin + private val sequentialSpecification: SequentialSpecification = loadSequentialSpecification(sequentialSpecification) /** * Cache with all LTS states in order to reuse the equivalent ones. @@ -124,7 +121,7 @@ class LTS(sequentialSpecification: KClass<*>) { } } check(transitionInfo.result != Suspended) { - "Execution of the follow-up part of this operation ${actor.method} suspended - this behaviour is not supported" + "Execution of the follow-up part of this operation ${actor} suspended - this behaviour is not supported" } return if (expectedResult.isLegalByFollowUp(transitionInfo, actor.allowExtraSuspension)) transitionInfo else null } @@ -196,7 +193,7 @@ class LTS(sequentialSpecification: KClass<*>) { private fun getResumedOperations(resumedTicketsWithResults: Map): List { val resumedOperations = mutableListOf() - resumedTicketsWithResults.forEach { resumedTicket, res -> + resumedTicketsWithResults.forEach { (resumedTicket, res) -> resumedOperations.add(ResumptionInfo(res.resumedActor, res.by, resumedTicket)) } // Ignore the order of resumption by sorting the list of resumptions. @@ -227,7 +224,7 @@ class LTS(sequentialSpecification: KClass<*>) { } CANCELLATION -> { continuationsMap[Operation(this.actor, this.ticket, REQUEST)]!!.cancelByLincheck(promptCancellation = actor.promptCancellation) - val wasSuspended = suspendedOperations.removeIf { it.actor == actor && it.ticket == ticket } + val wasSuspended = suspendedOperations.removeAll { it.actor == actor && it.ticket == ticket } if (!actor.promptCancellation) { check(wasSuspended) { "The operation can be cancelled after resumption only in the prompt cancellation mode" } } @@ -246,7 +243,7 @@ class LTS(sequentialSpecification: KClass<*>) { } resumedOperations.forEach { (resumedTicket, res) -> if (!prevResumedTickets.contains(resumedTicket)) { - suspendedOperations.removeIf { it.ticket == resumedTicket } + suspendedOperations.removeAll { it.ticket == resumedTicket } res.by = actor } } @@ -281,7 +278,7 @@ class LTS(sequentialSpecification: KClass<*>) { ).intern(null) { _, _ -> initialState } } - private fun createInitialStateInstance() = sequentialSpecification.getConstructor().newInstance() + private fun createInitialStateInstance() = sequentialSpecification.getInitialState() fun checkStateEquivalenceImplementation() { val i1 = createInitialStateInstance() @@ -324,24 +321,24 @@ class LTS(sequentialSpecification: KClass<*>) { fun generateDotGraph(): String { val builder = StringBuilder() - builder.appendln("digraph {") - builder.appendln("\"${initialState.hashCode()}\" [style=filled, fillcolor=green]") - builder.appendTransitions(initialState, IdentityHashMap()) - builder.appendln("}") + builder.appendLine("digraph {") + builder.appendLine("\"${initialState.hashCode()}\" [style=filled, fillcolor=green]") + builder.appendTransitions(initialState, HashMap()) + builder.appendLine("}") return builder.toString() } - private fun StringBuilder.appendTransitions(state: State, visitedStates: IdentityHashMap) { - state.transitionsByRequests.forEach { actor, transition -> - appendln("${state.hashCode()} -> ${transition.nextState.hashCode()} [ label=\", rf=${transition.rf?.contentToString()}\" ]") + private fun StringBuilder.appendTransitions(state: State, visitedStates: HashMap) { + state.transitionsByRequests.forEach { (actor, transition) -> + appendLine("${state.hashCode()} -> ${transition.nextState.hashCode()} [ label=\", rf=${transition.rf?.contentToString()}\" ]") if (visitedStates.put(transition.nextState, Unit) === null) appendTransitions(transition.nextState, visitedStates) } - state.transitionsByFollowUps.forEach { ticket, transition -> - appendln("${state.hashCode()} -> ${transition.nextState.hashCode()} [ label=\", rf=${transition.rf?.contentToString()}\" ]") + state.transitionsByFollowUps.forEach { (ticket, transition) -> + appendLine("${state.hashCode()} -> ${transition.nextState.hashCode()} [ label=\", rf=${transition.rf?.contentToString()}\" ]") if (visitedStates.put(transition.nextState, Unit) === null) appendTransitions(transition.nextState, visitedStates) } - state.transitionsByCancellations.forEach { ticket, transition -> - appendln("${state.hashCode()} -> ${transition.nextState.hashCode()} [ label=\", rf=${transition.rf?.contentToString()}\" ]") + state.transitionsByCancellations.forEach { (ticket, transition) -> + appendLine("${state.hashCode()} -> ${transition.nextState.hashCode()} [ label=\", rf=${transition.rf?.contentToString()}\" ]") if (visitedStates.put(transition.nextState, Unit) === null) appendTransitions(transition.nextState, visitedStates) } } @@ -369,7 +366,16 @@ private class StateInfo( resumedOperations == other.resumedOperations } - override fun hashCode() = Objects.hash( + private fun hashAll(vararg vals: Any?): Int { + var res = 0 + for (v in vals) { + res += v.hashCode() + res *= 31 + } + return res + } + + override fun hashCode() = hashAll( instance, suspendedOperations.map { it.actor }, resumedOperations diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt index df22015c2..5f8dc4ddc 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt @@ -31,7 +31,7 @@ import kotlin.collections.HashSet * * * IMPORTANT! - * All implementations should have `(sequentialSpecification: KClass<*>)` constructor, + * All implementations should have `(sequentialSpecification: SequentialSpecification)` constructor, * which takes the scenario to be tested and the correct sequential implementation of the testing data structure. */ interface Verifier { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt index cc6404d38..7f74fb391 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -36,10 +36,10 @@ actual data class Actor @JvmOverloads constructor( val arguments: List, val handledExceptions: List> = emptyList(), val cancelOnSuspension: Boolean = false, - val allowExtraSuspension: Boolean = false, + actual val allowExtraSuspension: Boolean = false, val blocking: Boolean = false, val causesBlocking: Boolean = false, - val promptCancellation: Boolean = false, + actual val promptCancellation: Boolean = false, // we have to specify `isSuspendable` property explicitly for transformed classes since // `isSuspendable` implementation produces a circular dependency and, therefore, fails. actual val isSuspendable: Boolean = method.isSuspendable() diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 1b8899916..6de91a180 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -48,11 +48,11 @@ actual abstract class CTestConfiguration( val verifierClass: KClass, val requireStateEquivalenceImplCheck: Boolean, val minimizeFailedScenario: Boolean, - val sequentialSpecification: KClass<*>?, + val sequentialSpecification: SequentialSpecification?, val timeoutMs: Long ) { - abstract fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, - stateRepresentationMethod: Method?, verifier: Verifier): Strategy + abstract fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier): Strategy companion object { const val DEFAULT_ITERATIONS = 100 diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index 1ed9394fe..aa23e001e 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -31,6 +31,12 @@ import java.util.* import java.util.stream.Collectors import kotlin.collections.HashMap +actual typealias ValidationFunction = Method + +actual val ValidationFunction.name get() = this.name + +actual typealias StateRepresentationFunction = Method + /** * Contains information about the provided operations (see [Operation]). * Several [tests][StressCTest] can refer to one structure @@ -39,8 +45,8 @@ import kotlin.collections.HashMap actual class CTestStructure private constructor( actual val actorGenerators: List, actual val operationGroups: List, - val validationFunctions: List, - val stateRepresentation: Method? + val validationFunctions: List, + val stateRepresentation: StateRepresentationFunction? ) { companion object { /** @@ -50,8 +56,8 @@ actual class CTestStructure private constructor( val namedGens: MutableMap> = HashMap() val groupConfigs: MutableMap = HashMap() val actorGenerators: MutableList = ArrayList() - val validationFunctions: MutableList = ArrayList() - val stateRepresentations: MutableList = ArrayList() + val validationFunctions: MutableList = ArrayList() + val stateRepresentations: MutableList = ArrayList() var clazz = testClass while (clazz != null) { readTestStructureFromClass(clazz, namedGens, groupConfigs, actorGenerators, validationFunctions, stateRepresentations) @@ -60,10 +66,10 @@ actual class CTestStructure private constructor( check(stateRepresentations.size <= 1) { "Several functions marked with " + StateRepresentation::class.java.simpleName + " were found, while at most one should be specified: " + - stateRepresentations.stream().map { obj: Method -> obj.name }.collect(Collectors.joining(", ")) + stateRepresentations.stream().map { obj: StateRepresentationFunction -> obj.name }.collect(Collectors.joining(", ")) } - var stateRepresentation: Method? = null - if (!stateRepresentations.isEmpty()) stateRepresentation = stateRepresentations[0] + var stateRepresentation: StateRepresentationFunction? = null + if (stateRepresentations.isNotEmpty()) stateRepresentation = stateRepresentations[0] // Create StressCTest class configuration return CTestStructure(actorGenerators, ArrayList(groupConfigs.values), validationFunctions, stateRepresentation) } @@ -71,8 +77,8 @@ actual class CTestStructure private constructor( private fun readTestStructureFromClass(clazz: Class<*>, namedGens: MutableMap>, groupConfigs: MutableMap, actorGenerators: MutableList, - validationFunctions: MutableList, - stateRepresentations: MutableList) { + validationFunctions: MutableList, + stateRepresentations: MutableList) { // Read named parameter paramgen (declared for class) for (paramAnn in clazz.getAnnotationsByType(Param::class.java)) { require(!paramAnn.name.isEmpty()) { "@Param name in class declaration cannot be empty" } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java index 720c9db87..3e8e61192 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/ExecutionClassLoader.java @@ -1,5 +1,3 @@ -package org.jetbrains.kotlinx.lincheck; - /* * #%L * Lincheck @@ -22,6 +20,8 @@ * #L% */ +package org.jetbrains.kotlinx.lincheck; + import org.jetbrains.kotlinx.lincheck.runner.TestThreadExecution; /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt index 367cc0c1c..38273e7a1 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt @@ -22,7 +22,6 @@ package org.jetbrains.kotlinx.lincheck import java.lang.reflect.* import kotlin.reflect.* -import kotlin.reflect.jvm.* fun KClass.getConstructor(vararg args: Class<*>) : Constructor { return this.java.getConstructor(*args) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 4efc97e6d..a3666212b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -142,7 +142,7 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { testClass = testClass, scenario = this, validationFunctions = testStructure.validationFunctions, - stateRepresentationMethod = testStructure.stateRepresentation, + stateRepresentationFunction = testStructure.stateRepresentation, verifier = verifier ).run() @@ -179,7 +179,7 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { private fun CTestConfiguration.createVerifier() = - verifierClass.constructors.find { it.parameters.size == 1 && it.parameters[0].type == KClass::class.createType(listOf(STAR)) }!! + verifierClass.constructors.find { it.parameters.size == 1 && it.parameters[0].type == SequentialSpecification::class.createType() }!! .call(sequentialSpecification).also { if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java index 0f7b9b4b0..d4fd3702a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java @@ -37,7 +37,7 @@ import java.util.stream.Collectors; import static org.jetbrains.kotlinx.lincheck.TransformationClassLoader.*; -import static org.jetbrains.kotlinx.lincheck.UtilsKt.getCanonicalClassName; +import static org.jetbrains.kotlinx.lincheck.CommonUtilsKt.getCanonicalClassName; import static org.objectweb.asm.Opcodes.*; /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 461efedba..9698dbdfa 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -40,17 +40,18 @@ import kotlin.reflect.* import kotlin.reflect.full.* import kotlin.reflect.jvm.* +actual class SequentialSpecification(val kClass: KClass<*>) { + actual fun getInitialState(): Any = kClass.getConstructor().newInstance() +} -fun chooseSequentialSpecification(sequentialSpecificationByUser: KClass<*>?, testClass: Class<*>): KClass<*> = - if (sequentialSpecificationByUser === DummySequentialSpecification::class || sequentialSpecificationByUser == null) testClass.kotlin - else sequentialSpecificationByUser - -internal fun executeActor(testInstance: Any, actor: Actor) = executeActor(testInstance, actor, null) +fun chooseSequentialSpecification(sequentialSpecificationByUser: KClass<*>?, testClass: Class<*>): SequentialSpecification = + if (sequentialSpecificationByUser === DummySequentialSpecification::class || sequentialSpecificationByUser == null) SequentialSpecification(testClass.kotlin) + else SequentialSpecification(sequentialSpecificationByUser) /** * Executes the specified actor on the sequential specification instance and returns its result. */ -internal fun executeActor( +internal actual fun executeActor( instance: Any, actor: Actor, completion: Continuation? @@ -78,18 +79,7 @@ internal fun executeActor( } } -internal inline fun executeValidationFunctions(instance: Any, validationFunctions: List, - onError: (functionName: String, exception: Throwable) -> Unit) { - for (f in validationFunctions) { - val validationException = executeValidationFunction(instance, f) - if (validationException != null) { - onError(f.name, validationException) - return - } - } -} - -private fun executeValidationFunction(instance: Any, validationFunction: Method): Throwable? { +internal actual fun executeValidationFunction(instance: Any, validationFunction: ValidationFunction): Throwable? { val m = getMethod(instance, validationFunction) try { m.invoke(instance) @@ -138,7 +128,7 @@ private fun Class.getMethod(name: String, parameterTypes: Array if (wasSuspended) SuspendedVoidResult else VoidResult res != null && res is Throwable -> createExceptionResult(res.javaClass, wasSuspended) res === COROUTINE_SUSPENDED -> Suspended @@ -182,49 +172,10 @@ internal operator fun ExecutionResult.get(threadId: Int): List = when (t else -> parallelResultsWithClock[threadId - 1].map { it.result } } -internal class StoreExceptionHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler { - var exception: Throwable? = null - - override fun handleException(context: CoroutineContext, exception: Throwable) { - this.exception = exception - } -} - -@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") -internal fun CancellableContinuation.cancelByLincheck(promptCancellation: Boolean): CancellationResult { - val exceptionHandler = context[CoroutineExceptionHandler] as StoreExceptionHandler - exceptionHandler.exception = null - val cancelled = cancel(cancellationByLincheckException) - exceptionHandler.exception?.let { - throw it.cause!! // let's throw the original exception, ignoring the internal coroutines details - } - return when { - cancelled -> CancellationResult.CANCELLED_BEFORE_RESUMPTION - promptCancellation -> { - context[Job]!!.cancel() // we should always put a job into the context for prompt cancellation - CancellationResult.CANCELLED_AFTER_RESUMPTION - } - else -> CancellationResult.CANCELLATION_FAILED - } -} - -internal enum class CancellationResult { CANCELLED_BEFORE_RESUMPTION, CANCELLED_AFTER_RESUMPTION, CANCELLATION_FAILED } - @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") private val cancelCompletedResultMethod = DispatchedTask::class.declaredFunctions.find { it.name == "cancelCompletedResult" }!!.javaMethod!! -/** - * Returns `true` if the continuation was cancelled by [CancellableContinuation.cancel]. - */ -fun kotlin.Result.cancelledByLincheck() = exceptionOrNull() === cancellationByLincheckException - -private val cancellationByLincheckException = Exception("Cancelled by lincheck") - -object CancellableContinuationHolder { - var storedLastCancellableCont: CancellableContinuation<*>? = null -} - -fun storeCancellableContinuation(cont: CancellableContinuation<*>) { +actual fun storeCancellableContinuation(cont: CancellableContinuation<*>) { val t = Thread.currentThread() if (t is FixedActiveThreadsExecutor.TestThread) { t.cont = cont @@ -315,6 +266,3 @@ internal fun getRemapperByTransformers(classTransformers: List): R classTransformers.any { it is ManagedStrategyTransformer } -> JavaUtilRemapper() else -> null } - -internal val String.canonicalClassName get() = this.replace('/', '.') -internal val String.internalClassName get() = this.replace('.', '/') \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 3c254e470..021f139ec 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -48,8 +48,8 @@ private typealias SuspensionPointResultWithContinuation = AtomicReference, - validationFunctions: List, - stateRepresentationFunction: Method?, + validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, private val timeoutMs: Long, // for deadlock or livelock detection private val useClocks: UseClocks // specifies whether `HBClock`-s should always be used or with some probability ) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index 41fdb697a..53a600cad 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -38,8 +38,8 @@ import java.io.* actual abstract class Runner protected constructor( protected val strategy: Strategy, private val _testClass: Class<*>, // will be transformed later - protected val validationFunctions: List, - protected val stateRepresentationFunction: Method? + protected val validationFunctions: List, + protected val stateRepresentationFunction: StateRepresentationFunction? ) : Closeable { protected actual var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` protected lateinit var testClass: Class<*> // not available before `initialize` call diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt index 5be0de581..741d4dcb0 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt @@ -35,7 +35,7 @@ abstract class ManagedCTestConfiguration( generatorClass: KClass, verifierClass: KClass, val checkObstructionFreedom: Boolean, val hangingDetectionThreshold: Int, val invocationsPerIteration: Int, val guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: KClass<*>?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean + sequentialSpecification: SequentialSpecification?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean ) : CTestConfiguration( testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index de3c70d88..45c58728c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -46,8 +46,8 @@ abstract class ManagedStrategy( private val testClass: Class<*>, scenario: ExecutionScenario, private val verifier: Verifier, - private val validationFunctions: List, - private val stateRepresentationFunction: Method?, + private val validationFunctions: List, + private val stateRepresentationFunction: StateRepresentationFunction?, private val testCfg: ManagedCTestConfiguration ) : Strategy(scenario), Closeable { // The number of parallel threads. @@ -723,9 +723,9 @@ abstract class ManagedStrategy( * to the strategy so that it can known about some required events. */ private class ManagedStrategyRunner( - private val managedStrategy: ManagedStrategy, testClass: Class<*>, validationFunctions: List, - stateRepresentationMethod: Method?, timeoutMs: Long, useClocks: UseClocks -) : ParallelThreadsRunner(managedStrategy, testClass, validationFunctions, stateRepresentationMethod, timeoutMs, useClocks) { + private val managedStrategy: ManagedStrategy, testClass: Class<*>, validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, timeoutMs: Long, useClocks: UseClocks +) : ParallelThreadsRunner(managedStrategy, testClass, validationFunctions, stateRepresentationFunction, timeoutMs, useClocks) { override fun onStart(iThread: Int) { super.onStart(iThread) managedStrategy.onStart(iThread) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index de2230eb2..366abe2be 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -21,6 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* @@ -35,11 +36,11 @@ class ModelCheckingCTestConfiguration(testClass: Class<*>, iterations: Int, thre actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, invocationsPerIteration: Int, guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: KClass<*>?, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean + sequentialSpecification: SequentialSpecification?, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean ) : ManagedCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs, eliminateLocalObjects, verboseTrace) { - override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, - stateRepresentationMethod: Method?, verifier: Verifier): Strategy - = ModelCheckingStrategy(this, testClass, scenario, validationFunctions, stateRepresentationMethod, verifier) + override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier): Strategy + = ModelCheckingStrategy(this, testClass, scenario, validationFunctions, stateRepresentationFunction, verifier) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt index 1073ceff0..07e4b4502 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt @@ -21,6 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* @@ -47,8 +48,8 @@ internal class ModelCheckingStrategy( testCfg: ModelCheckingCTestConfiguration, testClass: Class<*>, scenario: ExecutionScenario, - validationFunctions: List, - stateRepresentation: Method?, + validationFunctions: List, + stateRepresentation: StateRepresentationFunction?, verifier: Verifier ) : ManagedStrategy(testClass, scenario, verifier, validationFunctions, stateRepresentation, testCfg) { // The number of invocations that the strategy is eligible to use to search for an incorrect execution. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index ad297aa9a..5221a92c7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -33,12 +33,12 @@ import kotlin.reflect.* class StressCTestConfiguration(testClass: Class<*>, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: KClass<*>?, timeoutMs: Long + sequentialSpecification: SequentialSpecification?, timeoutMs: Long ) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs) { - override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, - stateRepresentationMethod: Method?, verifier: Verifier) = - StressStrategy(this, testClass, scenario, validationFunctions, stateRepresentationMethod, verifier) + override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier) = + StressStrategy(this, testClass, scenario, validationFunctions, stateRepresentationFunction, verifier) companion object { const val DEFAULT_INVOCATIONS = 10000 diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 265c4a276..e66d2e57c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -21,6 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck.strategy.stress +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* @@ -31,8 +32,8 @@ class StressStrategy( testCfg: StressCTestConfiguration, testClass: Class<*>, scenario: ExecutionScenario, - validationFunctions: List, - stateRepresentationFunction: Method?, + validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, private val verifier: Verifier ) : Strategy(scenario) { private val invocations = testCfg.invocationsPerIteration diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt index 0d1253adf..5f91ac66c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt @@ -34,7 +34,7 @@ import kotlin.reflect.* * the next possible transitions using [VerifierContext.nextContext] function. This verifier * uses depth-first search to find a proper path. */ -abstract class AbstractLTSVerifier(protected val sequentialSpecification: KClass<*>) : CachedVerifier() { +abstract class AbstractLTSVerifier(protected val sequentialSpecification: SequentialSpecification) : CachedVerifier() { abstract val lts: LTS abstract fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult): VerifierContext diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt index 9fef72b70..bb8aa2cd4 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt @@ -21,6 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck.verifier +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario import kotlin.reflect.* @@ -28,7 +29,7 @@ import kotlin.reflect.* /** * This verifier does nothing and could be used for performance benchmarking. */ -class EpsilonVerifier(sequentialSpecification: KClass<*>) : Verifier { +class EpsilonVerifier(sequentialSpecification: SequentialSpecification) : Verifier { override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean = true // Always correct results :) override fun checkStateEquivalenceImplementation() {} diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt new file mode 100644 index 000000000..4d32aeb41 --- /dev/null +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt @@ -0,0 +1,29 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.verifier + +import org.jetbrains.kotlinx.lincheck.* + +actual fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification): SequentialSpecification = + SequentialSpecification( + TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv) } + .loadClass(sequentialSpecification.kClass.java.name)!!.kotlin + ) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt index 4399134f5..f22a3abc2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt @@ -19,6 +19,7 @@ */ package org.jetbrains.kotlinx.lincheck.verifier +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* import kotlin.reflect.* @@ -28,7 +29,7 @@ import kotlin.reflect.* * It just tries to find any operations sequence which execution produces the same results. */ class SerializabilityVerifier( - sequentialSpecification: KClass<*> + sequentialSpecification: SequentialSpecification ) : CachedVerifier() { private val linerizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt index 685f55dc4..52e7c76e8 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt @@ -35,7 +35,7 @@ import kotlin.reflect.* * This verifier is based on [AbstractLTSVerifier] and caches the already processed results * for performance improvement (see [CachedVerifier]). */ -class LinearizabilityVerifier(sequentialSpecification: KClass<*>) : AbstractLTSVerifier(sequentialSpecification) { +class LinearizabilityVerifier(sequentialSpecification: SequentialSpecification) : AbstractLTSVerifier(sequentialSpecification) { override val lts: LTS = LTS(sequentialSpecification = sequentialSpecification) override fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult) = diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 7863be13f..1718faadd 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -36,7 +36,7 @@ import kotlin.reflect.* * However, we believe that quiescent points do not occur * in practice while supporting them complicates the implementation. */ -class QuiescentConsistencyVerifier(sequentialSpecification: KClass<*>) : Verifier { +class QuiescentConsistencyVerifier(sequentialSpecification: SequentialSpecification) : Verifier { private val linearizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) private val scenarioMapping: MutableMap = WeakHashMap() diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt index 3b8ea35f8..2c5818e90 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt @@ -58,7 +58,7 @@ fun verify( correct: Boolean ) { val (scenario, results) = scenarioWithResults(block) - val verifier = verifierClass.getConstructor(KClass::class.java).newInstance(testClass.kotlin) + val verifier = verifierClass.getConstructor(SequentialSpecification::class.java).newInstance(SequentialSpecification(testClass.kotlin)) val res = verifier.verifyResults(scenario, results) assert(res == correct) } From ac46f5b6172503d97594758bf5b5751346454d2e Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 10 Feb 2021 19:19:13 +0300 Subject: [PATCH 13/72] move verifiers to common --- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 6 ++++- .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 25 +++++++++++++++++++ .../lincheck/verifier/AbstractLTSVerifier.kt | 0 .../lincheck/verifier/EpsilonVerifier.kt | 11 +++----- .../verifier/SerializabilityVerifier.kt | 1 - .../LinearizabilityVerifier.kt | 0 .../quiescent/QuiescentConsistencyVerifier.kt | 5 +--- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 7 ++++-- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 24 +++--------------- .../lincheck/execution/ExecutionScenario.kt | 2 -- .../stress/StressCTestConfiguration.kt | 1 - 11 files changed, 44 insertions(+), 38 deletions(-) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt (100%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt (89%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt (99%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt (100%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt (97%) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt index b603fe445..bdfa9007a 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -21,6 +21,7 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.verifier.quiescent.* /** * The actor entity describe the operation with its parameters @@ -32,6 +33,9 @@ expect class Actor { val isSuspendable: Boolean val allowExtraSuspension: Boolean val promptCancellation: Boolean + val cancelOnSuspension: Boolean override fun toString(): String -} \ No newline at end of file +} + +expect val Actor.isQuiescentConsistent: Boolean \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index 61dd565af..03453ef3e 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -22,8 +22,13 @@ package org.jetbrains.kotlinx.lincheck import kotlinx.coroutines.* +import org.jetbrains.kotlinx.lincheck.execution.* import kotlin.coroutines.* +expect class TestClass { + fun createInstance(): Any +} + expect class SequentialSpecification { fun getInitialState(): Any } @@ -90,5 +95,25 @@ internal fun CancellableContinuation.cancelByLincheck(promptCancellation: } } +/** + * Returns scenario for the specified thread. Note that initial and post parts + * are represented as threads with ids `0` and `threads + 1` respectively. + */ +internal operator fun ExecutionScenario.get(threadId: Int): List = when (threadId) { + 0 -> initExecution + threads + 1 -> postExecution + else -> parallelExecution[threadId - 1] +} + +/** + * Returns results for the specified thread. Note that initial and post parts + * are represented as threads with ids `0` and `threads + 1` respectively. + */ +internal operator fun ExecutionResult.get(threadId: Int): List = when (threadId) { + 0 -> initResults + parallelResultsWithClock.size + 1 -> postResults + else -> parallelResultsWithClock[threadId - 1].map { it.result } +} + internal val String.canonicalClassName get() = this.replace('/', '.') internal val String.internalClassName get() = this.replace('.', '/') \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt similarity index 89% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt index bb8aa2cd4..66ee53cec 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt @@ -1,9 +1,8 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,15 +15,13 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario -import kotlin.reflect.* /** * This verifier does nothing and could be used for performance benchmarking. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt similarity index 99% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt index f22a3abc2..5a0e103c4 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt @@ -22,7 +22,6 @@ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* -import kotlin.reflect.* /** * This verifier checks that the specified results could be happen in serializable execution. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt similarity index 97% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 1718faadd..61b1a0416 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* -import java.util.* import kotlin.collections.ArrayList import kotlin.reflect.* @@ -38,7 +37,7 @@ import kotlin.reflect.* */ class QuiescentConsistencyVerifier(sequentialSpecification: SequentialSpecification) : Verifier { private val linearizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) - private val scenarioMapping: MutableMap = WeakHashMap() + private val scenarioMapping: MutableMap = HashMap() // TODO change logic to WeakHashMap override fun checkStateEquivalenceImplementation() = linearizabilityVerifier.checkStateEquivalenceImplementation() @@ -118,8 +117,6 @@ class QuiescentConsistencyVerifier(sequentialSpecification: SequentialSpecificat } } -private val Actor.isQuiescentConsistent: Boolean get() = method.isAnnotationPresent(QuiescentConsistent::class.java) - /** * This annotation indicates that the method it is presented on * is quiescent consistent. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 7f74fb391..107c6579f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -22,6 +22,7 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.verifier.quiescent.* import java.lang.reflect.* import kotlin.reflect.jvm.kotlinFunction @@ -35,7 +36,7 @@ actual data class Actor @JvmOverloads constructor( val method: Method, val arguments: List, val handledExceptions: List> = emptyList(), - val cancelOnSuspension: Boolean = false, + actual val cancelOnSuspension: Boolean = false, actual val allowExtraSuspension: Boolean = false, val blocking: Boolean = false, val causesBlocking: Boolean = false, @@ -60,4 +61,6 @@ actual data class Actor @JvmOverloads constructor( val handlesExceptions = handledExceptions.isNotEmpty() } -fun Method.isSuspendable(): Boolean = kotlinFunction?.isSuspend ?: false \ No newline at end of file +fun Method.isSuspendable(): Boolean = kotlinFunction?.isSuspend ?: false + +actual val Actor.isQuiescentConsistent: Boolean get() = method.isAnnotationPresent(QuiescentConsistent::class.java) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 9698dbdfa..2eb386c3a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -40,6 +40,10 @@ import kotlin.reflect.* import kotlin.reflect.full.* import kotlin.reflect.jvm.* +actual class TestClass(val clazz: Class<*>) { + actual fun createInstance(): Any = clazz.getDeclaredConstructor().newInstance() +} + actual class SequentialSpecification(val kClass: KClass<*>) { actual fun getInitialState(): Any = kClass.getConstructor().newInstance() } @@ -152,26 +156,6 @@ inline fun Throwable.catch(vararg exceptions: Class<*>, block: () -> R): R { } else throw this } -/** - * Returns scenario for the specified thread. Note that initial and post parts - * are represented as threads with ids `0` and `threads + 1` respectively. - */ -internal operator fun ExecutionScenario.get(threadId: Int): List = when (threadId) { - 0 -> initExecution - threads + 1 -> postExecution - else -> parallelExecution[threadId - 1] -} - -/** - * Returns results for the specified thread. Note that initial and post parts - * are represented as threads with ids `0` and `threads + 1` respectively. - */ -internal operator fun ExecutionResult.get(threadId: Int): List = when (threadId) { - 0 -> initResults - parallelResultsWithClock.size + 1 -> postResults - else -> parallelResultsWithClock[threadId - 1].map { it.result } -} - @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") private val cancelCompletedResultMethod = DispatchedTask::class.declaredFunctions.find { it.name == "cancelCompletedResult" }!!.javaMethod!! diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt index 33386553c..0f01f6526 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt @@ -20,8 +20,6 @@ package org.jetbrains.kotlinx.lincheck.execution -import org.jetbrains.kotlinx.lincheck.* - /** * Returns `true` if there is at least one suspendable actor in the generated scenario */ diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index 5221a92c7..7a9bfe54a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -24,7 +24,6 @@ package org.jetbrains.kotlinx.lincheck.strategy.stress import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* -import java.lang.reflect.* import kotlin.reflect.* /** From 0c2b606189f36cc09fd8ff8e7fc766abf0dd06d9 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 10 Feb 2021 19:42:27 +0300 Subject: [PATCH 14/72] replace testClass: Class<*> by TestClass abstraction --- .../org/jetbrains/kotlinx/lincheck/CommonUtils.kt | 2 ++ .../jetbrains/kotlinx/lincheck/CTestConfiguration.kt | 12 ++++++------ .../org/jetbrains/kotlinx/lincheck/LinChecker.kt | 4 ++-- .../main/org/jetbrains/kotlinx/lincheck/Options.kt | 2 +- src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt | 6 ++++-- .../kotlinx/lincheck/runner/ParallelThreadsRunner.kt | 4 ++-- .../org/jetbrains/kotlinx/lincheck/runner/Runner.kt | 6 +++--- .../runner/TestThreadExecutionGenerator.java | 2 +- .../strategy/managed/ManagedCTestConfiguration.kt | 2 +- .../lincheck/strategy/managed/ManagedStrategy.kt | 4 ++-- .../strategy/managed/ManagedStrategyStateHolder.kt | 8 ++++---- .../lincheck/strategy/managed/ObjectManager.kt | 3 ++- .../modelchecking/ModelCheckingCTestConfiguration.kt | 4 ++-- .../managed/modelchecking/ModelCheckingOptions.kt | 2 +- .../managed/modelchecking/ModelCheckingStrategy.kt | 2 +- .../strategy/stress/StressCTestConfiguration.kt | 4 ++-- .../lincheck/strategy/stress/StressOptions.kt | 5 ++--- .../lincheck/strategy/stress/StressStrategy.kt | 2 +- .../runner/ParallelThreadsRunnerExceptionTest.kt | 6 +++--- .../test/runner/TestThreadExecutionHelperTest.java | 2 +- 20 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index 03453ef3e..af0a90490 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -26,6 +26,8 @@ import org.jetbrains.kotlinx.lincheck.execution.* import kotlin.coroutines.* expect class TestClass { + val name: String + fun createInstance(): Any } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 6de91a180..d5e99b2e4 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -38,7 +38,7 @@ import kotlin.reflect.* * Abstract configuration for different lincheck modes. */ actual abstract class CTestConfiguration( - val testClass: Class<*>, + val testClass: TestClass, actual val iterations: Int, actual val threads: Int, actual val actorsPerThread: Int, @@ -51,7 +51,7 @@ actual abstract class CTestConfiguration( val sequentialSpecification: SequentialSpecification?, val timeoutMs: Long ) { - abstract fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, + abstract fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier): Strategy companion object { @@ -70,20 +70,20 @@ actual abstract class CTestConfiguration( internal fun createFromTestClassAnnotations(testClass: Class<*>): List { val stressConfigurations: List = testClass.getAnnotationsByType(StressCTest::class.java) .map { ann: StressCTest -> - StressCTestConfiguration(testClass, ann.iterations, + StressCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, ann.generator, ann.verifier, ann.invocationsPerIteration, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, - chooseSequentialSpecification(ann.sequentialSpecification, testClass), DEFAULT_TIMEOUT_MS + chooseSequentialSpecification(ann.sequentialSpecification, TestClass(testClass)), DEFAULT_TIMEOUT_MS ) } val modelCheckingConfigurations: List = testClass.getAnnotationsByType(ModelCheckingCTest::class.java) .map { ann: ModelCheckingCTest -> - ModelCheckingCTestConfiguration(testClass, ann.iterations, + ModelCheckingCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, ann.generator, ann.verifier, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, ann.invocationsPerIteration, ManagedCTestConfiguration.DEFAULT_GUARANTEES, ann.requireStateEquivalenceImplCheck, - ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification, testClass), + ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification, TestClass(testClass)), DEFAULT_TIMEOUT_MS, DEFAULT_ELIMINATE_LOCAL_OBJECTS, DEFAULT_VERBOSE_TRACE ) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index a3666212b..df54b7f83 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -42,7 +42,7 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { init { val logLevel = options?.logLevel ?: testClass.getAnnotation(LogLevel::class.java)?.value ?: DEFAULT_LOG_LEVEL reporter = Reporter(logLevel) - testConfigurations = if (options != null) listOf(options.createTestConfigurations(testClass)) + testConfigurations = if (options != null) listOf(options.createTestConfigurations(TestClass(testClass))) else createFromTestClassAnnotations(testClass) } @@ -139,7 +139,7 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { private fun ExecutionScenario.run(testCfg: CTestConfiguration, verifier: Verifier): LincheckFailure? = testCfg.createStrategy( - testClass = testClass, + testClass = TestClass(testClass), scenario = this, validationFunctions = testStructure.validationFunctions, stateRepresentationFunction = testStructure.stateRepresentation, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt index 9c7097e8c..67f4f2162 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -131,7 +131,7 @@ abstract class Options, CTEST : CTestConfiguration> { this.minimizeFailedScenario = minimizeFailedScenario } - abstract fun createTestConfigurations(testClass: Class<*>): CTEST + abstract fun createTestConfigurations(testClass: TestClass): CTEST /** * Set logging level, [DEFAULT_LOG_LEVEL] is used by default. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 2eb386c3a..198f0fa2d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -41,6 +41,8 @@ import kotlin.reflect.full.* import kotlin.reflect.jvm.* actual class TestClass(val clazz: Class<*>) { + actual val name = clazz.name + actual fun createInstance(): Any = clazz.getDeclaredConstructor().newInstance() } @@ -48,8 +50,8 @@ actual class SequentialSpecification(val kClass: KClass<*>) { actual fun getInitialState(): Any = kClass.getConstructor().newInstance() } -fun chooseSequentialSpecification(sequentialSpecificationByUser: KClass<*>?, testClass: Class<*>): SequentialSpecification = - if (sequentialSpecificationByUser === DummySequentialSpecification::class || sequentialSpecificationByUser == null) SequentialSpecification(testClass.kotlin) +fun chooseSequentialSpecification(sequentialSpecificationByUser: KClass<*>?, testClass: TestClass): SequentialSpecification = + if (sequentialSpecificationByUser === DummySequentialSpecification::class || sequentialSpecificationByUser == null) SequentialSpecification(testClass.clazz.kotlin) else SequentialSpecification(sequentialSpecificationByUser) /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 021f139ec..f09d33382 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -47,7 +47,7 @@ private typealias SuspensionPointResultWithContinuation = AtomicReference, + testClass: TestClass, validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, private val timeoutMs: Long, // for deadlock or livelock detection @@ -144,7 +144,7 @@ internal open class ParallelThreadsRunner( } private fun reset() { - testInstance = testClass.getDeclaredConstructor().newInstance() + testInstance = testClass.createInstance() testThreadExecutions.forEachIndexed { t, ex -> ex.testInstance = testInstance val threads = scenario.threads diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index 53a600cad..01a165e44 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -37,12 +37,12 @@ import java.io.* */ actual abstract class Runner protected constructor( protected val strategy: Strategy, - private val _testClass: Class<*>, // will be transformed later + private val _testClass: TestClass, // will be transformed later protected val validationFunctions: List, protected val stateRepresentationFunction: StateRepresentationFunction? ) : Closeable { protected actual var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` - protected lateinit var testClass: Class<*> // not available before `initialize` call + protected lateinit var testClass: TestClass // not available before `initialize` call @Suppress("LeakingThis") val classLoader: ExecutionClassLoader = if (needsTransformation() || strategy.needsTransformation()) TransformationClassLoader(strategy, this) else ExecutionClassLoader() @@ -54,7 +54,7 @@ actual abstract class Runner protected constructor( */ actual open fun initialize() { scenario = strategy.scenario.convertForLoader(classLoader) - testClass = loadClass(_testClass.typeName) + testClass = TestClass(loadClass(_testClass.clazz.typeName)) } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java index 51e6211a0..53141033b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java @@ -111,7 +111,7 @@ public static TestThreadExecution create(Runner runner, int iThread, List String internalClassName = className.replace('.', '/'); List objArgs = new ArrayList<>(); Class clz = runner.getClassLoader().defineClass(className, - generateClass(internalClassName, getType(runner.getTestClass()), iThread, actors, objArgs, completions, scenarioContainsSuspendableActors)); + generateClass(internalClassName, getType(runner.getTestClass().getClazz()), iThread, actors, objArgs, completions, scenarioContainsSuspendableActors)); try { TestThreadExecution execution = clz.getDeclaredConstructor().newInstance(); execution.runner = runner; diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt index 741d4dcb0..7fd6e887d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt @@ -30,7 +30,7 @@ import kotlin.reflect.* * A common configuration for managed strategies. */ abstract class ManagedCTestConfiguration( - testClass: Class<*>, iterations: Int, + testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, val checkObstructionFreedom: Boolean, val hangingDetectionThreshold: Int, val invocationsPerIteration: Int, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 45c58728c..7b985d220 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -43,7 +43,7 @@ import kotlin.collections.set * and class loading problems. */ abstract class ManagedStrategy( - private val testClass: Class<*>, + private val testClass: TestClass, scenario: ExecutionScenario, private val verifier: Verifier, private val validationFunctions: List, @@ -723,7 +723,7 @@ abstract class ManagedStrategy( * to the strategy so that it can known about some required events. */ private class ManagedStrategyRunner( - private val managedStrategy: ManagedStrategy, testClass: Class<*>, validationFunctions: List, + private val managedStrategy: ManagedStrategy, testClass: TestClass, validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, timeoutMs: Long, useClocks: UseClocks ) : ParallelThreadsRunner(managedStrategy, testClass, validationFunctions, stateRepresentationFunction, timeoutMs, useClocks) { override fun onStart(iThread: Int) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategyStateHolder.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategyStateHolder.kt index 3442d4d42..ac1803f32 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategyStateHolder.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategyStateHolder.kt @@ -41,7 +41,7 @@ internal object ManagedStrategyStateHolder { /** * Sets the strategy and its initial state for the specified class loader. */ - fun setState(loader: ClassLoader, strategy: ManagedStrategy?, testClass: Class) { + fun setState(loader: ClassLoader, strategy: ManagedStrategy?, testClass: TestClass) { try { val clazz = loader.loadClass(ManagedStrategyStateHolder::class.java.canonicalName) clazz.getField("strategy")[null] = strategy @@ -57,17 +57,17 @@ internal object ManagedStrategyStateHolder { /** * Prepare the state for the specified class loader for the next invocation. */ - fun resetState(loader: ClassLoader, testClass: Class) { + fun resetState(loader: ClassLoader, testClass: TestClass) { try { val clazz = loader.loadClass(ManagedStrategyStateHolder::class.java.canonicalName) - clazz.getMethod("resetStateImpl", Class::class.java).invoke(null, testClass) + clazz.getMethod("resetStateImpl", TestClass::class.java).invoke(null, testClass) } catch (e: Exception) { throw IllegalStateException("Cannot set state to ManagedStateHolder", e) } } @JvmStatic - fun resetStateImpl(testClass: Class) { + fun resetStateImpl(testClass: TestClass) { random!!.setSeed(INITIAL_SEED) objectManager = ObjectManager(testClass) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ObjectManager.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ObjectManager.kt index e0dbd9967..9d08a60c8 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ObjectManager.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ObjectManager.kt @@ -21,6 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck.strategy.managed +import org.jetbrains.kotlinx.lincheck.* import java.util.* /** @@ -34,7 +35,7 @@ import java.util.* * associated with the corresponding field names, so that in the trace users see * the field names instead of something like `AtomicInteger@100500`. */ -internal class ObjectManager(private val testClass: Class) { +internal class ObjectManager(private val testClass: TestClass) { // For each local object store all objects that depend on it (e.g, are referenced by it). // Non-local objects are not presented in this map. private val localObjects = IdentityHashMap>() diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index 366abe2be..cb6529d4d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -32,7 +32,7 @@ import kotlin.reflect.* /** * Configuration for [random search][ModelCheckingStrategy] strategy. */ -class ModelCheckingCTestConfiguration(testClass: Class<*>, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, +class ModelCheckingCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, invocationsPerIteration: Int, guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, @@ -40,7 +40,7 @@ class ModelCheckingCTestConfiguration(testClass: Class<*>, iterations: Int, thre ) : ManagedCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs, eliminateLocalObjects, verboseTrace) { - override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, + override fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier): Strategy = ModelCheckingStrategy(this, testClass, scenario, validationFunctions, stateRepresentationFunction, verifier) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt index b3c18c63d..ad4006640 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlinx.lincheck.strategy.managed.* * Options for [model checking][ModelCheckingStrategy] strategy. */ class ModelCheckingOptions : ManagedOptions() { - override fun createTestConfigurations(testClass: Class<*>): ModelCheckingCTestConfiguration { + override fun createTestConfigurations(testClass: TestClass): ModelCheckingCTestConfiguration { return ModelCheckingCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, verifier, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt index 07e4b4502..d50473030 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt @@ -46,7 +46,7 @@ import kotlin.random.* */ internal class ModelCheckingStrategy( testCfg: ModelCheckingCTestConfiguration, - testClass: Class<*>, + testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, stateRepresentation: StateRepresentationFunction?, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index 7a9bfe54a..c5b52997f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -29,13 +29,13 @@ import kotlin.reflect.* /** * Configuration for [stress][StressStrategy] strategy. */ -class StressCTestConfiguration(testClass: Class<*>, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, +class StressCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, sequentialSpecification: SequentialSpecification?, timeoutMs: Long ) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs) { - override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunctions: List, + override fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier) = StressStrategy(this, testClass, scenario, validationFunctions, stateRepresentationFunction, verifier) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt index c7c690d64..dee5b107a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt @@ -21,8 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck.strategy.stress -import org.jetbrains.kotlinx.lincheck.Options -import org.jetbrains.kotlinx.lincheck.chooseSequentialSpecification +import org.jetbrains.kotlinx.lincheck.* /** * Options for [stress][StressStrategy] strategy. @@ -37,7 +36,7 @@ open class StressOptions : Options() { invocationsPerIteration = invocations } - override fun createTestConfigurations(testClass: Class<*>): StressCTestConfiguration { + override fun createTestConfigurations(testClass: TestClass): StressCTestConfiguration { return StressCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, verifier, invocationsPerIteration, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, chooseSequentialSpecification(sequentialSpecification, testClass), timeoutMs) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index e66d2e57c..6ef9c466b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -30,7 +30,7 @@ import java.lang.reflect.* class StressStrategy( testCfg: StressCTestConfiguration, - testClass: Class<*>, + testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt index 9927c438b..975cf2e71 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerExceptionTest.kt @@ -110,7 +110,7 @@ class ParallelThreadsRunnerExceptionTest { } } ParallelThreadsRunner( - strategy = mockStrategy(scenario), testClass = testClass, validationFunctions = emptyList(), + strategy = mockStrategy(scenario), testClass = TestClass(testClass), validationFunctions = emptyList(), stateRepresentationFunction = null, useClocks = RANDOM, timeoutMs = DEFAULT_TIMEOUT_MS ).use { runner -> runner.initialize() @@ -135,7 +135,7 @@ class ParallelThreadsRunnerExceptionTest { } } ParallelThreadsRunner( - strategy = mockStrategy(scenario), testClass = testClass, validationFunctions = emptyList(), + strategy = mockStrategy(scenario), testClass = TestClass(testClass), validationFunctions = emptyList(), stateRepresentationFunction = null, useClocks = RANDOM, timeoutMs = DEFAULT_TIMEOUT_MS ).use { runner -> runner.initialize() @@ -154,7 +154,7 @@ class ParallelThreadsRunnerExceptionTest { } } ParallelThreadsRunner( - strategy = mockStrategy(scenario), testClass = testClass, validationFunctions = emptyList(), + strategy = mockStrategy(scenario), testClass = TestClass(testClass), validationFunctions = emptyList(), stateRepresentationFunction = null, useClocks = RANDOM, timeoutMs = DEFAULT_TIMEOUT_MS ).use { runner -> runner.initialize() diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java index 787399b16..5bca061e2 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java @@ -45,7 +45,7 @@ public LincheckFailure run() { throw new UnsupportedOperationException(); } }; - runner = new Runner(strategy, ArrayDeque.class, emptyList(), null) { + runner = new Runner(strategy, new TestClass(ArrayDeque.class), emptyList(), null) { @Override public InvocationResult run() { throw new UnsupportedOperationException(); From ec80ba5c288a5c0c833d54b779d95522a4228d67 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 11 Feb 2021 20:42:47 +0300 Subject: [PATCH 15/72] Move Options to common, strange fixes for SequentialSpecification, VerifierClass, ExecutionGeneratorClass --- .../kotlinx/lincheck/CTestConfiguration.kt | 41 ++++- .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 8 +- .../org/jetbrains/kotlinx/lincheck/Options.kt | 170 ++++++++++++++++++ .../kotlinx/lincheck/runner/Runner.kt | 116 ------------ .../lincheck/verifier/AbstractLTSVerifier.kt | 2 +- .../lincheck/verifier/EpsilonVerifier.kt | 2 +- .../kotlinx/lincheck/verifier/LTS.kt | 8 +- .../verifier/SerializabilityVerifier.kt | 2 +- .../kotlinx/lincheck/verifier/Verifier.kt | 2 +- .../LinearizabilityVerifier.kt | 2 +- .../quiescent/QuiescentConsistencyVerifier.kt | 2 +- .../kotlinx/lincheck/CTestConfiguration.kt | 44 +---- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 12 +- .../org/jetbrains/kotlinx/lincheck/Options.kt | 164 ++--------------- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 10 +- .../lincheck/runner/ParallelThreadsRunner.kt | 1 - .../kotlinx/lincheck/runner/Runner.kt | 34 ++-- .../managed/ManagedCTestConfiguration.kt | 4 +- .../strategy/managed/ManagedStrategy.kt | 1 - .../ModelCheckingCTestConfiguration.kt | 9 +- .../stress/StressCTestConfiguration.kt | 4 +- .../lincheck/strategy/stress/StressOptions.kt | 16 +- .../strategy/stress/StressStrategy.kt | 1 - .../kotlinx/lincheck/verifier/JvmLTS.kt | 7 +- .../lincheck/test/PromptCancellationTest.kt | 6 +- .../test/verifier/CustomScenarioDSL.kt | 2 +- 26 files changed, 281 insertions(+), 389 deletions(-) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt delete mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 18299251b..ba11a4cf0 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -1,16 +1,41 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* import kotlin.reflect.* /** * Abstract configuration for different lincheck modes. */ -expect abstract class CTestConfiguration { - val iterations: Int - val threads: Int - val actorsPerThread: Int - val actorsBefore: Int - val actorsAfter: Int - val generatorClass: KClass -} \ No newline at end of file +abstract class CTestConfiguration( + val testClass: TestClass, + val iterations: Int, + val threads: Int, + val actorsPerThread: Int, + val actorsBefore: Int, + val actorsAfter: Int, + val generatorClass: ExecutionGeneratorClass, + val verifierClass: VerifierClass, + val requireStateEquivalenceImplCheck: Boolean, + val minimizeFailedScenario: Boolean, + val sequentialSpecification: SequentialSpecification<*>?, + val timeoutMs: Long +) { + abstract fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier): Strategy + + companion object { + const val DEFAULT_ITERATIONS = 100 + const val DEFAULT_THREADS = 2 + const val DEFAULT_ACTORS_PER_THREAD = 5 + const val DEFAULT_ACTORS_BEFORE = 5 + const val DEFAULT_ACTORS_AFTER = 5 + const val DEFAULT_MINIMIZE_ERROR = true + const val DEFAULT_TIMEOUT_MS: Long = 10000 + } +} + +expect val DEFAULT_EXECUTION_GENERATOR: ExecutionGeneratorClass +expect val DEFAULT_VERIFIER: VerifierClass \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index af0a90490..9dda28b53 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -24,6 +24,7 @@ package org.jetbrains.kotlinx.lincheck import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.execution.* import kotlin.coroutines.* +import kotlin.reflect.* expect class TestClass { val name: String @@ -31,9 +32,10 @@ expect class TestClass { fun createInstance(): Any } -expect class SequentialSpecification { - fun getInitialState(): Any -} +expect class SequentialSpecification +expect fun SequentialSpecification.getInitialState(): T + +expect fun chooseSequentialSpecification(sequentialSpecificationByUser: SequentialSpecification<*>?, testClass: TestClass): SequentialSpecification<*> object CancellableContinuationHolder { var storedLastCancellableCont: CancellableContinuation<*>? = null diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt new file mode 100644 index 000000000..30376f054 --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -0,0 +1,170 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.reflect.* + +expect class ExecutionGeneratorClass +expect class VerifierClass + +/** + * Abstract class for test options. + */ +abstract class Options, CTEST : CTestConfiguration> { + internal var logLevel = DEFAULT_LOG_LEVEL + protected var iterations = CTestConfiguration.DEFAULT_ITERATIONS + protected var threads = CTestConfiguration.DEFAULT_THREADS + protected var actorsPerThread = CTestConfiguration.DEFAULT_ACTORS_PER_THREAD + protected var actorsBefore = CTestConfiguration.DEFAULT_ACTORS_BEFORE + protected var actorsAfter = CTestConfiguration.DEFAULT_ACTORS_AFTER + var executionGenerator = DEFAULT_EXECUTION_GENERATOR + var verifier = DEFAULT_VERIFIER + protected var requireStateEquivalenceImplementationCheck = true + protected var minimizeFailedScenario = CTestConfiguration.DEFAULT_MINIMIZE_ERROR + var sequentialSpecification: SequentialSpecification<*>? = null + protected var timeoutMs: Long = CTestConfiguration.DEFAULT_TIMEOUT_MS + + /** + * Number of different test scenarios to be executed + */ + fun iterations(iterations: Int): OPT = applyAndCast { + this.iterations = iterations + } + + /** + * Use the specified number of threads for the parallel part of an execution. + * + * Note, that the the actual number of threads can be less due to some restrictions + * like [Operation.runOnce]. + * + * @see ExecutionScenario.parallelExecution + */ + fun threads(threads: Int): OPT = applyAndCast { + this.threads = threads + } + + /** + * Generate the specified number of operations for each thread of the parallel part of an execution. + * + * Note, that the the actual number of operations can be less due to some restrictions + * like [Operation.runOnce]. + * + * @see ExecutionScenario.parallelExecution + */ + fun actorsPerThread(actorsPerThread: Int): OPT = applyAndCast { + this.actorsPerThread = actorsPerThread + } + + /** + * Generate the specified number of operation for the initial sequential part of an execution. + * + * Note, that the the actual number of operations can be less due to some restrictions + * like [Operation.runOnce]. + * + * @see ExecutionScenario.initExecution + */ + fun actorsBefore(actorsBefore: Int): OPT = applyAndCast { + this.actorsBefore = actorsBefore + } + + /** + * Generate the specified number of operation for the last sequential part of an execution. + * + * Note, that the the actual number of operations can be less due to some restrictions + * like [Operation.runOnce]. + * + * @see ExecutionScenario.postExecution + */ + fun actorsAfter(actorsAfter: Int): OPT = applyAndCast { + this.actorsAfter = actorsAfter + } + + /** + * Use the specified execution generator. + */ + fun executionGenerator(executionGenerator: ExecutionGeneratorClass): OPT = applyAndCast { + this.executionGenerator = executionGenerator + } + + /** + * Use the specified verifier. + */ + fun verifier(verifier: VerifierClass): OPT = applyAndCast { + this.verifier = verifier + } + + /** + * Require correctness check of test instance state equivalency relation defined by the user. + * It checks whether two new instances of a test class are equal. + * If the check fails [[IllegalStateException]] is thrown. + */ + fun requireStateEquivalenceImplCheck(require: Boolean): OPT = applyAndCast { + this.requireStateEquivalenceImplementationCheck = require + } + + /** + * If this feature is enabled and an invalid interleaving has been found, + * *lincheck* tries to minimize the corresponding scenario in order to + * construct a smaller one so that the test fails on it as well. + * Enabled by default. + */ + fun minimizeFailedScenario(minimizeFailedScenario: Boolean): OPT = applyAndCast { + this.minimizeFailedScenario = minimizeFailedScenario + } + + abstract fun createTestConfigurations(testClass: TestClass): CTEST + + /** + * Set logging level, [DEFAULT_LOG_LEVEL] is used by default. + */ + fun logLevel(logLevel: LoggingLevel): OPT = applyAndCast { + this.logLevel = logLevel + } + + /** + * The specified class defines the sequential behavior of the testing data structure; + * it is used by [Verifier] to build a labeled transition system, + * and should have the same methods as the testing data structure. + * + * By default, the provided concurrent implementation is used in a sequential way. + */ + fun sequentialSpecification(clazz: SequentialSpecification<*>?): OPT = applyAndCast { + this.sequentialSpecification = clazz + } + + /** + * Internal, DO NOT USE. + */ + internal fun invocationTimeout(timeoutMs: Long): OPT = applyAndCast { + this.timeoutMs = timeoutMs + } + + companion object { + @Suppress("UNCHECKED_CAST") + internal inline fun , CTEST : CTestConfiguration> Options.applyAndCast( + block: Options.() -> Unit + ) = this.apply { + block() + } as OPT + } +} diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt deleted file mode 100644 index 930b45a62..000000000 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2021 JetBrains s.r.o. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * - */ - -package org.jetbrains.kotlinx.lincheck.runner - -import org.jetbrains.kotlinx.lincheck.execution.* - -/** - * Runner determines how to run your concurrent test. In order to support techniques - * like fibers, it may require code transformation, so that [createTransformer] should - * provide the corresponding transformer and [needsTransformation] should return `true`. - */ -expect abstract class Runner { - protected var scenario: ExecutionScenario - - /** - * This method is a part of `Runner` initialization and should be invoked after this runner - * creation. It is separated from the constructor to perform the strategy initialization at first. - */ - open fun initialize() - - /** - * Returns the current state representation of the test instance constructed via - * the function marked with [StateRepresentation] annotation, or `null` - * if no such function is provided. - * - * Please note, that it is unsafe to call this method concurrently with the running scenario. - * However, it is fine to call it if the execution is paused somewhere in the middle. - */ - open fun constructStateRepresentation(): String? - - /** - * This method should return `true` if code transformation - * is required for this runner; returns `false` by default. - */ - open fun needsTransformation(): Boolean - - /** - * This method is invoked by every test thread as the first operation. - * @param iThread number of invoking thread - */ - open fun onStart(iThread: Int) - - /** - * This method is invoked by every test thread as the last operation - * if no exception has been thrown. - * @param iThread number of invoking thread - */ - open fun onFinish(iThread: Int) - - /** - * This method is invoked by the corresponding test thread - * when an unexpected exception is thrown. - */ - open fun onFailure(iThread: Int, e: Throwable) - - /** - * This method is invoked by the corresponding test thread - * when the current coroutine suspends. - * @param iThread number of invoking thread - */ - open fun afterCoroutineSuspended(iThread: Int) - - /** - * This method is invoked by the corresponding test thread - * when the current coroutine is resumed. - */ - open fun afterCoroutineResumed(iThread: Int) - - /** - * This method is invoked by the corresponding test thread - * when the current coroutine is cancelled. - */ - open fun afterCoroutineCancelled(iThread: Int) - - /** - * Returns `true` if the coroutine corresponding to - * the actor `actorId` in the thread `iThread` is resumed. - */ - open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean - - /** - * Is invoked before each actor execution from the specified thread. - * The invocations are inserted into the generated code. - */ - fun onActorStart(iThread: Int) - - /** - * Closes the resources used in this runner. - */ - fun close() - - /** - * @return whether all scenario threads are completed or suspended - * Used by generated code. - */ - val isParallelExecutionCompleted: Boolean - -} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt index 5f91ac66c..ac097cbfb 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt @@ -34,7 +34,7 @@ import kotlin.reflect.* * the next possible transitions using [VerifierContext.nextContext] function. This verifier * uses depth-first search to find a proper path. */ -abstract class AbstractLTSVerifier(protected val sequentialSpecification: SequentialSpecification) : CachedVerifier() { +abstract class AbstractLTSVerifier(protected val sequentialSpecification: SequentialSpecification<*>) : CachedVerifier() { abstract val lts: LTS abstract fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult): VerifierContext diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt index 66ee53cec..ac52153a6 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario /** * This verifier does nothing and could be used for performance benchmarking. */ -class EpsilonVerifier(sequentialSpecification: SequentialSpecification) : Verifier { +class EpsilonVerifier(sequentialSpecification: SequentialSpecification<*>) : Verifier { override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean = true // Always correct results :) override fun checkStateEquivalenceImplementation() {} diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index bee112461..372326acd 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -33,7 +33,7 @@ import kotlin.reflect.* typealias RemappingFunction = IntArray typealias ResumedTickets = Set -expect fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification): SequentialSpecification +expect fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification<*>): SequentialSpecification /** * Common interface for different labeled transition systems, which several correctness formalisms use. @@ -55,9 +55,9 @@ expect fun loadSequentialSpecification(sequentialSpecification: SequentialSpecif * Practically, Kotlin implementation of such operations via suspend functions is supported. */ -class LTS(sequentialSpecification: SequentialSpecification) { +class LTS(sequentialSpecification: SequentialSpecification<*>) { // we should transform the specification with `CancellabilitySupportClassTransformer` - private val sequentialSpecification: SequentialSpecification = loadSequentialSpecification(sequentialSpecification) + private val sequentialSpecification: SequentialSpecification = loadSequentialSpecification(sequentialSpecification) /** * Cache with all LTS states in order to reuse the equivalent ones. @@ -278,7 +278,7 @@ class LTS(sequentialSpecification: SequentialSpecification) { ).intern(null) { _, _ -> initialState } } - private fun createInitialStateInstance() = sequentialSpecification.getInitialState() + private fun createInitialStateInstance(): Any = sequentialSpecification.getInitialState() fun checkStateEquivalenceImplementation() { val i1 = createInitialStateInstance() diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt index 5a0e103c4..cedae80b3 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* * It just tries to find any operations sequence which execution produces the same results. */ class SerializabilityVerifier( - sequentialSpecification: SequentialSpecification + sequentialSpecification: SequentialSpecification<*> ) : CachedVerifier() { private val linerizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt index 5f8dc4ddc..1aeb49915 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt @@ -31,7 +31,7 @@ import kotlin.collections.HashSet * * * IMPORTANT! - * All implementations should have `(sequentialSpecification: SequentialSpecification)` constructor, + * All implementations should have `(sequentialSpecification: SequentialSpecification<*>)` constructor, * which takes the scenario to be tested and the correct sequential implementation of the testing data structure. */ interface Verifier { diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt index 52e7c76e8..79d203731 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt @@ -35,7 +35,7 @@ import kotlin.reflect.* * This verifier is based on [AbstractLTSVerifier] and caches the already processed results * for performance improvement (see [CachedVerifier]). */ -class LinearizabilityVerifier(sequentialSpecification: SequentialSpecification) : AbstractLTSVerifier(sequentialSpecification) { +class LinearizabilityVerifier(sequentialSpecification: SequentialSpecification<*>) : AbstractLTSVerifier(sequentialSpecification) { override val lts: LTS = LTS(sequentialSpecification = sequentialSpecification) override fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult) = diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 61b1a0416..07d154275 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -35,7 +35,7 @@ import kotlin.reflect.* * However, we believe that quiescent points do not occur * in practice while supporting them complicates the implementation. */ -class QuiescentConsistencyVerifier(sequentialSpecification: SequentialSpecification) : Verifier { +class QuiescentConsistencyVerifier(sequentialSpecification: SequentialSpecification<*>) : Verifier { private val linearizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) private val scenarioMapping: MutableMap = HashMap() // TODO change logic to WeakHashMap diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index d5e99b2e4..371210b8a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -23,7 +23,6 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.CTestConfiguration.Companion.DEFAULT_TIMEOUT_MS import org.jetbrains.kotlinx.lincheck.execution.* -import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* import org.jetbrains.kotlinx.lincheck.strategy.managed.ManagedCTestConfiguration.Companion.DEFAULT_ELIMINATE_LOCAL_OBJECTS import org.jetbrains.kotlinx.lincheck.strategy.managed.ManagedCTestConfiguration.Companion.DEFAULT_VERBOSE_TRACE @@ -31,59 +30,28 @@ import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.* import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* -import java.lang.reflect.* import kotlin.reflect.* -/** - * Abstract configuration for different lincheck modes. - */ -actual abstract class CTestConfiguration( - val testClass: TestClass, - actual val iterations: Int, - actual val threads: Int, - actual val actorsPerThread: Int, - actual val actorsBefore: Int, - actual val actorsAfter: Int, - actual val generatorClass: KClass, - val verifierClass: KClass, - val requireStateEquivalenceImplCheck: Boolean, - val minimizeFailedScenario: Boolean, - val sequentialSpecification: SequentialSpecification?, - val timeoutMs: Long -) { - abstract fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, - stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier): Strategy - - companion object { - const val DEFAULT_ITERATIONS = 100 - const val DEFAULT_THREADS = 2 - const val DEFAULT_ACTORS_PER_THREAD = 5 - const val DEFAULT_ACTORS_BEFORE = 5 - const val DEFAULT_ACTORS_AFTER = 5 - val DEFAULT_EXECUTION_GENERATOR: KClass = RandomExecutionGenerator::class - val DEFAULT_VERIFIER: KClass = LinearizabilityVerifier::class - const val DEFAULT_MINIMIZE_ERROR = true - const val DEFAULT_TIMEOUT_MS: Long = 10000 - } -} +actual val DEFAULT_EXECUTION_GENERATOR: ExecutionGeneratorClass = RandomExecutionGenerator::class.java +actual val DEFAULT_VERIFIER: VerifierClass = LinearizabilityVerifier::class.java internal fun createFromTestClassAnnotations(testClass: Class<*>): List { val stressConfigurations: List = testClass.getAnnotationsByType(StressCTest::class.java) .map { ann: StressCTest -> StressCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator, ann.verifier, ann.invocationsPerIteration, + ann.generator.java, ann.verifier.java, ann.invocationsPerIteration, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, - chooseSequentialSpecification(ann.sequentialSpecification, TestClass(testClass)), DEFAULT_TIMEOUT_MS + chooseSequentialSpecification(ann.sequentialSpecification.java, TestClass(testClass)), DEFAULT_TIMEOUT_MS ) } val modelCheckingConfigurations: List = testClass.getAnnotationsByType(ModelCheckingCTest::class.java) .map { ann: ModelCheckingCTest -> ModelCheckingCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator, ann.verifier, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, + ann.generator.java, ann.verifier.java, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, ann.invocationsPerIteration, ManagedCTestConfiguration.DEFAULT_GUARANTEES, ann.requireStateEquivalenceImplCheck, - ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification, TestClass(testClass)), + ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification.java, TestClass(testClass)), DEFAULT_TIMEOUT_MS, DEFAULT_ELIMINATE_LOCAL_OBJECTS, DEFAULT_VERBOSE_TRACE ) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index df54b7f83..bf0201b24 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -21,15 +21,11 @@ */ package org.jetbrains.kotlinx.lincheck -import org.jetbrains.kotlinx.lincheck.CTestConfiguration.* import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* -import java.lang.reflect.* import kotlin.reflect.* -import kotlin.reflect.KTypeProjection.Companion.STAR -import kotlin.reflect.full.* /** * This class runs concurrent tests. @@ -179,10 +175,10 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { private fun CTestConfiguration.createVerifier() = - verifierClass.constructors.find { it.parameters.size == 1 && it.parameters[0].type == SequentialSpecification::class.createType() }!! - .call(sequentialSpecification).also { - if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() - } + verifierClass.constructors.find{ it.parameters.size == 1 && it.parameters[0].type == SequentialSpecification::class.java }!! + .newInstance(sequentialSpecification).also { + if (requireStateEquivalenceImplCheck) (it as Verifier).checkStateEquivalenceImplementation() + } as Verifier private fun CTestConfiguration.createExecutionGenerator() = generatorClass.getConstructor( diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt index 67f4f2162..8857bf5ee 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -1,169 +1,27 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck -import org.jetbrains.kotlinx.lincheck.execution.* -import org.jetbrains.kotlinx.lincheck.annotations.Operation -import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.reflect.* -/** - * Abstract class for test options. - */ -abstract class Options, CTEST : CTestConfiguration> { - internal var logLevel = DEFAULT_LOG_LEVEL - protected var iterations = CTestConfiguration.DEFAULT_ITERATIONS - protected var threads = CTestConfiguration.DEFAULT_THREADS - protected var actorsPerThread = CTestConfiguration.DEFAULT_ACTORS_PER_THREAD - protected var actorsBefore = CTestConfiguration.DEFAULT_ACTORS_BEFORE - protected var actorsAfter = CTestConfiguration.DEFAULT_ACTORS_AFTER - protected var executionGenerator = CTestConfiguration.DEFAULT_EXECUTION_GENERATOR - protected var verifier = CTestConfiguration.DEFAULT_VERIFIER - protected var requireStateEquivalenceImplementationCheck = true - protected var minimizeFailedScenario = CTestConfiguration.DEFAULT_MINIMIZE_ERROR - protected var sequentialSpecification: KClass<*>? = null - protected var timeoutMs: Long = CTestConfiguration.DEFAULT_TIMEOUT_MS - - /** - * Number of different test scenarios to be executed - */ - fun iterations(iterations: Int): OPT = applyAndCast { - this.iterations = iterations - } - - /** - * Use the specified number of threads for the parallel part of an execution. - * - * Note, that the the actual number of threads can be less due to some restrictions - * like [Operation.runOnce]. - * - * @see ExecutionScenario.parallelExecution - */ - fun threads(threads: Int): OPT = applyAndCast { - this.threads = threads - } - - /** - * Generate the specified number of operations for each thread of the parallel part of an execution. - * - * Note, that the the actual number of operations can be less due to some restrictions - * like [Operation.runOnce]. - * - * @see ExecutionScenario.parallelExecution - */ - fun actorsPerThread(actorsPerThread: Int): OPT = applyAndCast { - this.actorsPerThread = actorsPerThread - } - - /** - * Generate the specified number of operation for the initial sequential part of an execution. - * - * Note, that the the actual number of operations can be less due to some restrictions - * like [Operation.runOnce]. - * - * @see ExecutionScenario.initExecution - */ - fun actorsBefore(actorsBefore: Int): OPT = applyAndCast { - this.actorsBefore = actorsBefore - } - - /** - * Generate the specified number of operation for the last sequential part of an execution. - * - * Note, that the the actual number of operations can be less due to some restrictions - * like [Operation.runOnce]. - * - * @see ExecutionScenario.postExecution - */ - fun actorsAfter(actorsAfter: Int): OPT = applyAndCast { - this.actorsAfter = actorsAfter - } - - /** - * Use the specified execution generator. - */ - fun executionGenerator(executionGenerator: Class): OPT = applyAndCast { - this.executionGenerator = executionGenerator.kotlin - } - - /** - * Use the specified verifier. - */ - fun verifier(verifier: Class): OPT = applyAndCast { - this.verifier = verifier.kotlin - } - - /** - * Require correctness check of test instance state equivalency relation defined by the user. - * It checks whether two new instances of a test class are equal. - * If the check fails [[IllegalStateException]] is thrown. - */ - fun requireStateEquivalenceImplCheck(require: Boolean): OPT = applyAndCast { - requireStateEquivalenceImplementationCheck = require - } - - /** - * If this feature is enabled and an invalid interleaving has been found, - * *lincheck* tries to minimize the corresponding scenario in order to - * construct a smaller one so that the test fails on it as well. - * Enabled by default. - */ - fun minimizeFailedScenario(minimizeFailedScenario: Boolean): OPT = applyAndCast { - this.minimizeFailedScenario = minimizeFailedScenario - } - - abstract fun createTestConfigurations(testClass: TestClass): CTEST - - /** - * Set logging level, [DEFAULT_LOG_LEVEL] is used by default. - */ - fun logLevel(logLevel: LoggingLevel): OPT = applyAndCast { - this.logLevel = logLevel - } - - /** - * The specified class defines the sequential behavior of the testing data structure; - * it is used by [Verifier] to build a labeled transition system, - * and should have the same methods as the testing data structure. - * - * By default, the provided concurrent implementation is used in a sequential way. - */ - fun sequentialSpecification(clazz: Class<*>?): OPT = applyAndCast { - sequentialSpecification = clazz?.kotlin - } - - /** - * Internal, DO NOT USE. - */ - internal fun invocationTimeout(timeoutMs: Long): OPT = applyAndCast { - this.timeoutMs = timeoutMs - } - - companion object { - @Suppress("UNCHECKED_CAST") - private inline fun , CTEST : CTestConfiguration> Options.applyAndCast( - block: Options.() -> Unit - ) = this.apply { - block() - } as OPT - } -} +actual typealias ExecutionGeneratorClass = Class +actual typealias VerifierClass = Class +actual typealias SequentialSpecification = Class +actual fun SequentialSpecification.getInitialState(): T = this.newInstance() \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 198f0fa2d..60670e46a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -46,13 +46,9 @@ actual class TestClass(val clazz: Class<*>) { actual fun createInstance(): Any = clazz.getDeclaredConstructor().newInstance() } -actual class SequentialSpecification(val kClass: KClass<*>) { - actual fun getInitialState(): Any = kClass.getConstructor().newInstance() -} - -fun chooseSequentialSpecification(sequentialSpecificationByUser: KClass<*>?, testClass: TestClass): SequentialSpecification = - if (sequentialSpecificationByUser === DummySequentialSpecification::class || sequentialSpecificationByUser == null) SequentialSpecification(testClass.clazz.kotlin) - else SequentialSpecification(sequentialSpecificationByUser) +actual fun chooseSequentialSpecification(sequentialSpecificationByUser: SequentialSpecification<*>?, testClass: TestClass): SequentialSpecification<*> = + if (sequentialSpecificationByUser === DummySequentialSpecification::class.java || sequentialSpecificationByUser == null) testClass.clazz + else sequentialSpecificationByUser /** * Executes the specified actor on the sequential specification instance and returns its result. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index f09d33382..8eaccd958 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -30,7 +30,6 @@ import org.jetbrains.kotlinx.lincheck.runner.FixedActiveThreadsExecutor.TestThre import org.jetbrains.kotlinx.lincheck.runner.UseClocks.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.objectweb.asm.* -import java.lang.reflect.* import java.util.concurrent.* import java.util.concurrent.atomic.* import kotlin.coroutines.* diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index 01a165e44..e05439b36 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -19,12 +19,12 @@ * . * #L% */ + package org.jetbrains.kotlinx.lincheck.runner import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.objectweb.asm.* -import java.lang.reflect.* import java.util.concurrent.atomic.* import org.jetbrains.kotlinx.lincheck.annotations.StateRepresentation import org.jetbrains.kotlinx.lincheck.execution.* @@ -35,13 +35,13 @@ import java.io.* * like fibers, it may require code transformation, so that [createTransformer] should * provide the corresponding transformer and [needsTransformation] should return `true`. */ -actual abstract class Runner protected constructor( +abstract class Runner protected constructor( protected val strategy: Strategy, private val _testClass: TestClass, // will be transformed later protected val validationFunctions: List, protected val stateRepresentationFunction: StateRepresentationFunction? ) : Closeable { - protected actual var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` + protected var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` protected lateinit var testClass: TestClass // not available before `initialize` call @Suppress("LeakingThis") val classLoader: ExecutionClassLoader = if (needsTransformation() || strategy.needsTransformation()) TransformationClassLoader(strategy, this) @@ -52,7 +52,7 @@ actual abstract class Runner protected constructor( * This method is a part of `Runner` initialization and should be invoked after this runner * creation. It is separated from the constructor to perform the strategy initialization at first. */ - actual open fun initialize() { + open fun initialize() { scenario = strategy.scenario.convertForLoader(classLoader) testClass = TestClass(loadClass(_testClass.clazz.typeName)) } @@ -65,7 +65,7 @@ actual abstract class Runner protected constructor( * Please note, that it is unsafe to call this method concurrently with the running scenario. * However, it is fine to call it if the execution is paused somewhere in the middle. */ - actual open fun constructStateRepresentation(): String? = null + open fun constructStateRepresentation(): String? = null /** * Loads the specified class via this runner' class loader. @@ -84,7 +84,7 @@ actual abstract class Runner protected constructor( * This method should return `true` if code transformation * is required for this runner; returns `false` by default. */ - actual open fun needsTransformation(): Boolean = false + open fun needsTransformation(): Boolean = false /** * Runs the next invocation. @@ -95,63 +95,63 @@ actual abstract class Runner protected constructor( * This method is invoked by every test thread as the first operation. * @param iThread number of invoking thread */ - actual open fun onStart(iThread: Int) {} + open fun onStart(iThread: Int) {} /** * This method is invoked by every test thread as the last operation * if no exception has been thrown. * @param iThread number of invoking thread */ - actual open fun onFinish(iThread: Int) {} + open fun onFinish(iThread: Int) {} /** * This method is invoked by the corresponding test thread * when an unexpected exception is thrown. */ - actual open fun onFailure(iThread: Int, e: Throwable) {} + open fun onFailure(iThread: Int, e: Throwable) {} /** * This method is invoked by the corresponding test thread * when the current coroutine suspends. * @param iThread number of invoking thread */ - actual open fun afterCoroutineSuspended(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + open fun afterCoroutineSuspended(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") /** * This method is invoked by the corresponding test thread * when the current coroutine is resumed. */ - actual open fun afterCoroutineResumed(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + open fun afterCoroutineResumed(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") /** * This method is invoked by the corresponding test thread * when the current coroutine is cancelled. */ - actual open fun afterCoroutineCancelled(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + open fun afterCoroutineCancelled(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") /** * Returns `true` if the coroutine corresponding to * the actor `actorId` in the thread `iThread` is resumed. */ - actual open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean = throw UnsupportedOperationException("Coroutines are not supported") + open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean = throw UnsupportedOperationException("Coroutines are not supported") /** * Is invoked before each actor execution from the specified thread. * The invocations are inserted into the generated code. */ - actual fun onActorStart(iThread: Int) { + fun onActorStart(iThread: Int) { strategy.onActorStart(iThread) } /** * Closes the resources used in this runner. */ - actual override fun close() {} + override fun close() {} /** * @return whether all scenario threads are completed or suspended * Used by generated code. */ - actual val isParallelExecutionCompleted: Boolean + val isParallelExecutionCompleted: Boolean get() = completedOrSuspendedThreads.get() == scenario.threads -} +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt index 7fd6e887d..1925eb64f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt @@ -32,10 +32,10 @@ import kotlin.reflect.* abstract class ManagedCTestConfiguration( testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: KClass, verifierClass: KClass, + generatorClass: ExecutionGeneratorClass, verifierClass: VerifierClass, val checkObstructionFreedom: Boolean, val hangingDetectionThreshold: Int, val invocationsPerIteration: Int, val guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: SequentialSpecification?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean + sequentialSpecification: SequentialSpecification<*>?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean ) : CTestConfiguration( testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 7b985d220..3dc2c8e17 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -30,7 +30,6 @@ import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.objectweb.asm.* import java.io.* -import java.lang.reflect.* import java.util.* import kotlin.collections.set diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index cb6529d4d..9af95a087 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -33,10 +33,11 @@ import kotlin.reflect.* * Configuration for [random search][ModelCheckingStrategy] strategy. */ class ModelCheckingCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, - actorsAfter: Int, generatorClass: KClass, verifierClass: KClass, - checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, invocationsPerIteration: Int, - guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: SequentialSpecification?, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean + actorsAfter: Int, generatorClass: ExecutionGeneratorClass, + verifierClass: VerifierClass, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, + invocationsPerIteration: Int, guarantees: List, requireStateEquivalenceCheck: Boolean, + minimizeFailedScenario: Boolean, sequentialSpecification: SequentialSpecification<*>?, timeoutMs: Long, + eliminateLocalObjects: Boolean, verboseTrace: Boolean ) : ManagedCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs, eliminateLocalObjects, verboseTrace) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index c5b52997f..de1f5e76e 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -30,9 +30,9 @@ import kotlin.reflect.* * Configuration for [stress][StressStrategy] strategy. */ class StressCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: KClass, verifierClass: KClass, + generatorClass: ExecutionGeneratorClass, verifierClass: VerifierClass, val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: SequentialSpecification?, timeoutMs: Long + sequentialSpecification: SequentialSpecification<*>?, timeoutMs: Long ) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs) { override fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt index dee5b107a..ad43f95da 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt @@ -1,23 +1,21 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.strategy.stress diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 6ef9c466b..bc2b62185 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* -import java.lang.reflect.* class StressStrategy( testCfg: StressCTestConfiguration, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt index 4d32aeb41..0041dd9aa 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt @@ -22,8 +22,5 @@ package org.jetbrains.kotlinx.lincheck.verifier import org.jetbrains.kotlinx.lincheck.* -actual fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification): SequentialSpecification = - SequentialSpecification( - TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv) } - .loadClass(sequentialSpecification.kClass.java.name)!!.kotlin - ) \ No newline at end of file +actual fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification<*>): SequentialSpecification = + TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv) }.loadClass(sequentialSpecification.name)!! \ No newline at end of file diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/PromptCancellationTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/PromptCancellationTest.kt index fd5f963a3..6f48cab24 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/PromptCancellationTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/PromptCancellationTest.kt @@ -32,7 +32,7 @@ import kotlin.reflect.* abstract class AbstractPromptCancellationTest( vararg expectedFailures: KClass, - val sequentialSpecification: KClass<*>? = null + val sequentialSpecification: SequentialSpecification<*>? = null ) : AbstractLincheckTest(*expectedFailures) { @Volatile private var returnResult = 0 @@ -80,7 +80,7 @@ abstract class AbstractPromptCancellationTest( actorsPerThread(1) actorsAfter(0) requireStateEquivalenceImplCheck(false) - sequentialSpecification?.let { sequentialSpecification(it.java) } + sequentialSpecification(this@AbstractPromptCancellationTest.sequentialSpecification) } } @@ -88,7 +88,7 @@ class CorrectPromptCancellationTest : AbstractPromptCancellationTest() class IncorrectPromptCancellationTest : AbstractPromptCancellationTest( IncorrectResultsFailure::class, - sequentialSpecification = IncorrectPromptCancellationSequential::class + sequentialSpecification = IncorrectPromptCancellationSequential::class.java ) class IncorrectPromptCancellationSequential { diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt index 2c5818e90..33e7cc857 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt @@ -58,7 +58,7 @@ fun verify( correct: Boolean ) { val (scenario, results) = scenarioWithResults(block) - val verifier = verifierClass.getConstructor(SequentialSpecification::class.java).newInstance(SequentialSpecification(testClass.kotlin)) + val verifier = verifierClass.getConstructor(SequentialSpecification::class.java).newInstance(testClass) val res = verifier.verifyResults(scenario, results) assert(res == correct) } From 069c0ba11ed062543f2b3d54e74042723bb12001 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 16 Feb 2021 16:08:04 +0300 Subject: [PATCH 16/72] move annotation to jvm, fix some unused expect-actual --- .../kotlinx/lincheck/annotations/LogLevel.kt | 29 ------ .../lincheck/annotations/OpGroupConfig.kt | 37 -------- .../kotlinx/lincheck/annotations/Operation.kt | 88 ------------------ .../kotlinx/lincheck/annotations/Param.kt | 57 ------------ .../lincheck/execution/ExecutionScenario.kt | 5 ++ .../kotlinx/lincheck/strategy/Strategy.kt | 3 +- .../jetbrains/kotlinx/lincheck/JvmHacks.kt | 28 ------ .../kotlinx/lincheck/annotations/LogLevel.kt | 2 +- .../lincheck/annotations/OpGroupConfig.kt | 24 ++--- .../kotlinx/lincheck/annotations/Operation.kt | 89 +++++++++++++++++++ .../kotlinx/lincheck/annotations/Param.kt | 10 +-- .../annotations/StateRepresentation.kt | 18 ++-- .../kotlinx/lincheck/annotations/Validate.kt | 0 .../lincheck/execution/ExecutionScenario.kt | 26 ------ 14 files changed, 122 insertions(+), 294 deletions(-) delete mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt delete mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt delete mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt delete mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt delete mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt create mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt rename src/{common => jvm}/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt (87%) rename src/{common => jvm}/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt (100%) delete mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt deleted file mode 100644 index c544200ae..000000000 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * - */ - -package org.jetbrains.kotlinx.lincheck.annotations - -/** - * This annotation should be added to a test class to specify the logging level. - * By default, [LoggingLevel.ERROR] is used. - */ -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) -expect annotation class LogLevel \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt deleted file mode 100644 index a52f2dc3f..000000000 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * - */ - -package org.jetbrains.kotlinx.lincheck.annotations - -/** - * Set some restrictions to the group with the specified name, - * used during the scenario generation phase. - */ -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) -expect annotation class OpGroupConfig(val name: String, val nonParallel: Boolean) { - /** - * Holder annotation for [OpGroupConfig]. - * Not a public API. - */ - @Retention(AnnotationRetention.RUNTIME) - @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) - annotation class OpGroupConfigs(vararg val value: OpGroupConfig) -} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt deleted file mode 100644 index a08bec781..000000000 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* -* #%L -* Lincheck -* %% -* Copyright (C) 2015 - 2018 Devexperts, LLC -* %% -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Lesser Public License for more details. -* -* You should have received a copy of the GNU General Lesser Public -* License along with this program. If not, see -* . -* #L% -*/ -package org.jetbrains.kotlinx.lincheck.annotations - -import kotlinx.coroutines.CancellableContinuation -import kotlin.reflect.KClass - -/** - * Mark your method with this annotation in order - * to use it in concurrent testing as an operation. - */ -@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) -annotation class Operation( - /** - * Binds the arguments of this operation with the specified [parameter configurations][Param] - * by their [Param.name] names. - */ - val params: Array = [], - /** - * Set it to `true` if you this operation should be called - * at most once during the test invocation; `false` by default. - */ - val runOnce: Boolean = false, - /** - * Specifies the operation group which can add some execution restriction. - * @see OpGroupConfig.name - */ - val group: String = "", - /** - * Handle the specified exceptions as a result of this operation invocation. - */ - val handleExceptionsAsResult: Array> = [], - /** - * Specifies whether the operation can be cancelled if it suspends, - * see [CancellableContinuation.cancel]; `true` by default. - */ - val cancellableOnSuspension: Boolean = true, - /** - * The operation marked with [allowExtraSuspension] is allowed to - * suspend (and, therefore, be cancelled if [cancellableOnSuspension] - * is set to `true`) even if it should not according to the sequential - * specification. The one may consider this as a relaxation of the - * dual data structures formalism. - */ - val allowExtraSuspension: Boolean = false, - /** - * Specifies whether this operation is blocking. - * This way, if the test checks for a non-blocking progress guarantee, - * **lincheck** will not fail the test if a hang is detected on - * a running operations with this `blocking` marker. - */ - val blocking: Boolean = false, - /** - * Specifies whether this operation invocation can lead - * to a blocking behavior of another concurrent operation. - * This way, if the test checks for a non-blocking progress guarantee, - * **lincheck** will not fail the test if a hang is detected - * while one of the operations marked with [causesBlocking] - * is running concurrently. Note, that this operation is not - * considered as blocking until it is marked as [blocking]. - */ - val causesBlocking: Boolean = false, - /** - * Specifies whether this cancellable operation supports - * prompt cancellation, `false` by default. This parameter - * is ignored if [cancellableOnSuspension] is `false`. - */ - val promptCancellation: Boolean = false) \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt deleted file mode 100644 index e61383419..000000000 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * - */ - -package org.jetbrains.kotlinx.lincheck.annotations - -import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator -import org.jetbrains.kotlinx.lincheck.paramgen.ParameterGenerator.Dummy -import kotlin.reflect.KClass - -/** - * Use this annotation to specify parameter generators. - * @see ParameterGenerator - */ -@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) -@Repeatable -expect annotation class Param constructor( - /** - * If the annotation is set on a class, creates a [parameter generator][ParameterGenerator] - * which can be used in [operations][Operation] by this name. If is set on an operation, - * uses the specified named parameter generator which is created as described before. - */ - val name: String, - /** - * Specifies the [ParameterGenerator] class which should be used for this parameter. - */ - val gen: KClass>, - /** - * Specifies the configuration for the [parameter generator][.gen]. - */ - val conf: String -) { - /** - * Holder annotation for [Param]. - * Not a public API. - */ - @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) - @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) - annotation class Params constructor(vararg val value: Param) -} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt index 0ef739225..dd9b7da7d 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt @@ -65,6 +65,11 @@ class ExecutionScenario( val ExecutionScenario.threads: Int get() = parallelExecution.size +/** + * Returns `true` if there is at least one suspendable actor in the generated scenario + */ +fun ExecutionScenario.hasSuspendableActors() = parallelExecution.any { actors -> actors.any { it.isSuspendable } } || postExecution.any { it.isSuspendable } + internal fun printInColumnsCustom( groupedObjects: List>, joinColumns: (List) -> String diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt index f9e805d15..c8da2f52c 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -30,11 +30,12 @@ import org.jetbrains.kotlinx.lincheck.execution.* * without any code change. */ expect abstract class Strategy protected constructor(scenario: ExecutionScenario) { + val scenario: ExecutionScenario + abstract fun run(): LincheckFailure? /** * Is invoked before each actor execution. */ open fun onActorStart(iThread: Int) - val scenario: ExecutionScenario } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt deleted file mode 100644 index 38273e7a1..000000000 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/JvmHacks.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2021 JetBrains s.r.o. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * - */ - -package org.jetbrains.kotlinx.lincheck - -import java.lang.reflect.* -import kotlin.reflect.* - -fun KClass.getConstructor(vararg args: Class<*>) : Constructor { - return this.java.getConstructor(*args) -} diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt index c68c3ec4d..0d156a3e7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/LogLevel.kt @@ -31,4 +31,4 @@ import java.lang.annotation.Inherited @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited -actual annotation class LogLevel(val value: LoggingLevel) \ No newline at end of file +annotation class LogLevel(val value: LoggingLevel) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt index be548a045..42555e97a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/OpGroupConfig.kt @@ -32,17 +32,17 @@ import java.lang.annotation.Repeatable @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Repeatable(OpGroupConfig.OpGroupConfigs::class) @Inherited -actual annotation class OpGroupConfig actual constructor( - /** - * Name of this group used by [Operation.group]. - */ - actual val name: String = "", - /** - * Set it to `true` for executing all actors in this group - * from one thread. This restriction allows to test single-reader - * and/or single-writer data structures and similar solutions. - */ - actual val nonParallel: Boolean = false) { +annotation class OpGroupConfig constructor( + /** + * Name of this group used by [Operation.group]. + */ + val name: String = "", + /** + * Set it to `true` for executing all actors in this group + * from one thread. This restriction allows to test single-reader + * and/or single-writer data structures and similar solutions. + */ + val nonParallel: Boolean = false) { /** * Holder annotation for [OpGroupConfig]. * Not a public API. @@ -50,5 +50,5 @@ actual annotation class OpGroupConfig actual constructor( @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited - actual annotation class OpGroupConfigs actual constructor(actual vararg val value: OpGroupConfig) + annotation class OpGroupConfigs constructor(vararg val value: OpGroupConfig) } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt new file mode 100644 index 000000000..1a0f3fe81 --- /dev/null +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Operation.kt @@ -0,0 +1,89 @@ +/* +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +package org.jetbrains.kotlinx.lincheck.annotations + +import kotlinx.coroutines.CancellableContinuation +import kotlin.reflect.KClass + +/** + * Mark your method with this annotation in order + * to use it in concurrent testing as an operation. + */ +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +annotation class Operation( + /** + * Binds the arguments of this operation with the specified [parameter configurations][Param] + * by their [Param.name] names. + */ + val params: Array = [], + /** + * Set it to `true` if you this operation should be called + * at most once during the test invocation; `false` by default. + */ + val runOnce: Boolean = false, + /** + * Specifies the operation group which can add some execution restriction. + * @see OpGroupConfig.name + */ + val group: String = "", + /** + * Handle the specified exceptions as a result of this operation invocation. + */ + val handleExceptionsAsResult: Array> = [], + /** + * Specifies whether the operation can be cancelled if it suspends, + * see [CancellableContinuation.cancel]; `true` by default. + */ + val cancellableOnSuspension: Boolean = true, + /** + * The operation marked with [allowExtraSuspension] is allowed to + * suspend (and, therefore, be cancelled if [cancellableOnSuspension] + * is set to `true`) even if it should not according to the sequential + * specification. The one may consider this as a relaxation of the + * dual data structures formalism. + */ + val allowExtraSuspension: Boolean = false, + /** + * Specifies whether this operation is blocking. + * This way, if the test checks for a non-blocking progress guarantee, + * **lincheck** will not fail the test if a hang is detected on + * a running operations with this `blocking` marker. + */ + val blocking: Boolean = false, + /** + * Specifies whether this operation invocation can lead + * to a blocking behavior of another concurrent operation. + * This way, if the test checks for a non-blocking progress guarantee, + * **lincheck** will not fail the test if a hang is detected + * while one of the operations marked with [causesBlocking] + * is running concurrently. Note, that this operation is not + * considered as blocking until it is marked as [blocking]. + */ + val causesBlocking: Boolean = false, + /** + * Specifies whether this cancellable operation supports + * prompt cancellation, `false` by default. This parameter + * is ignored if [cancellableOnSuspension] is `false`. + */ + val promptCancellation: Boolean = false +) \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt index b203091d7..521b8184d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Param.kt @@ -34,21 +34,21 @@ import kotlin.reflect.KClass @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Repeatable(Param.Params::class) @Inherited -actual annotation class Param actual constructor( +annotation class Param constructor( /** * If the annotation is set on a class, creates a [parameter generator][ParameterGenerator] * which can be used in [operations][Operation] by this name. If is set on an operation, * uses the specified named parameter generator which is created as described before. */ - actual val name: String = "", + val name: String = "", /** * Specifies the [ParameterGenerator] class which should be used for this parameter. */ - actual val gen: KClass> = Dummy::class, + val gen: KClass> = Dummy::class, /** * Specifies the configuration for the [parameter generator][.gen]. */ - actual val conf: String = "" + val conf: String = "" ) { /** * Holder annotation for [Param]. @@ -57,5 +57,5 @@ actual annotation class Param actual constructor( @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Inherited - actual annotation class Params actual constructor(actual vararg val value: Param) + annotation class Params constructor(vararg val value: Param) } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt similarity index 87% rename from src/common/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt index 821291645..6ec123b76 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/StateRepresentation.kt @@ -1,23 +1,21 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.annotations @@ -27,7 +25,7 @@ package org.jetbrains.kotlinx.lincheck.annotations * each meaningful event (e.g., write to atomic variable or function that potentially * changes the data structure call). In order to specify the way for representing * the data structure state, a public no-argument function that returns [String] - * should be marked with this annotation. + * should be marked with this annotation. */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt similarity index 100% rename from src/common/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt rename to src/jvm/main/org/jetbrains/kotlinx/lincheck/annotations/Validate.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt deleted file mode 100644 index 0f01f6526..000000000 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2021 JetBrains s.r.o. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * - */ - -package org.jetbrains.kotlinx.lincheck.execution - -/** - * Returns `true` if there is at least one suspendable actor in the generated scenario - */ -fun ExecutionScenario.hasSuspendableActors() = parallelExecution.any { actors -> actors.any { it.isSuspendable } } || postExecution.any { it.isSuspendable } \ No newline at end of file From f6825ab911da3104c96e79a05e11d350041df22b Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Tue, 16 Feb 2021 16:19:05 +0300 Subject: [PATCH 17/72] Get rid of WeakHashMap in CachedVerifier --- .../main/org/jetbrains/kotlinx/lincheck/Actor.kt | 1 - .../org/jetbrains/kotlinx/lincheck/Options.kt | 1 - .../lincheck/paramgen/ParameterGenerator.kt | 1 - .../lincheck/verifier/SerializabilityVerifier.kt | 4 ++-- .../kotlinx/lincheck/verifier/Verifier.kt | 16 +++++++++++----- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt index bdfa9007a..7b565f0b1 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -20,7 +20,6 @@ package org.jetbrains.kotlinx.lincheck -import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.verifier.quiescent.* /** diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt index 30376f054..4bfacaf19 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -20,7 +20,6 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.execution.* -import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.reflect.* diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt index 5df6d9821..ca286c8da 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/paramgen/ParameterGenerator.kt @@ -19,7 +19,6 @@ */ package org.jetbrains.kotlinx.lincheck.paramgen -import org.jetbrains.kotlinx.lincheck.annotations.Operation /** * The implementation of this interface is used to generate parameters diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt index cedae80b3..c97298839 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.kt @@ -33,8 +33,8 @@ class SerializabilityVerifier( private val linerizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) // always ignore clocks - override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult) = - super.verifyResults(scenario, results.withEmptyClocks) + override fun verifyResults(scenario: ExecutionScenario, result: ExecutionResult) = + super.verifyResults(scenario, result.withEmptyClocks) override fun verifyResultsImpl(scenario: ExecutionScenario, results: ExecutionResult) = linerizabilityVerifier.verifyResultsImpl(scenario.converted, results.converted) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt index 1aeb49915..e8dfa9123 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/Verifier.kt @@ -39,7 +39,7 @@ interface Verifier { * Verifies the specified results for correctness. * Returns `true` if results are possible, `false` otherwise. */ - fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean + fun verifyResults(scenario: ExecutionScenario, result: ExecutionResult): Boolean /** * Verifiers which use sequential implementation instances as states (or parts of them) @@ -66,10 +66,16 @@ internal inline fun Map.computeIfAbsent(key: K, defaultValue: (K) - * phase significantly. */ abstract class CachedVerifier : Verifier { - private val previousResults: MutableMap> = HashMap() - override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean { - val newResult = previousResults.computeIfAbsent(scenario) { s: ExecutionScenario -> HashSet() }.add(results) - return if (!newResult) true else verifyResultsImpl(scenario, results) + private var lastScenario: ExecutionScenario? = null + private val previousResults = HashSet() + + override fun verifyResults(scenario: ExecutionScenario, result: ExecutionResult): Boolean { + if (lastScenario != scenario) { + lastScenario = scenario + previousResults.clear() + } + val newResult = previousResults.add(result) + return if (!newResult) true else verifyResultsImpl(scenario, result) } abstract fun verifyResultsImpl(scenario: ExecutionScenario, results: ExecutionResult): Boolean From 362e1521e9b8d926d815a5f5b7c17a660e25815d Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Tue, 16 Feb 2021 16:26:02 +0300 Subject: [PATCH 18/72] Get rid of WeakHashMap in QuiescentConsistencyVerifier --- .../quiescent/QuiescentConsistencyVerifier.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 07d154275..93a601564 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -37,34 +37,39 @@ import kotlin.reflect.* */ class QuiescentConsistencyVerifier(sequentialSpecification: SequentialSpecification<*>) : Verifier { private val linearizabilityVerifier = LinearizabilityVerifier(sequentialSpecification) - private val scenarioMapping: MutableMap = HashMap() // TODO change logic to WeakHashMap + private var lastScenario: ExecutionScenario? = null + private lateinit var lastConvertedScenario: ExecutionScenario override fun checkStateEquivalenceImplementation() = linearizabilityVerifier.checkStateEquivalenceImplementation() - override fun verifyResults(scenario: ExecutionScenario, results: ExecutionResult): Boolean { + override fun verifyResults(scenario: ExecutionScenario, result: ExecutionResult): Boolean { val convertedScenario = scenario.converted - val convertedResults = results.convert(scenario, convertedScenario.threads) + val convertedResults = result.convert(scenario, convertedScenario.threads) checkScenarioAndResultsAreSimilarlyConverted(convertedScenario, convertedResults) return linearizabilityVerifier.verifyResults(convertedScenario, convertedResults) } private val ExecutionScenario.converted: ExecutionScenario - get() = scenarioMapping.computeIfAbsent(this) { - val parallelExecutionConverted = ArrayList>() - repeat(threads) { - parallelExecutionConverted.add(ArrayList()) - } - parallelExecution.forEachIndexed { t, threadActors -> - for (a in threadActors) { - if (a.isQuiescentConsistent) { - parallelExecutionConverted.add(mutableListOf(a)) - } else { - parallelExecutionConverted[t].add(a) + get() { + if (lastScenario != this) { + lastScenario = this + val parallelExecutionConverted = ArrayList>() + repeat(threads) { + parallelExecutionConverted.add(ArrayList()) } + parallelExecution.forEachIndexed { t, threadActors -> + for (a in threadActors) { + if (a.isQuiescentConsistent) { + parallelExecutionConverted.add(mutableListOf(a)) + } else { + parallelExecutionConverted[t].add(a) + } + } + } + lastConvertedScenario = ExecutionScenario(initExecution, parallelExecutionConverted, postExecution) } + return lastConvertedScenario } - ExecutionScenario(initExecution, parallelExecutionConverted, postExecution) - } private fun ExecutionResult.convert(originalScenario: ExecutionScenario, newThreads: Int): ExecutionResult { val parallelResults = ArrayList>() From cf0bd5f8784832a690a9d68303c56447550290f3 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Tue, 16 Feb 2021 17:06:26 +0300 Subject: [PATCH 19/72] Meeting changes --- .../jetbrains/kotlinx/lincheck/CTestConfiguration.kt | 5 ++--- .../main/org/jetbrains/kotlinx/lincheck/Options.kt | 8 ++++---- .../jetbrains/kotlinx/lincheck/CTestConfiguration.kt | 8 ++++++-- .../main/org/jetbrains/kotlinx/lincheck/LinChecker.kt | 7 +++---- .../main/org/jetbrains/kotlinx/lincheck/Options.kt | 11 +++++++++-- .../strategy/managed/ManagedCTestConfiguration.kt | 2 +- .../modelchecking/ModelCheckingCTestConfiguration.kt | 4 ++-- .../managed/modelchecking/ModelCheckingOptions.kt | 2 +- .../strategy/stress/StressCTestConfiguration.kt | 4 ++-- .../kotlinx/lincheck/strategy/stress/StressOptions.kt | 2 +- .../modelchecking/ModelCheckingOptionsTest.java | 4 ++-- .../test/strategy/stress/StressOptionsTest.java | 4 ++-- 12 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index ba11a4cf0..91267a57d 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -20,7 +20,7 @@ abstract class CTestConfiguration( val verifierClass: VerifierClass, val requireStateEquivalenceImplCheck: Boolean, val minimizeFailedScenario: Boolean, - val sequentialSpecification: SequentialSpecification<*>?, + val sequentialSpecification: SequentialSpecification<*>, val timeoutMs: Long ) { abstract fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, @@ -37,5 +37,4 @@ abstract class CTestConfiguration( } } -expect val DEFAULT_EXECUTION_GENERATOR: ExecutionGeneratorClass -expect val DEFAULT_VERIFIER: VerifierClass \ No newline at end of file +expect val DEFAULT_EXECUTION_GENERATOR: ExecutionGeneratorClass \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt index 4bfacaf19..8e50bd8ea 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -21,10 +21,10 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* +import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* import kotlin.reflect.* expect class ExecutionGeneratorClass -expect class VerifierClass /** * Abstract class for test options. @@ -37,7 +37,7 @@ abstract class Options, CTEST : CTestConfiguration> { protected var actorsBefore = CTestConfiguration.DEFAULT_ACTORS_BEFORE protected var actorsAfter = CTestConfiguration.DEFAULT_ACTORS_AFTER var executionGenerator = DEFAULT_EXECUTION_GENERATOR - var verifier = DEFAULT_VERIFIER + var verifierGenerator: (sequentialSpecification: SequentialSpecification<*>) -> Verifier = { sequentialSpecification -> LinearizabilityVerifier(sequentialSpecification) } protected var requireStateEquivalenceImplementationCheck = true protected var minimizeFailedScenario = CTestConfiguration.DEFAULT_MINIMIZE_ERROR var sequentialSpecification: SequentialSpecification<*>? = null @@ -108,8 +108,8 @@ abstract class Options, CTEST : CTestConfiguration> { /** * Use the specified verifier. */ - fun verifier(verifier: VerifierClass): OPT = applyAndCast { - this.verifier = verifier + fun verifier(verifier: (sequentialSpecification: SequentialSpecification<*>) -> Verifier): OPT = applyAndCast { + this.verifierGenerator = verifier } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 371210b8a..7de9c3779 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -40,7 +40,9 @@ internal fun createFromTestClassAnnotations(testClass: Class<*>): List StressCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator.java, ann.verifier.java, ann.invocationsPerIteration, + ann.generator.java, { sequentialSpecification -> + ann.verifier.java.getConstructor(SequentialSpecification::class.java).newInstance(sequentialSpecification) + }, ann.invocationsPerIteration, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification.java, TestClass(testClass)), DEFAULT_TIMEOUT_MS ) @@ -49,7 +51,9 @@ internal fun createFromTestClassAnnotations(testClass: Class<*>): List ModelCheckingCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator.java, ann.verifier.java, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, + ann.generator.java, { sequentialSpecification -> + ann.verifier.java.getConstructor(SequentialSpecification::class.java).newInstance(sequentialSpecification) + }, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, ann.invocationsPerIteration, ManagedCTestConfiguration.DEFAULT_GUARANTEES, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification.java, TestClass(testClass)), DEFAULT_TIMEOUT_MS, DEFAULT_ELIMINATE_LOCAL_OBJECTS, DEFAULT_VERBOSE_TRACE diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index bf0201b24..f6a5701a6 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -175,10 +175,9 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { private fun CTestConfiguration.createVerifier() = - verifierClass.constructors.find{ it.parameters.size == 1 && it.parameters[0].type == SequentialSpecification::class.java }!! - .newInstance(sequentialSpecification).also { - if (requireStateEquivalenceImplCheck) (it as Verifier).checkStateEquivalenceImplementation() - } as Verifier + verifierClass(this.sequentialSpecification).also { + if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() + } private fun CTestConfiguration.createExecutionGenerator() = generatorClass.getConstructor( diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt index 8857bf5ee..2c4ab0c48 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -19,9 +19,16 @@ */ package org.jetbrains.kotlinx.lincheck -import kotlin.reflect.* +import org.jetbrains.kotlinx.lincheck.verifier.* actual typealias ExecutionGeneratorClass = Class actual typealias VerifierClass = Class actual typealias SequentialSpecification = Class -actual fun SequentialSpecification.getInitialState(): T = this.newInstance() \ No newline at end of file +actual fun SequentialSpecification.getInitialState(): T = this.newInstance() + +fun , CTEST : CTestConfiguration> Options.verifier(verifierClass: Class): OPT { + verifier { sequentialSpecification -> + verifierClass.getConstructor(SequentialSpecification::class.java).newInstance(sequentialSpecification) + } + return this as OPT +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt index 1925eb64f..7e2482dcf 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt @@ -32,7 +32,7 @@ import kotlin.reflect.* abstract class ManagedCTestConfiguration( testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: ExecutionGeneratorClass, verifierClass: VerifierClass, + generatorClass: ExecutionGeneratorClass, verifierClass: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, val checkObstructionFreedom: Boolean, val hangingDetectionThreshold: Int, val invocationsPerIteration: Int, val guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, sequentialSpecification: SequentialSpecification<*>?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index 9af95a087..0aef3e9a2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -34,9 +34,9 @@ import kotlin.reflect.* */ class ModelCheckingCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, generatorClass: ExecutionGeneratorClass, - verifierClass: VerifierClass, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, + verifierClass: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, invocationsPerIteration: Int, guarantees: List, requireStateEquivalenceCheck: Boolean, - minimizeFailedScenario: Boolean, sequentialSpecification: SequentialSpecification<*>?, timeoutMs: Long, + minimizeFailedScenario: Boolean, sequentialSpecification: SequentialSpecification<*>, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean ) : ManagedCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceCheck, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt index ad4006640..32462f3ed 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt @@ -30,7 +30,7 @@ import org.jetbrains.kotlinx.lincheck.strategy.managed.* class ModelCheckingOptions : ManagedOptions() { override fun createTestConfigurations(testClass: TestClass): ModelCheckingCTestConfiguration { return ModelCheckingCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, - executionGenerator, verifier, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, + executionGenerator, verifierGenerator, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, chooseSequentialSpecification(sequentialSpecification, testClass), timeoutMs, eliminateLocalObjects, verboseTrace) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index de1f5e76e..30e1de1a8 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -30,9 +30,9 @@ import kotlin.reflect.* * Configuration for [stress][StressStrategy] strategy. */ class StressCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: ExecutionGeneratorClass, verifierClass: VerifierClass, + generatorClass: ExecutionGeneratorClass, verifierClass: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: SequentialSpecification<*>?, timeoutMs: Long + sequentialSpecification: SequentialSpecification<*>, timeoutMs: Long ) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs) { override fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt index ad43f95da..7cccd8baa 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt @@ -36,7 +36,7 @@ open class StressOptions : Options() { override fun createTestConfigurations(testClass: TestClass): StressCTestConfiguration { return StressCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, - verifier, invocationsPerIteration, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, + verifierGenerator, invocationsPerIteration, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, chooseSequentialSpecification(sequentialSpecification, testClass), timeoutMs) } } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java index 195a6499b..098c06f37 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java @@ -47,8 +47,8 @@ public void test() { ModelCheckingOptions opts = new ModelCheckingOptions() .iterations(10) .invocationsPerIteration(200) - .executionGenerator(RandomExecutionGenerator.class) - .verifier(LinearizabilityVerifier.class) +// .executionGenerator(RandomExecutionGenerator.class) +// .verifier(LinearizabilityVerifier.class) .threads(2) .actorsPerThread(3) .checkObstructionFreedom(true) diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java index 545276e12..c0ecda066 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java @@ -44,8 +44,8 @@ public void test() { StressOptions opts = new StressOptions() .iterations(10) .invocationsPerIteration(200) - .executionGenerator(RandomExecutionGenerator.class) - .verifier(LinearizabilityVerifier.class) +// .executionGenerator(RandomExecutionGenerator.class) +// .verifier(LinearizabilityVerifier.class) .threads(2) .actorsPerThread(3) .logLevel(LoggingLevel.ERROR) From 5c1a4ef161509787fd65ca54d1bfe3389af37162 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 22 Feb 2021 03:43:00 +0300 Subject: [PATCH 20/72] fix last commit, break java api --- .../kotlinx/lincheck/CTestConfiguration.kt | 8 +++---- .../org/jetbrains/kotlinx/lincheck/Options.kt | 9 ++++---- .../kotlinx/lincheck/CTestConfiguration.kt | 21 +++++++++++-------- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 7 ++----- .../org/jetbrains/kotlinx/lincheck/Options.kt | 13 +++++++++--- .../managed/ManagedCTestConfiguration.kt | 7 +++---- .../ModelCheckingCTestConfiguration.kt | 6 ++---- .../modelchecking/ModelCheckingOptions.kt | 2 +- .../stress/StressCTestConfiguration.kt | 5 ++--- .../lincheck/strategy/stress/StressOptions.kt | 2 +- .../ModelCheckingOptionsTest.java | 3 +++ .../strategy/stress/StressOptionsTest.java | 2 ++ 12 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 91267a57d..e81594440 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -16,8 +16,8 @@ abstract class CTestConfiguration( val actorsPerThread: Int, val actorsBefore: Int, val actorsAfter: Int, - val generatorClass: ExecutionGeneratorClass, - val verifierClass: VerifierClass, + val executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator, + val verifierGenerator: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, val requireStateEquivalenceImplCheck: Boolean, val minimizeFailedScenario: Boolean, val sequentialSpecification: SequentialSpecification<*>, @@ -35,6 +35,4 @@ abstract class CTestConfiguration( const val DEFAULT_MINIMIZE_ERROR = true const val DEFAULT_TIMEOUT_MS: Long = 10000 } -} - -expect val DEFAULT_EXECUTION_GENERATOR: ExecutionGeneratorClass \ No newline at end of file +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt index 8e50bd8ea..dbf28b04d 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -24,8 +24,6 @@ import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* import kotlin.reflect.* -expect class ExecutionGeneratorClass - /** * Abstract class for test options. */ @@ -36,7 +34,8 @@ abstract class Options, CTEST : CTestConfiguration> { protected var actorsPerThread = CTestConfiguration.DEFAULT_ACTORS_PER_THREAD protected var actorsBefore = CTestConfiguration.DEFAULT_ACTORS_BEFORE protected var actorsAfter = CTestConfiguration.DEFAULT_ACTORS_AFTER - var executionGenerator = DEFAULT_EXECUTION_GENERATOR + var executionGeneratorGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator = + { testConfiguration, testStructure -> RandomExecutionGenerator(testConfiguration, testStructure) } var verifierGenerator: (sequentialSpecification: SequentialSpecification<*>) -> Verifier = { sequentialSpecification -> LinearizabilityVerifier(sequentialSpecification) } protected var requireStateEquivalenceImplementationCheck = true protected var minimizeFailedScenario = CTestConfiguration.DEFAULT_MINIMIZE_ERROR @@ -101,8 +100,8 @@ abstract class Options, CTEST : CTestConfiguration> { /** * Use the specified execution generator. */ - fun executionGenerator(executionGenerator: ExecutionGeneratorClass): OPT = applyAndCast { - this.executionGenerator = executionGenerator + fun executionGenerator(executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator): OPT = applyAndCast { + this.executionGeneratorGenerator = executionGenerator } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 7de9c3779..a3c059576 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -32,17 +32,17 @@ import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* import kotlin.reflect.* -actual val DEFAULT_EXECUTION_GENERATOR: ExecutionGeneratorClass = RandomExecutionGenerator::class.java -actual val DEFAULT_VERIFIER: VerifierClass = LinearizabilityVerifier::class.java - internal fun createFromTestClassAnnotations(testClass: Class<*>): List { val stressConfigurations: List = testClass.getAnnotationsByType(StressCTest::class.java) .map { ann: StressCTest -> StressCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator.java, { sequentialSpecification -> - ann.verifier.java.getConstructor(SequentialSpecification::class.java).newInstance(sequentialSpecification) - }, ann.invocationsPerIteration, + { testConfiguration, testStructure -> + ann.generator.java.getConstructor(CTestConfiguration::class.java, CTestStructure::class.java) + .newInstance(testConfiguration, testStructure) + }, { sequentialSpecification -> + ann.verifier.java.getConstructor(SequentialSpecification::class.java).newInstance(sequentialSpecification) + }, ann.invocationsPerIteration, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification.java, TestClass(testClass)), DEFAULT_TIMEOUT_MS ) @@ -51,9 +51,12 @@ internal fun createFromTestClassAnnotations(testClass: Class<*>): List ModelCheckingCTestConfiguration(TestClass(testClass), ann.iterations, ann.threads, ann.actorsPerThread, ann.actorsBefore, ann.actorsAfter, - ann.generator.java, { sequentialSpecification -> - ann.verifier.java.getConstructor(SequentialSpecification::class.java).newInstance(sequentialSpecification) - }, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, + { testConfiguration, testStructure -> + ann.generator.java.getConstructor(CTestConfiguration::class.java, CTestStructure::class.java) + .newInstance(testConfiguration, testStructure) + }, { sequentialSpecification -> + ann.verifier.java.getConstructor(SequentialSpecification::class.java).newInstance(sequentialSpecification) + }, ann.checkObstructionFreedom, ann.hangingDetectionThreshold, ann.invocationsPerIteration, ManagedCTestConfiguration.DEFAULT_GUARANTEES, ann.requireStateEquivalenceImplCheck, ann.minimizeFailedScenario, chooseSequentialSpecification(ann.sequentialSpecification.java, TestClass(testClass)), DEFAULT_TIMEOUT_MS, DEFAULT_ELIMINATE_LOCAL_OBJECTS, DEFAULT_VERBOSE_TRACE diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index f6a5701a6..834ccb67b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -175,15 +175,12 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { private fun CTestConfiguration.createVerifier() = - verifierClass(this.sequentialSpecification).also { + verifierGenerator(this.sequentialSpecification).also { if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() } private fun CTestConfiguration.createExecutionGenerator() = - generatorClass.getConstructor( - CTestConfiguration::class.java, - CTestStructure::class.java - ).newInstance(this, testStructure) + executionGenerator(this, testStructure) // This companion object is used for backwards compatibility. companion object { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt index 2c4ab0c48..728e4f630 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Options.kt @@ -19,12 +19,19 @@ */ package org.jetbrains.kotlinx.lincheck +import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* -actual typealias ExecutionGeneratorClass = Class -actual typealias VerifierClass = Class actual typealias SequentialSpecification = Class -actual fun SequentialSpecification.getInitialState(): T = this.newInstance() +actual fun SequentialSpecification.getInitialState(): T = this.getDeclaredConstructor().newInstance() + +fun , CTEST : CTestConfiguration> Options.executionGenerator(executionGeneratorClass: Class): OPT { + executionGenerator { + testConfiguration, testStructure -> executionGeneratorClass.getConstructor(CTestConfiguration::class.java, CTestStructure::class.java) + .newInstance(testConfiguration, testStructure) + } + return this as OPT +} fun , CTEST : CTestConfiguration> Options.verifier(verifierClass: Class): OPT { verifier { sequentialSpecification -> diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt index 7e2482dcf..2a26cb729 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedCTestConfiguration.kt @@ -24,7 +24,6 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* -import kotlin.reflect.* /** * A common configuration for managed strategies. @@ -32,12 +31,12 @@ import kotlin.reflect.* abstract class ManagedCTestConfiguration( testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: ExecutionGeneratorClass, verifierClass: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, + executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator, verifierClass: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, val checkObstructionFreedom: Boolean, val hangingDetectionThreshold: Int, val invocationsPerIteration: Int, val guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: SequentialSpecification<*>?, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean + sequentialSpecification: SequentialSpecification<*>, timeoutMs: Long, val eliminateLocalObjects: Boolean, val verboseTrace: Boolean ) : CTestConfiguration( - testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, + testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, verifierClass, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs ) { companion object { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index 0aef3e9a2..19b2ca612 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -26,19 +26,17 @@ import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* import org.jetbrains.kotlinx.lincheck.verifier.* -import java.lang.reflect.* -import kotlin.reflect.* /** * Configuration for [random search][ModelCheckingStrategy] strategy. */ class ModelCheckingCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, - actorsAfter: Int, generatorClass: ExecutionGeneratorClass, + actorsAfter: Int, executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator, verifierClass: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, checkObstructionFreedom: Boolean, hangingDetectionThreshold: Int, invocationsPerIteration: Int, guarantees: List, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, sequentialSpecification: SequentialSpecification<*>, timeoutMs: Long, eliminateLocalObjects: Boolean, verboseTrace: Boolean -) : ManagedCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, +) : ManagedCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, verifierClass, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs, eliminateLocalObjects, verboseTrace) { override fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt index 32462f3ed..d00838b0a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingOptions.kt @@ -30,7 +30,7 @@ import org.jetbrains.kotlinx.lincheck.strategy.managed.* class ModelCheckingOptions : ManagedOptions() { override fun createTestConfigurations(testClass: TestClass): ModelCheckingCTestConfiguration { return ModelCheckingCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, - executionGenerator, verifierGenerator, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, + executionGeneratorGenerator, verifierGenerator, checkObstructionFreedom, hangingDetectionThreshold, invocationsPerIteration, guarantees, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, chooseSequentialSpecification(sequentialSpecification, testClass), timeoutMs, eliminateLocalObjects, verboseTrace) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index 30e1de1a8..8d9c5a704 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -24,16 +24,15 @@ package org.jetbrains.kotlinx.lincheck.strategy.stress import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* -import kotlin.reflect.* /** * Configuration for [stress][StressStrategy] strategy. */ class StressCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - generatorClass: ExecutionGeneratorClass, verifierClass: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, + executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator, verifierGenerator: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, sequentialSpecification: SequentialSpecification<*>, timeoutMs: Long -) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, generatorClass, verifierClass, +) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, verifierGenerator, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs) { override fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, verifier: Verifier) = diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt index 7cccd8baa..c102ad455 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt @@ -35,7 +35,7 @@ open class StressOptions : Options() { } override fun createTestConfigurations(testClass: TestClass): StressCTestConfiguration { - return StressCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, + return StressCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGeneratorGenerator, verifierGenerator, invocationsPerIteration, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, chooseSequentialSpecification(sequentialSpecification, testClass), timeoutMs) } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java index 098c06f37..c56c15b94 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/modelchecking/ModelCheckingOptionsTest.java @@ -23,6 +23,7 @@ import org.jetbrains.kotlinx.lincheck.LinChecker; import org.jetbrains.kotlinx.lincheck.LoggingLevel; +import org.jetbrains.kotlinx.lincheck.OptionsKt; import org.jetbrains.kotlinx.lincheck.annotations.Operation; import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator; import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.ModelCheckingOptions; @@ -57,6 +58,8 @@ public void test() { .addGuarantee(forClasses("java.util.WeakHashMap").allMethods().ignore()) .requireStateEquivalenceImplCheck(false) .minimizeFailedScenario(false); + OptionsKt.executionGenerator(opts, RandomExecutionGenerator.class); // TODO broken java api https://stackoverflow.com/questions/28294509/accessing-kotlin-extension-functions-from-java + OptionsKt.verifier(opts, LinearizabilityVerifier.class); LinChecker.check(ModelCheckingOptionsTest.class, opts); } } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java index c0ecda066..d6de01729 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java @@ -51,6 +51,8 @@ public void test() { .logLevel(LoggingLevel.ERROR) .requireStateEquivalenceImplCheck(false) .minimizeFailedScenario(false); + OptionsKt.executionGenerator(opts, RandomExecutionGenerator.class); // TODO broken java api https://stackoverflow.com/questions/28294509/accessing-kotlin-extension-functions-from-java + OptionsKt.verifier(opts, LinearizabilityVerifier.class); LinChecker.check(StressOptionsTest.class, opts); } } \ No newline at end of file From 1ff837534a7e21c6162ec3a56bf74ea797350942 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 22 Feb 2021 04:38:26 +0300 Subject: [PATCH 21/72] Move Runner and StressStrategy to common, make ParallelThreadsRunner expect-actual --- .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 5 + .../lincheck/runner/ParallelThreadsRunner.kt | 45 +++++ .../kotlinx/lincheck/runner/Runner.kt | 162 ++++++++++++++++++ .../stress/StressCTestConfiguration.kt | 0 .../lincheck/strategy/stress/StressOptions.kt | 0 .../strategy/stress/StressStrategy.kt | 22 ++- .../lincheck/TransformationClassLoader.java | 2 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 6 +- .../lincheck/runner/ParallelThreadsRunner.kt | 24 +-- .../kotlinx/lincheck/runner/Runner.kt | 151 ++-------------- .../runner/TestThreadExecutionGenerator.java | 2 +- .../strategy/managed/ManagedStrategy.kt | 10 +- 12 files changed, 260 insertions(+), 169 deletions(-) create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt (100%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt (100%) rename src/{jvm => common}/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt (78%) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index 9dda28b53..7c9bc43a8 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -64,6 +64,11 @@ internal inline fun executeValidationFunctions(instance: Any, validationFunction } } +/** + * loader class type should be ClassLoader in jvm + */ +internal expect fun ExecutionScenario.convertForLoader(loader: Any): ExecutionScenario + /** * Returns `true` if the continuation was cancelled by [CancellableContinuation.cancel]. */ diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt new file mode 100644 index 000000000..8eb132d5c --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -0,0 +1,45 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.strategy.* + +/** + * This runner executes parallel scenario part in different threads. + * Supports running scenarios with `suspend` functions. + * + * It is pretty useful for stress testing or if you do not care about context switch expenses. + */ +internal expect open class ParallelThreadsRunner( + strategy: Strategy, + testClass: TestClass, + validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, + timeoutMs: Long, // for deadlock or livelock detection + useClocks: UseClocks // specifies whether `HBClock`-s should always be used or with some probability +) : Runner + +internal enum class UseClocks { ALWAYS, RANDOM } + +internal enum class CompletionStatus { CANCELLED, RESUMED } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt new file mode 100644 index 000000000..56523044d --- /dev/null +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -0,0 +1,162 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import kotlinx.atomicfu.* +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.execution.* +import kotlin.contracts.* + +expect fun Runner.loadClassLoader(): Any +expect fun loadClass(classLoader: Any, testClass: TestClass): TestClass + +expect class AtomicInteger(value: Int) { + fun get(): Int +} + +/** + * Runner determines how to run your concurrent test. In order to support techniques + * like fibers, it may require code transformation, so that [createTransformer] should + * provide the corresponding transformer and [needsTransformation] should return `true`. + */ +abstract class Runner protected constructor( + val strategy: Strategy, + private val _testClass: TestClass, // will be transformed later + protected val validationFunctions: List, + protected val stateRepresentationFunction: StateRepresentationFunction? +) { + protected var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` + protected lateinit var testClass: TestClass // not available before `initialize` call + @Suppress("LeakingThis") + val classLoader: Any = loadClassLoader() + protected val completedOrSuspendedThreads = AtomicInteger(0) + + /** + * This method is a part of `Runner` initialization and should be invoked after this runner + * creation. It is separated from the constructor to perform the strategy initialization at first. + */ + open fun initialize() { + scenario = strategy.scenario.convertForLoader(classLoader) + testClass = loadClass(_testClass) + } + + /** + * Returns the current state representation of the test instance constructed via + * the function marked with [StateRepresentation] annotation, or `null` + * if no such function is provided. + * + * Please note, that it is unsafe to call this method concurrently with the running scenario. + * However, it is fine to call it if the execution is paused somewhere in the middle. + */ + open fun constructStateRepresentation(): String? = null + + /** + * Loads the specified class via this runner' class loader. + */ + private fun loadClass(testClass: TestClass): TestClass = loadClass(classLoader, testClass) + + /** + * Creates a transformer required for this runner. + * Throws [UnsupportedOperationException] by default. + * + * @param cv should be of type ClassVisitor + * @return class visitor which transform the code due to support this runner. + */ + open fun createTransformer(cv: Any): Any? = null + + /** + * This method should return `true` if code transformation + * is required for this runner; returns `false` by default. + */ + open fun needsTransformation(): Boolean = false + + /** + * Runs the next invocation. + */ + abstract fun run(): InvocationResult + + /** + * This method is invoked by every test thread as the first operation. + * @param iThread number of invoking thread + */ + open fun onStart(iThread: Int) {} + + /** + * This method is invoked by every test thread as the last operation + * if no exception has been thrown. + * @param iThread number of invoking thread + */ + open fun onFinish(iThread: Int) {} + + /** + * This method is invoked by the corresponding test thread + * when an unexpected exception is thrown. + */ + open fun onFailure(iThread: Int, e: Throwable) {} + + /** + * This method is invoked by the corresponding test thread + * when the current coroutine suspends. + * @param iThread number of invoking thread + */ + open fun afterCoroutineSuspended(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + + /** + * This method is invoked by the corresponding test thread + * when the current coroutine is resumed. + */ + open fun afterCoroutineResumed(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + + /** + * This method is invoked by the corresponding test thread + * when the current coroutine is cancelled. + */ + open fun afterCoroutineCancelled(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") + + /** + * Returns `true` if the coroutine corresponding to + * the actor `actorId` in the thread `iThread` is resumed. + */ + open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean = throw UnsupportedOperationException("Coroutines are not supported") + + /** + * Is invoked before each actor execution from the specified thread. + * The invocations are inserted into the generated code. + */ + fun onActorStart(iThread: Int) { + strategy.onActorStart(iThread) + } + + /** + * Closes the resources used in this runner. + */ + open fun close() {} + + /** + * @return whether all scenario threads are completed or suspended + * Used by generated code. + */ + val isParallelExecutionCompleted: Boolean + get() = completedOrSuspendedThreads.get() == scenario.threads +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt similarity index 100% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt similarity index 78% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt rename to src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index bc2b62185..53c3510c6 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -56,18 +56,22 @@ class StressStrategy( } override fun run(): LincheckFailure? { - runner.use { - // Run invocations - for (invocation in 0 until invocations) { - when (val ir = runner.run()) { - is CompletedInvocationResult -> { - if (!verifier.verifyResults(scenario, ir.results)) - return IncorrectResultsFailure(scenario, ir.results) + try { + runner.also { + // Run invocations + for (invocation in 0 until invocations) { + when (val ir = runner.run()) { + is CompletedInvocationResult -> { + if (!verifier.verifyResults(scenario, ir.results)) + return IncorrectResultsFailure(scenario, ir.results) + } + else -> return ir.toLincheckFailure(scenario) } - else -> return ir.toLincheckFailure(scenario) } + return null } - return null + } finally { + runner.close() } } } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java index d4fd3702a..abd557230 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/TransformationClassLoader.java @@ -58,7 +58,7 @@ public TransformationClassLoader(Strategy strategy, Runner runner) { classTransformers = new ArrayList<>(); // Apply the strategy's transformer at first, then the runner's one. if (strategy.needsTransformation()) classTransformers.add(strategy::createTransformer); - if (runner.needsTransformation()) classTransformers.add(runner::createTransformer); + if (runner.needsTransformation()) classTransformers.add(cv -> (ClassVisitor)runner.createTransformer(cv)); remapper = UtilsKt.getRemapperByTransformers( // create transformers just for their class information classTransformers.stream() diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 60670e46a..f5fe1a2ea 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -166,14 +166,14 @@ actual fun storeCancellableContinuation(cont: CancellableContinuation<*>) { } } -internal fun ExecutionScenario.convertForLoader(loader: ClassLoader) = ExecutionScenario( +internal actual fun ExecutionScenario.convertForLoader(loader: Any) = ExecutionScenario( initExecution, parallelExecution.map { actors -> actors.map { a -> - val args = a.arguments.map { it.convertForLoader(loader) } + val args = a.arguments.map { it.convertForLoader(loader as ClassLoader) } // the original `isSuspendable` is used here since `KFunction.isSuspend` fails on transformed classes Actor( - method = a.method.convertForLoader(loader), + method = a.method.convertForLoader(loader as ClassLoader), arguments = args, handledExceptions = a.handledExceptions, cancelOnSuspension = a.cancelOnSuspension, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 8eaccd958..9f6fe7f54 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -1,23 +1,21 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.runner @@ -44,7 +42,7 @@ private typealias SuspensionPointResultWithContinuation = AtomicReference, @@ -325,7 +323,7 @@ internal open class ParallelThreadsRunner( } override fun needsTransformation() = true - override fun createTransformer(cv: ClassVisitor) = CancellabilitySupportClassTransformer(cv) + override fun createTransformer(cv: Any) = CancellabilitySupportClassTransformer(cv as ClassVisitor) override fun constructStateRepresentation() = stateRepresentationFunction?.let{ getMethod(testInstance, it) }?.invoke(testInstance) as String? @@ -336,8 +334,4 @@ internal open class ParallelThreadsRunner( } } -internal enum class UseClocks { ALWAYS, RANDOM } - -internal enum class CompletionStatus { CANCELLED, RESUMED } - private const val MAX_SPINNING_TIME_BEFORE_YIELD = 2_000_000 \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index e05439b36..167a25351 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -1,157 +1,38 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.runner import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.strategy.* -import org.objectweb.asm.* -import java.util.concurrent.atomic.* -import org.jetbrains.kotlinx.lincheck.annotations.StateRepresentation -import org.jetbrains.kotlinx.lincheck.execution.* -import java.io.* -/** - * Runner determines how to run your concurrent test. In order to support techniques - * like fibers, it may require code transformation, so that [createTransformer] should - * provide the corresponding transformer and [needsTransformation] should return `true`. - */ -abstract class Runner protected constructor( - protected val strategy: Strategy, - private val _testClass: TestClass, // will be transformed later - protected val validationFunctions: List, - protected val stateRepresentationFunction: StateRepresentationFunction? -) : Closeable { - protected var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` - protected lateinit var testClass: TestClass // not available before `initialize` call - @Suppress("LeakingThis") - val classLoader: ExecutionClassLoader = if (needsTransformation() || strategy.needsTransformation()) TransformationClassLoader(strategy, this) - else ExecutionClassLoader() - protected val completedOrSuspendedThreads = AtomicInteger(0) - - /** - * This method is a part of `Runner` initialization and should be invoked after this runner - * creation. It is separated from the constructor to perform the strategy initialization at first. - */ - open fun initialize() { - scenario = strategy.scenario.convertForLoader(classLoader) - testClass = TestClass(loadClass(_testClass.clazz.typeName)) - } - - /** - * Returns the current state representation of the test instance constructed via - * the function marked with [StateRepresentation] annotation, or `null` - * if no such function is provided. - * - * Please note, that it is unsafe to call this method concurrently with the running scenario. - * However, it is fine to call it if the execution is paused somewhere in the middle. - */ - open fun constructStateRepresentation(): String? = null - - /** - * Loads the specified class via this runner' class loader. - */ - private fun loadClass(className: String): Class<*> = classLoader.loadClass(className) - - /** - * Creates a transformer required for this runner. - * Throws [UnsupportedOperationException] by default. - * - * @return class visitor which transform the code due to support this runner. - */ - open fun createTransformer(cv: ClassVisitor): ClassVisitor? = null - - /** - * This method should return `true` if code transformation - * is required for this runner; returns `false` by default. - */ - open fun needsTransformation(): Boolean = false - - /** - * Runs the next invocation. - */ - abstract fun run(): InvocationResult +actual fun Runner.loadClassLoader(): Any = + if (needsTransformation() || strategy.needsTransformation()) TransformationClassLoader(strategy, this) else ExecutionClassLoader() - /** - * This method is invoked by every test thread as the first operation. - * @param iThread number of invoking thread - */ - open fun onStart(iThread: Int) {} +actual fun loadClass(classLoader: Any, testClass: TestClass): TestClass = TestClass((classLoader as ClassLoader).loadClass(testClass.clazz.typeName)) - /** - * This method is invoked by every test thread as the last operation - * if no exception has been thrown. - * @param iThread number of invoking thread - */ - open fun onFinish(iThread: Int) {} +actual typealias AtomicInteger = java.util.concurrent.atomic.AtomicInteger - /** - * This method is invoked by the corresponding test thread - * when an unexpected exception is thrown. - */ - open fun onFailure(iThread: Int, e: Throwable) {} - - /** - * This method is invoked by the corresponding test thread - * when the current coroutine suspends. - * @param iThread number of invoking thread - */ - open fun afterCoroutineSuspended(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") - - /** - * This method is invoked by the corresponding test thread - * when the current coroutine is resumed. - */ - open fun afterCoroutineResumed(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") - - /** - * This method is invoked by the corresponding test thread - * when the current coroutine is cancelled. - */ - open fun afterCoroutineCancelled(iThread: Int): Unit = throw UnsupportedOperationException("Coroutines are not supported") - - /** - * Returns `true` if the coroutine corresponding to - * the actor `actorId` in the thread `iThread` is resumed. - */ - open fun isCoroutineResumed(iThread: Int, actorId: Int): Boolean = throw UnsupportedOperationException("Coroutines are not supported") - - /** - * Is invoked before each actor execution from the specified thread. - * The invocations are inserted into the generated code. - */ - fun onActorStart(iThread: Int) { - strategy.onActorStart(iThread) +fun Runner.use(block: (Runner) -> R): R { + try { + return block(this) + } finally { + this.close() } - - /** - * Closes the resources used in this runner. - */ - override fun close() {} - - /** - * @return whether all scenario threads are completed or suspended - * Used by generated code. - */ - val isParallelExecutionCompleted: Boolean - get() = completedOrSuspendedThreads.get() == scenario.threads } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java index 53141033b..fcc18d6bd 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java @@ -110,7 +110,7 @@ public static TestThreadExecution create(Runner runner, int iThread, List String className = TestThreadExecution.class.getCanonicalName() + generatedClassNumber++; String internalClassName = className.replace('.', '/'); List objArgs = new ArrayList<>(); - Class clz = runner.getClassLoader().defineClass(className, + Class clz = ((ExecutionClassLoader)runner.getClassLoader()).defineClass(className, generateClass(internalClassName, getType(runner.getTestClass().getClazz()), iThread, actors, objArgs, completions, scenarioContainsSuspendableActors)); try { TestThreadExecution execution = clz.getDeclaredConstructor().newInstance(); diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 3dc2c8e17..b14274c1a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -117,7 +117,7 @@ abstract class ManagedStrategy( ManagedStrategyRunner(this, testClass, validationFunctions, stateRepresentationFunction, testCfg.timeoutMs, UseClocks.ALWAYS) private fun initializeManagedState() { - ManagedStrategyStateHolder.setState(runner.classLoader, this, testClass) + ManagedStrategyStateHolder.setState(runner.classLoader as ClassLoader, this, testClass) } override fun createTransformer(cv: ClassVisitor): ClassVisitor = ManagedStrategyTransformer( @@ -174,7 +174,7 @@ abstract class ManagedStrategy( ignoredSectionDepth.fill(0) callStackTrace.forEach { it.clear() } suspendedFunctionsStack.forEach { it.clear() } - ManagedStrategyStateHolder.resetState(runner.classLoader, testClass) + ManagedStrategyStateHolder.resetState(runner.classLoader as ClassLoader, testClass) } // == BASIC STRATEGY METHODS == @@ -222,9 +222,9 @@ abstract class ManagedStrategy( val sameResults = loggedResults !is CompletedInvocationResult || failingResult !is CompletedInvocationResult || loggedResults.results == failingResult.results check(sameResultTypes && sameResults) { StringBuilder().apply { - appendln("Non-determinism found. Probably caused by non-deterministic code (WeakHashMap, Object.hashCode, etc).") - appendln("Reporting scenario without execution trace.") - appendln(loggedResults.asLincheckFailureWithoutTrace().toString()) + appendLine("Non-determinism found. Probably caused by non-deterministic code (WeakHashMap, Object.hashCode, etc).") + appendLine("Reporting scenario without execution trace.") + appendLine(loggedResults.asLincheckFailureWithoutTrace().toString()) }.toString() } return Trace(traceCollector!!.trace, testCfg.verboseTrace) From a72dcef963f7a2571da6aa7253bf4c774749154c Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 16 Mar 2021 10:47:43 +0300 Subject: [PATCH 22/72] add native platform, fix stable state --- build.gradle.kts | 15 +- gradle.properties | 2 + gradle/native-targets.gradle | 166 ++++++++++++ .../kotlinx/lincheck/CTestStructure.kt | 2 + .../kotlinx/lincheck/CommonReporter.kt | 4 +- .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 23 +- .../jetbrains/kotlinx/lincheck/ValueResult.kt | 2 +- .../runner/FixedActiveThreadsExecutor.kt | 243 ++++++++++++++++++ .../lincheck/runner/ParallelThreadsRunner.kt | 4 + .../kotlinx/lincheck/runner/Runner.kt | 1 + .../kotlinx/lincheck/verifier/LTS.kt | 2 - .../kotlinx/lincheck/CTestStructure.kt | 6 +- .../jetbrains/kotlinx/lincheck/Reporter.kt | 2 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 22 +- .../runner/FixedActiveThreadsExecutor.kt | 219 ++-------------- .../lincheck/runner/ParallelThreadsRunner.kt | 7 +- .../lincheck/runner/TestThreadExecution.java | 11 +- .../strategy/managed/ManagedStrategy.kt | 2 +- .../runner/FixedActiveThreadsExecutorTest.kt | 6 +- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 45 ++++ .../jetbrains/kotlinx/lincheck/LinChecker.kt | 194 ++++++++++++++ .../jetbrains/kotlinx/lincheck/NativeUtils.kt | 77 ++++++ .../kotlinx/lincheck/ValidationFunction.kt | 46 ++++ .../jetbrains/kotlinx/lincheck/ValueResult.kt | 34 +++ .../kotlinx/lincheck/appendFailure.kt | 31 +++ .../lincheck/execution/ActorGenerator.kt | 44 ++++ .../runner/FixedActiveThreadsExecutor.kt | 55 ++++ .../lincheck/runner/ParallelThreadsRunner.kt | 139 ++++++++++ .../lincheck/runner/TestThreadExecution.kt | 52 ++++ .../kotlinx/lincheck/runner/ThreadDump.kt} | 7 +- .../lincheck/runner/loadClassLoader.kt | 46 ++++ .../kotlinx/lincheck/strategy/Strategy.kt | 41 +++ .../kotlinx/lincheck/strategy/Trace.kt | 23 ++ src/native/test/FirstTest.kt | 100 +++++++ 34 files changed, 1427 insertions(+), 246 deletions(-) create mode 100644 gradle/native-targets.gradle create mode 100644 src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt rename src/{jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt => native/main/org/jetbrains/kotlinx/lincheck/runner/ThreadDump.kt} (68%) create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/runner/loadClassLoader.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Trace.kt create mode 100644 src/native/test/FirstTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index f6eca8f30..cd2d10263 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,7 @@ import org.gradle.jvm.tasks.Jar +apply(from = rootProject.file("gradle/native-targets.gradle")) + // atomicfu buildscript { val atomicfuVersion: String by project @@ -38,6 +40,7 @@ kotlin { sourceSets { val commonMain by getting { kotlin.srcDir("src/common/main") + val kotlinVersion: String by project val kotlinxCoroutinesVersion: String by project val asmVersion: String by project @@ -53,6 +56,16 @@ kotlin { } } + val commonTest by getting { + kotlin.srcDir("src/common/test") + + val kotlinVersion: String by project + dependencies { + implementation(kotlin("test-common", kotlinVersion)) + implementation(kotlin("test-annotations-common", kotlinVersion)) + } + } + val jvmMain by getting { kotlin.srcDir("src/jvm/main") @@ -106,7 +119,7 @@ tasks { withType { maxParallelForks = 1 jvmArgs("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.util=ALL-UNNAMED") + "--add-exports", "java.base/jdk.internal.util=ALL-UNNAMED") } withType { diff --git a/gradle.properties b/gradle.properties index 4592638b4..fe556fb37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,6 +14,8 @@ reflectionsVersion=0.9.12 junitVersion=4.13.1 jctoolsVersion=2.1.0 +native.deploy= + # Workaround for Bintray treating .sha512 files as artifacts # https://github.com/gradle/gradle/issues/11412 systemProp.org.gradle.internal.publish.checksums.insecure=true \ No newline at end of file diff --git a/gradle/native-targets.gradle b/gradle/native-targets.gradle new file mode 100644 index 000000000..a345460e2 --- /dev/null +++ b/gradle/native-targets.gradle @@ -0,0 +1,166 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +/** + * Specifies what subset of Native targets to build + * + * ALL — all possible for compilation + * HOST — host-specific ones, without cross compilation (e.g. do not build linuxX64 on macOS host) + * SINGLE — only for current OS (to import into IDEA / quickly run tests) + * DISABLED — disable all Native targets (useful with Kotlin compiler built from sources) + * + * For HOST mode, all targets are still listed in .module file, so HOST mode is useful for release process. + */ +enum NativeState { ALL, HOST, SINGLE, DISABLED } + +def getNativeState(String description) { + if (description == null) return NativeState.SINGLE + switch(description.toLowerCase()) { + case 'all': + case 'true': + return NativeState.ALL + case 'host': + return NativeState.HOST + case 'disabled': + return NativeState.DISABLED + // 'single', 'false', etc + default: + return NativeState.SINGLE + } +} + +project.ext.ideaActive = System.getProperty('idea.active') == 'true' +project.ext.nativeState = getNativeState(property('native.deploy')) +project.ext.singleTargetMode = project.ext.ideaActive || (project.ext.nativeState == NativeState.SINGLE) + +project.ext.nativeMainSets = [] +project.ext.nativeTestSets = [] + +/** + * Disables compilation but leaves the target in .module file + */ +def disableCompilation(targets) { + configure(targets) { + compilations.all { + cinterops.all { project.tasks[interopProcessingTaskName].enabled = false } + compileKotlinTask.enabled = false + } + binaries.all { linkTask.enabled = false } + + mavenPublication { publicationToDisable -> + tasks.withType(AbstractPublishToMaven).all { + onlyIf { publication != publicationToDisable } + } + tasks.withType(GenerateModuleMetadata).all { + onlyIf { publication.get() != publicationToDisable } + } + } + } +} + +def getHostName() { + def target = System.getProperty("os.name") + if (target == 'Linux') return 'linux' + if (target.startsWith('Windows')) return 'windows' + if (target.startsWith('Mac')) return 'macos' + return 'unknown' +} + +kotlin { + targets { + //def manager = project.ext.hostManager + //def linuxEnabled = manager.isEnabled(presets.linuxX64.konanTarget) + //def macosEnabled = manager.isEnabled(presets.macosX64.konanTarget) + //def winEnabled = manager.isEnabled(presets.mingwX64.konanTarget) + + //def ideaPreset = presets.linuxX64 + //if (macosEnabled) ideaPreset = presets.macosX64 + //if (winEnabled) ideaPreset = presets.mingwX64 + //project.ext.ideaPreset = ideaPreset + project.ext.ideaPreset = presets.linuxX64 + } + + targets.metaClass.addTarget = { preset -> + def target = delegate.fromPreset(preset, preset.name) + project.ext.nativeMainSets.add(target.compilations['main'].kotlinSourceSets.first()) + project.ext.nativeTestSets.add(target.compilations['test'].kotlinSourceSets.first()) + } + + targets { + if (project.ext.nativeState == NativeState.DISABLED) return + if (project.ext.singleTargetMode) { + fromPreset(project.ext.ideaPreset, 'native') + } else { + // Linux + addTarget(presets.linuxX64) + addTarget(presets.linuxArm32Hfp) + addTarget(presets.linuxArm64) + + // Mac & iOS + addTarget(presets.macosX64) + + addTarget(presets.iosArm64) + addTarget(presets.iosArm32) + addTarget(presets.iosX64) + + addTarget(presets.watchosX86) + addTarget(presets.watchosArm32) + addTarget(presets.watchosArm64) + + addTarget(presets.tvosArm64) + addTarget(presets.tvosX64) + + // Windows + addTarget(presets.mingwX64) + addTarget(presets.mingwX86) + } + + if (project.ext.nativeState == NativeState.HOST) { + // linux targets that can cross-compile on all three hosts + def linuxCrossCompileTargets = [linuxX64, linuxArm32Hfp, linuxArm64] + if (getHostName() != "linux") { + disableCompilation(linuxCrossCompileTargets) + } + } + } + + + sourceSets { + nativeMain { + dependsOn commonMain + kotlin.srcDirs = ['src/native/main'] + } + // Empty source set is required in order to have native tests task + nativeTest { + dependsOn commonTest + kotlin.srcDirs = ['src/native/test'] + } + + if (!project.ext.singleTargetMode) { + configure(project.ext.nativeMainSets) { + dependsOn nativeMain + } + + configure(project.ext.nativeTestSets) { + dependsOn nativeTest + } + } + } +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index b149c662f..5e46f97d3 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -36,6 +36,8 @@ expect class StateRepresentationFunction expect class CTestStructure { val actorGenerators: List val operationGroups: List + val validationFunctions: List + val stateRepresentation: StateRepresentationFunction? } class OperationGroup(val name: String, val nonParallel: Boolean) { diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt index 78b56daf7..6fe8a7f36 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonReporter.kt @@ -100,7 +100,7 @@ private fun uniteActorsAndResultsAligned(actors: List, results: List. + * #L% + */ +package org.jetbrains.kotlinx.lincheck.runner + +import kotlinx.atomicfu.* +import kotlinx.coroutines.* +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import kotlin.jvm.* +import kotlin.math.* + +internal expect class TestThread(iThread: Int, runnerHash: Int, r: Runnable) { + + fun start() + fun stop() + + companion object { + fun currentThread(): Any? + } +} + +internal expect class LockSupport { + companion object { + fun park() + fun unpark(thread: Any?) + fun parkNanos(nanos: Long) + } +} + +internal expect fun currentTimeMillis(): Long + +/** + * This executor maintains the specified number of threads and is used by + * [ParallelThreadsRunner] to execute [ExecutionScenario]-s. The main feature + * is that this executor keeps the re-using threads "hot" (active) as long as + * possible, so that they should not be parked and unparked between invocations. + */ +internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: Int) { + // Threads used in this runner. + private val threads: List + /** + * null, waiting TestThread, Runnable task, or SHUTDOWN + */ + private val tasks = atomicArrayOfNulls(nThreads) + /** + * null, waiting in [submitAndAwait] thread, DONE, or exception + */ + private val results = atomicArrayOfNulls(nThreads) + /** + * Specifies the number of loop cycles for threads + * active waiting, after that they should be parked + */ + private var spinCount = 40000 + /** + * An adaptive active waiting strategy is used for the case when + * the number of threads is greater than the number of cores. + * This flag is set to `true` when any of the threads was parked + * during the previous [submitAndAwait] call. + */ + @Volatile + private var wasParked: Boolean = false + /** + * This balance is either increased or decreased by 1 at the + * end of each invocation when [wasParked] is `true` or `false` + * correspondingly. When the balance achieves [WAS_PARK_BALANCE_THRESHOLD], + * [spinCount] is doubled, and when the balance achieves + * -[WAS_PARK_BALANCE_THRESHOLD], [spinCount] is halved. + */ + private var wasParkedBalance: Int = 0 + + init { + threads = (0 until nThreads).map { iThread -> + TestThread(iThread, runnerHash, testThreadRunnable(iThread)).also { it.start() } + } + } + + /** + * Submits the specified set of [tasks] to this executor + * and waits until all of them are completed. + * The number of tasks should be equal to [nThreads]. + * + * @throws LincheckTimeoutException if more than [timeoutMs] is passed. + * @throws LincheckExecutionException if an unexpected exception is thrown during the execution. + */ + fun submitAndAwait(tasks: Array, timeoutMs: Long) { + require(tasks.size == nThreads) + submitTasks(tasks) + await(timeoutMs) + updateAdaptiveSpinCount() + } + + private fun submitTasks(tasks: Array) { + for (i in 0 until nThreads) { + results[i].value = null + submitTask(i, tasks[i]) + } + } + + private fun updateAdaptiveSpinCount() { + if (wasParked) { + wasParked = false + wasParkedBalance++ + if (wasParkedBalance >= WAS_PARK_BALANCE_THRESHOLD) { + spinCount /= 2 + wasParkedBalance = 0 + } + } else { + wasParkedBalance-- + if (wasParkedBalance <= -WAS_PARK_BALANCE_THRESHOLD) { + spinCount = min(spinCount * 2, MAX_SPIN_COUNT) + wasParkedBalance = 0 + } + } + } + + private fun submitTask(iThread: Int, task: Any) { + if (tasks[iThread].compareAndSet(null, task)) return + // CAS failed => a test thread is parked. + // Submit the task and unpark the waiting thread. + val thread = tasks[iThread].value as TestThread + tasks[iThread].value = task + LockSupport.unpark(thread) + } + + private fun await(timeoutMs: Long) { + val deadline = currentTimeMillis() + timeoutMs + for (iThread in 0 until nThreads) + awaitTask(iThread, deadline) + } + + private fun awaitTask(iThread: Int, deadline: Long) { + val result = getResult(iThread, deadline) + // Check whether there was an exception during the execution. + if (result != DONE) throw LincheckExecutionException(result as Throwable) + } + + private fun getResult(iThread: Int, deadline: Long): Any { + // Active wait for a result during the limited number of loop cycles. + spinWait { results[iThread].value }?.let { + return it + } + // Park with timeout until the result is set or the timeout is passed. + val currentThread = TestThread.currentThread() + if (results[iThread].compareAndSet(null, TestThread.currentThread())) { + while (results[iThread].value === currentThread) { + val timeLeft = deadline - currentTimeMillis() + if (timeLeft <= 0) throw LincheckTimeoutException() + LockSupport.parkNanos(timeLeft * 1_000_000) + } + } + return results[iThread].value!! + } + + private fun testThreadRunnable(iThread: Int) = Runnable { + loop@while (true) { + val task = getTask(iThread) + if (task == SHUTDOWN) return@Runnable + tasks[iThread].value = null // reset task + val runnable = task as Runnable + try { + runnable.run() + } catch(e: Throwable) { + setResult(iThread, wrapInvalidAccessFromUnnamedModuleExceptionWithDescription(e)) + continue@loop + } + setResult(iThread, DONE) + } + } + + private fun getTask(iThread: Int): Any { + // Active wait for a task for the limited number of loop cycles. + spinWait { tasks[iThread].value }?.let { + return it + } + // Park until a task is stored into `tasks[iThread]`. + val currentThread = TestThread.currentThread() + if (tasks[iThread].compareAndSet(null, TestThread.currentThread())) { + while (tasks[iThread].value === currentThread) { + LockSupport.park() + } + } + return tasks[iThread].value!! + } + + private fun setResult(iThread: Int, any: Any) { + if (results[iThread].compareAndSet(null, any)) return + // CAS failed => a test thread is parked. + // Set the result and unpark the waiting thread. + val thread = results[iThread].value + results[iThread].value = any + LockSupport.unpark(thread) + } + + private inline fun spinWait(getter: () -> Any?): Any? { + repeat(spinCount) { + getter()?.let { + return it + } + } + wasParked = true + return null + } + + fun close() { + // submit the shutdown task. + submitTasks(Array(nThreads) { SHUTDOWN }) + for (t in threads) t.stop() + } + + inline fun use(block: (FixedActiveThreadsExecutor) -> R): R { + try { + return block(this) + } finally { + close() + } + } + + companion object { + private val SHUTDOWN = Any() + private val DONE = Any() + private const val MAX_SPIN_COUNT = 1_000_000 + private const val WAS_PARK_BALANCE_THRESHOLD = 20 + } +} diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 8eb132d5c..08075f0ec 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -40,6 +40,10 @@ internal expect open class ParallelThreadsRunner( useClocks: UseClocks // specifies whether `HBClock`-s should always be used or with some probability ) : Runner +internal class LincheckExecutionException(cause: Throwable?): Exception(cause) + +internal class LincheckTimeoutException: Exception() + internal enum class UseClocks { ALWAYS, RANDOM } internal enum class CompletionStatus { CANCELLED, RESUMED } \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index 56523044d..06aeeb83e 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -33,6 +33,7 @@ expect fun loadClass(classLoader: Any, testClass: TestClass): TestClass expect class AtomicInteger(value: Int) { fun get(): Int + fun set(value: Int) } /** diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index 372326acd..1a91493f8 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -33,8 +33,6 @@ import kotlin.reflect.* typealias RemappingFunction = IntArray typealias ResumedTickets = Set -expect fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification<*>): SequentialSpecification - /** * Common interface for different labeled transition systems, which several correctness formalisms use. * Lincheck widely uses LTS-based formalisms for verification, see [AbstractLTSVerifier] implementations as examples. diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index aa23e001e..f674bcac6 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -45,8 +45,8 @@ actual typealias StateRepresentationFunction = Method actual class CTestStructure private constructor( actual val actorGenerators: List, actual val operationGroups: List, - val validationFunctions: List, - val stateRepresentation: StateRepresentationFunction? + actual val validationFunctions: List, + actual val stateRepresentation: StateRepresentationFunction? ) { companion object { /** @@ -117,7 +117,7 @@ actual class CTestStructure private constructor( actorGenerators.add(actorGenerator) // Get list of groups and add this operation to specified ones val opGroup = opAnn.group - if (!opGroup.isEmpty()) { + if (opGroup.isNotEmpty()) { val operationGroup = groupConfigs[opGroup] ?: throw IllegalStateException("Operation group $opGroup is not configured") operationGroup.actors.add(actorGenerator) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt index aec0b7a27..5affcdf8a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt @@ -51,7 +51,7 @@ internal actual fun StringBuilder.appendDeadlockWithDumpFailure(failure: Deadloc appendExecutionScenario(failure.scenario) appendLine() for ((t, stackTrace) in failure.threadDump.dump) { - val threadNumber = if (t is FixedActiveThreadsExecutor.TestThread) t.iThread.toString() else "?" + val threadNumber = if (t is TestThread) t.iThread.toString() else "?" appendLine("Thread-$threadNumber:") stackTrace.map { StackTraceElement(it.className.removePrefix(TransformationClassLoader.REMAPPED_PACKAGE_CANONICAL_NAME), it.methodName, it.fileName, it.lineNumber) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index a1716871e..dfa6e44c4 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -36,7 +36,6 @@ import java.lang.reflect.Method import java.util.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* -import kotlin.reflect.* import kotlin.reflect.full.* import kotlin.reflect.jvm.* @@ -46,6 +45,9 @@ actual class TestClass(val clazz: Class<*>) { actual fun createInstance(): Any = clazz.getDeclaredConstructor().newInstance() } +actual fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification<*>): SequentialSpecification = + TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv) }.loadClass(sequentialSpecification.name)!! + actual fun chooseSequentialSpecification(sequentialSpecificationByUser: SequentialSpecification<*>?, testClass: TestClass): SequentialSpecification<*> = if (sequentialSpecificationByUser === DummySequentialSpecification::class.java || sequentialSpecificationByUser == null) testClass.clazz else sequentialSpecificationByUser @@ -159,7 +161,7 @@ private val cancelCompletedResultMethod = DispatchedTask::class.declaredFunction actual fun storeCancellableContinuation(cont: CancellableContinuation<*>) { val t = Thread.currentThread() - if (t is FixedActiveThreadsExecutor.TestThread) { + if (t is TestThread) { t.cont = cont } else { storedLastCancellableCont = cont @@ -235,8 +237,8 @@ private class CustomObjectInputStream(val loader: ClassLoader, inputStream: Inpu * Collects the current thread dump and keeps only those * threads that are related to the specified [runner]. */ -internal fun collectThreadDump(runner: Runner) = ThreadDump(Thread.getAllStackTraces().filter { (t, _) -> - t is FixedActiveThreadsExecutor.TestThread && t.runnerHash == runner.hashCode() +internal actual fun collectThreadDump(runner: Runner) = ThreadDump(Thread.getAllStackTraces().filter { (t, _) -> + t is TestThread && t.runnerHash == runner.hashCode() }) /** @@ -248,15 +250,3 @@ internal fun getRemapperByTransformers(classTransformers: List): R classTransformers.any { it is ManagedStrategyTransformer } -> JavaUtilRemapper() else -> null } - -fun wrapInvalidAccessFromUnnamedModuleExceptionWithDescription(e: Throwable): Throwable { - if (e.message?.contains("to unnamed module") ?: false) { - return RuntimeException(ADD_OPENS_MESSAGE, e) - } - return e -} - -private val ADD_OPENS_MESSAGE = "It seems that you use Java 9+ and the code uses Unsafe or similar constructions that are not accessible from unnamed modules.\n" + - "Please add the following lines to your test running configuration:\n" + - "--add-opens java.base/jdk.internal.misc=ALL-UNNAMED\n" + - "--add-exports java.base/jdk.internal.util=ALL-UNNAMED" \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 0bf67fe65..6b07e2698 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -1,222 +1,41 @@ -/*- - * #%L +/* * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ package org.jetbrains.kotlinx.lincheck.runner -import kotlinx.atomicfu.* import kotlinx.coroutines.CancellableContinuation -import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.execution.* -import java.io.* import java.lang.* -import java.util.concurrent.* -import java.util.concurrent.locks.* -import kotlin.math.* -/** - * This executor maintains the specified number of threads and is used by - * [ParallelThreadsRunner] to execute [ExecutionScenario]-s. The main feature - * is that this executor keeps the re-using threads "hot" (active) as long as - * possible, so that they should not be parked and unparked between invocations. - */ -internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: Int) : Closeable { - // Threads used in this runner. - private val threads: List - /** - * null, waiting TestThread, Runnable task, or SHUTDOWN - */ - private val tasks = atomicArrayOfNulls(nThreads) - /** - * null, waiting in [submitAndAwait] thread, DONE, or exception - */ - private val results = atomicArrayOfNulls(nThreads) - /** - * Specifies the number of loop cycles for threads - * active waiting, after that they should be parked - */ - private var spinCount = 40000 - /** - * An adaptive active waiting strategy is used for the case when - * the number of threads is greater than the number of cores. - * This flag is set to `true` when any of the threads was parked - * during the previous [submitAndAwait] call. - */ - @Volatile - private var wasParked: Boolean = false - /** - * This balance is either increased or decreased by 1 at the - * end of each invocation when [wasParked] is `true` or `false` - * correspondingly. When the balance achieves [WAS_PARK_BALANCE_THRESHOLD], - * [spinCount] is doubled, and when the balance achieves - * -[WAS_PARK_BALANCE_THRESHOLD], [spinCount] is halved. - */ - private var wasParkedBalance: Int = 0 - - init { - threads = (0 until nThreads).map { iThread -> - TestThread(iThread, runnerHash, testThreadRunnable(iThread)).also { it.start() } - } - } - - /** - * Submits the specified set of [tasks] to this executor - * and waits until all of them are completed. - * The number of tasks should be equal to [nThreads]. - * - * @throws TimeoutException if more than [timeoutMs] is passed. - * @throws ExecutionException if an unexpected exception is thrown during the execution. - */ - fun submitAndAwait(tasks: Array, timeoutMs: Long) { - require(tasks.size == nThreads) - submitTasks(tasks) - await(timeoutMs) - updateAdaptiveSpinCount() - } - - private fun submitTasks(tasks: Array) { - for (i in 0 until nThreads) { - results[i].value = null - submitTask(i, tasks[i]) - } - } - - private fun updateAdaptiveSpinCount() { - if (wasParked) { - wasParked = false - wasParkedBalance++ - if (wasParkedBalance >= WAS_PARK_BALANCE_THRESHOLD) { - spinCount /= 2 - wasParkedBalance = 0 - } - } else { - wasParkedBalance-- - if (wasParkedBalance <= -WAS_PARK_BALANCE_THRESHOLD) { - spinCount = min(spinCount * 2, MAX_SPIN_COUNT) - wasParkedBalance = 0 - } - } - } - - private fun submitTask(iThread: Int, task: Any) { - if (tasks[iThread].compareAndSet(null, task)) return - // CAS failed => a test thread is parked. - // Submit the task and unpark the waiting thread. - val thread = tasks[iThread].value as TestThread - tasks[iThread].value = task - LockSupport.unpark(thread) - } - - private fun await(timeoutMs: Long) { - val deadline = System.currentTimeMillis() + timeoutMs - for (iThread in 0 until nThreads) - awaitTask(iThread, deadline) - } - - private fun awaitTask(iThread: Int, deadline: Long) { - val result = getResult(iThread, deadline) - // Check whether there was an exception during the execution. - if (result != DONE) throw ExecutionException(result as Throwable) - } +internal actual class TestThread actual constructor(val iThread: Int, val runnerHash: Int, r: Runnable) : Thread(r, "FixedActiveThreadsExecutor@$runnerHash-$iThread") { + var cont: CancellableContinuation<*>? = null - private fun getResult(iThread: Int, deadline: Long): Any { - // Active wait for a result during the limited number of loop cycles. - spinWait { results[iThread].value }?.let { - return it - } - // Park with timeout until the result is set or the timeout is passed. - val currentThread = Thread.currentThread() - if (results[iThread].compareAndSet(null, Thread.currentThread())) { - while (results[iThread].value === currentThread) { - val timeLeft = deadline - System.currentTimeMillis() - if (timeLeft <= 0) throw TimeoutException() - LockSupport.parkNanos(timeLeft * 1_000_000) - } - } - return results[iThread].value!! - } - - private fun testThreadRunnable(iThread: Int) = Runnable { - loop@while (true) { - val task = getTask(iThread) - if (task == SHUTDOWN) return@Runnable - tasks[iThread].value = null // reset task - val runnable = task as Runnable - try { - runnable.run() - } catch(e: Throwable) { - setResult(iThread, wrapInvalidAccessFromUnnamedModuleExceptionWithDescription(e)) - continue@loop - } - setResult(iThread, DONE) - } - } - - private fun getTask(iThread: Int): Any { - // Active wait for a task for the limited number of loop cycles. - spinWait { tasks[iThread].value }?.let { - return it - } - // Park until a task is stored into `tasks[iThread]`. - val currentThread = Thread.currentThread() - if (tasks[iThread].compareAndSet(null, Thread.currentThread())) { - while (tasks[iThread].value === currentThread) { - LockSupport.park() - } - } - return tasks[iThread].value!! - } - - private fun setResult(iThread: Int, any: Any) { - if (results[iThread].compareAndSet(null, any)) return - // CAS failed => a test thread is parked. - // Set the result and unpark the waiting thread. - val thread = results[iThread].value as Thread - results[iThread].value = any - LockSupport.unpark(thread) - } - - private inline fun spinWait(getter: () -> Any?): Any? { - repeat(spinCount) { - getter()?.let { - return it - } - } - wasParked = true - return null - } - - override fun close() { - // submit the shutdown task. - submitTasks(Array(nThreads) { SHUTDOWN }) - for (t in threads) t.stop() - } - - class TestThread(val iThread: Int, val runnerHash: Int, r: Runnable) : Thread(r, "FixedActiveThreadsExecutor@$runnerHash-$iThread") { - var cont: CancellableContinuation<*>? = null + actual companion object { + actual fun currentThread(): Any? = Thread.currentThread() // For storing identifier and then call unpark() } +} - companion object { - private val SHUTDOWN = Any() - private val DONE = Any() - private const val MAX_SPIN_COUNT = 1_000_000 - private const val WAS_PARK_BALANCE_THRESHOLD = 20 +internal actual class LockSupport { + actual companion object { + actual fun park() = java.util.concurrent.locks.LockSupport.park() + actual fun unpark(thread: Any?) = java.util.concurrent.locks.LockSupport.unpark(thread as Thread) + actual fun parkNanos(nanos: Long) = java.util.concurrent.locks.LockSupport.parkNanos(nanos) } } + +internal actual fun currentTimeMillis() = System.currentTimeMillis() \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 9f6fe7f54..15ff1f76d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -24,7 +24,6 @@ import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.CancellationResult.* import org.jetbrains.kotlinx.lincheck.execution.* -import org.jetbrains.kotlinx.lincheck.runner.FixedActiveThreadsExecutor.TestThread import org.jetbrains.kotlinx.lincheck.runner.UseClocks.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.objectweb.asm.* @@ -51,7 +50,7 @@ internal actual open class ParallelThreadsRunner actual constructor( private val useClocks: UseClocks // specifies whether `HBClock`-s should always be used or with some probability ) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others - private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash) // shoukd be closed in `close()` + private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash) // should be closed in `close()` private lateinit var testInstance: Any private lateinit var testThreadExecutions: Array @@ -259,10 +258,10 @@ internal actual open class ParallelThreadsRunner actual constructor( val afterInitStateRepresentation = constructStateRepresentation() try { executor.submitAndAwait(testThreadExecutions, timeoutMs) - } catch (e: TimeoutException) { + } catch (e: LincheckTimeoutException) { val threadDump = collectThreadDump(this) return DeadlockInvocationResult(threadDump) - } catch (e: ExecutionException) { + } catch (e: LincheckExecutionException) { return UnexpectedExceptionInvocationResult(e.cause!!) } val parallelResultsWithClock = testThreadExecutions.map { ex -> diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java index 0c72ab8dc..a1ebc21ba 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.java @@ -1,9 +1,8 @@ /* - * #%L * Lincheck - * %% - * Copyright (C) 2015 - 2018 Devexperts, LLC - * %% + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -16,9 +15,9 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * . - * #L% + * */ + package org.jetbrains.kotlinx.lincheck.runner; import org.jetbrains.kotlinx.lincheck.Result; diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index e04915ae5..b283278aa 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -681,7 +681,7 @@ abstract class ManagedStrategy( */ fun currentThreadNumber(): Int { val t = Thread.currentThread() - return if (t is FixedActiveThreadsExecutor.TestThread) { + return if (t is TestThread) { t.iThread } else { nThreads diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt index 0749b2890..c5ceda126 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt @@ -68,8 +68,8 @@ class FixedActiveThreadsExecutorTest { } try { executor.submitAndAwait(tasks, 200) - } catch (e: TimeoutException) { - return // TimeoutException is expected + } catch (e: LincheckTimeoutException) { + return@use // LincheckTimeoutException is expected } check(false) { "TimeoutException was expected" } } @@ -81,7 +81,7 @@ class FixedActiveThreadsExecutorTest { FixedActiveThreadsExecutor(2, uniqueRunnerHash).close() while (true) { // check that all test threads are finished - if (Thread.getAllStackTraces().keys.all { it !is FixedActiveThreadsExecutor.TestThread || it.runnerHash != uniqueRunnerHash }) + if (Thread.getAllStackTraces().keys.all { it !is TestThread || it.runnerHash != uniqueRunnerHash }) return } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt new file mode 100644 index 000000000..9a4612777 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -0,0 +1,45 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +/** + * The actor entity describe the operation with its parameters + * which is executed during the testing. + * + * @see Operation + */ +actual class Actor( + val function: (Any, List) -> Any?, // (instance, arguments) -> result + val arguments: List, + actual val isSuspendable: Boolean = false, + actual val allowExtraSuspension: Boolean = false, + actual val promptCancellation: Boolean = false, + actual val cancelOnSuspension: Boolean = false +) { + + actual override fun toString(): String { + TODO("Not yet implemented") + } + +} + +actual val Actor.isQuiescentConsistent: Boolean + get() = false // TODO check \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt new file mode 100644 index 000000000..04ee2dcb9 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -0,0 +1,194 @@ +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.jvm.* +import kotlin.reflect.* + +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + + +/** + * This class runs concurrent tests. + */ +class LinChecker (private val testClass: TestClass, private val testStructure: CTestStructure, options: Options<*, *>) { + private val testConfigurations: List + private val reporter: Reporter + + init { + val logLevel = options?.logLevel ?: DEFAULT_LOG_LEVEL + reporter = Reporter(logLevel) + testConfigurations = listOf(options.createTestConfigurations(testClass)) + } + + /** + * @throws LincheckAssertionError if the testing data structure is incorrect. + */ + fun check() { + val failure = checkImpl() ?: return + throw RuntimeException("testing data structure is incorrect $failure") // TODO rewrite throw LincheckAssertionError(failure) + } + + /** + * @return TestReport with information about concurrent test run. + */ + internal fun checkImpl(): LincheckFailure? { + check(testConfigurations.isNotEmpty()) { "No Lincheck test configuration to run" } + for (testCfg in testConfigurations) { + val failure = testCfg.checkImpl() + if (failure != null) return failure + } + return null + } + + private fun CTestConfiguration.checkImpl(): LincheckFailure? { + val exGen = createExecutionGenerator() + val verifier = createVerifier() + repeat(iterations) { i -> + val scenario = exGen.nextExecution() + scenario.validate() + reporter.logIteration(i + 1, iterations, scenario) + val failure = scenario.run(this, verifier) + if (failure != null) { + val minimizedFailedIteration = if (!minimizeFailedScenario) failure + else failure.minimize(this, verifier) + reporter.logFailedIteration(minimizedFailedIteration) + return minimizedFailedIteration + } + } + return null + } + + // Tries to minimize the specified failing scenario to make the error easier to understand. + // The algorithm is greedy: it tries to remove one actor from the scenario and checks + // whether a test with the modified one fails with error as well. If it fails, + // then the scenario has been successfully minimized, and the algorithm tries to minimize it again, recursively. + // Otherwise, if no actor can be removed so that the generated test fails, the minimization is completed. + // Thus, the algorithm works in the linear time of the total number of actors. + private fun LincheckFailure.minimize(testCfg: CTestConfiguration, verifier: Verifier): LincheckFailure { + reporter.logScenarioMinimization(scenario) + val parallelExecution = scenario.parallelExecution.map { it.toMutableList() }.toMutableList() + val initExecution = scenario.initExecution.toMutableList() + val postExecution = scenario.postExecution.toMutableList() + for (i in scenario.parallelExecution.indices) { + for (j in scenario.parallelExecution[i].indices) { + val newParallelExecution = parallelExecution.map { it.toMutableList() }.toMutableList() + newParallelExecution[i].removeAt(j) + if (newParallelExecution[i].isEmpty()) newParallelExecution.removeAt(i) // remove empty thread + val newScenario = ExecutionScenario( + initExecution, + newParallelExecution, + postExecution + ) + val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) + if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) + } + } + for (i in scenario.initExecution.indices) { + val newInitExecution = initExecution.toMutableList() + newInitExecution.removeAt(i) + val newScenario = ExecutionScenario( + newInitExecution, + parallelExecution, + postExecution + ) + val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) + if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) + } + for (i in scenario.postExecution.indices) { + val newPostExecution = postExecution.toMutableList() + newPostExecution.removeAt(i) + val newScenario = ExecutionScenario( + initExecution, + parallelExecution, + newPostExecution + ) + val newFailedIteration = newScenario.tryMinimize(testCfg, verifier) + if (newFailedIteration != null) return newFailedIteration.minimize(testCfg, verifier) + } + return this + } + + private fun ExecutionScenario.tryMinimize(testCfg: CTestConfiguration, verifier: Verifier) = + if (isValid) run(testCfg, verifier) else null + + private fun ExecutionScenario.run(testCfg: CTestConfiguration, verifier: Verifier): LincheckFailure? = + testCfg.createStrategy( + testClass = testClass, + scenario = this, + validationFunctions = testStructure.validationFunctions, + stateRepresentationFunction = testStructure.stateRepresentation, + verifier = verifier + ).run() + + private fun ExecutionScenario.copy() = ExecutionScenario( + ArrayList(initExecution), + parallelExecution.map { ArrayList(it) }, + ArrayList(postExecution) + ) + + private val ExecutionScenario.isValid: Boolean + get() = !isParallelPartEmpty && + (!hasSuspendableActors() || (!hasSuspendableActorsInInitPart && !hasPostPartAndSuspendableActors)) + + private fun ExecutionScenario.validate() { + require(!isParallelPartEmpty) { + "The generated scenario has empty parallel part" + } + if (hasSuspendableActors()) { + require(!hasSuspendableActorsInInitPart) { + "The generated scenario for the test class with suspendable methods contains suspendable actors in initial part" + } + require(!hasPostPartAndSuspendableActors) { + "The generated scenario for the test class with suspendable methods has non-empty post part" + } + } + } + + private val ExecutionScenario.hasSuspendableActorsInInitPart get() = + initExecution.any(Actor::isSuspendable) + private val ExecutionScenario.hasPostPartAndSuspendableActors get() = + (parallelExecution.any { actors -> actors.any { it.isSuspendable } } && postExecution.size > 0) + private val ExecutionScenario.isParallelPartEmpty get() = + parallelExecution.map { it.size }.sum() == 0 + + + private fun CTestConfiguration.createVerifier() = + verifierGenerator(this.sequentialSpecification).also { + if (requireStateEquivalenceImplCheck) it.checkStateEquivalenceImplementation() + } + + private fun CTestConfiguration.createExecutionGenerator() = + executionGenerator(this, testStructure) + + // This companion object is used for backwards compatibility. + companion object { + /** + * Runs the specified concurrent tests. If [options] is null, the provided on + * the testing class `@...CTest` annotations are used to specify the test parameters. + * + * @throws AssertionError if any of the tests fails. + */ + fun check(testClass: TestClass, testStructure: CTestStructure, options: Options<*, *>) { + LinChecker(testClass, testStructure, options).check() + } + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt new file mode 100644 index 000000000..aec0ee4bb --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt @@ -0,0 +1,77 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import kotlinx.coroutines.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.runner.* +import kotlin.coroutines.* + +actual class TestClass( + actual val name: String, + val function: () -> Any +) { + actual fun createInstance(): Any = function() +} + +actual class SequentialSpecification (val function: () -> Any) + +actual fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification<*>): SequentialSpecification = sequentialSpecification as SequentialSpecification + +actual fun SequentialSpecification.getInitialState(): T = function() as T + +internal actual fun createLincheckResult(res: Any?, wasSuspended: Boolean): Result { + TODO("Not yet implemented") +} + +internal actual fun executeValidationFunction(instance: Any, validationFunction: ValidationFunction): Throwable? { + TODO("Not yet implemented") +} + +/** + * loader class type should be ClassLoader in jvm + */ +internal actual fun ExecutionScenario.convertForLoader(loader: Any): ExecutionScenario { + return this +} + +actual fun chooseSequentialSpecification(sequentialSpecificationByUser: SequentialSpecification<*>?, testClass: TestClass): SequentialSpecification<*> = + sequentialSpecificationByUser ?: SequentialSpecification(testClass.function) + +actual fun storeCancellableContinuation(cont: CancellableContinuation<*>) { + TODO("Not yet implemented") +} + +internal actual fun executeActor( + instance: Any, + actor: Actor, + completion: Continuation? +): Result { + TODO("Not yet implemented") +} + +/** + * Collects the current thread dump and keeps only those + * threads that are related to the specified [runner]. + */ +internal actual fun collectThreadDump(runner: Runner): ThreadDump { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt new file mode 100644 index 000000000..834058bc2 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt @@ -0,0 +1,46 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.execution.* + +actual class ValidationFunction + +actual val ValidationFunction.name: String + get() = TODO("Not yet implemented") + +actual class StateRepresentationFunction( + val function: (Any) -> Any? +) + +/** + * Contains information about the provided operations (see [Operation]). + * Several [tests][StressCTest] can refer to one structure + * (i.e. one test class could have several [StressCTest] annotations) + */ +actual class CTestStructure constructor( + actual val actorGenerators: List, + actual val operationGroups: List, + actual val validationFunctions: List, + actual val stateRepresentation: StateRepresentationFunction? +) { + +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt new file mode 100644 index 000000000..74364d6d0 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt @@ -0,0 +1,34 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +/** + * Type of result used if the actor invocation returns any value. + */ +actual class ValueResult(actual val value: Any?, override val wasSuspended: Boolean = false) : Result() { + actual override fun equals(other: Any?): Boolean { + TODO("Not yet implemented") + } + + actual override fun hashCode(): Int { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt new file mode 100644 index 000000000..47194a891 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt @@ -0,0 +1,31 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.strategy.* + +internal actual fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilder { + TODO("Not yet implemented") +} + +internal actual fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDumpFailure): StringBuilder { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt new file mode 100644 index 000000000..89c9548a9 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -0,0 +1,44 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.execution + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.paramgen.* + +actual class ActorGenerator( + val function: (Any, List) -> Any?, // (instance, arguments) -> result + val parameterGenerators: List>, + actual val useOnce: Boolean = false, + actual val isSuspendable: Boolean = false +) { + actual override fun toString(): String { + TODO("Not yet implemented") + } + + actual fun generate(threadId: Int): Actor { + val arguments = parameterGenerators.map { it.generate() } + return Actor( + function = function, + arguments = arguments, + isSuspendable = isSuspendable + ) + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt new file mode 100644 index 000000000..7d34adedf --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -0,0 +1,55 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import kotlinx.coroutines.* +import kotlin.system.* + +@ThreadLocal +val currentThreadId = Any() + +internal actual class TestThread actual constructor(iThread: Int, runnerHash: Int, r: Runnable) { + actual fun start() { + } + + actual fun stop() { + } + + actual companion object { + actual fun currentThread(): Any? = currentThreadId + } + +} + +internal actual class LockSupport { + actual companion object { + actual fun park() { + } + + actual fun unpark(thread: Any?) { + } + + actual fun parkNanos(nanos: Long) { + } + } +} + +internal actual fun currentTimeMillis() = getTimeMillis() \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt new file mode 100644 index 000000000..15bca0921 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -0,0 +1,139 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import kotlin.coroutines.* +import kotlin.random.* + +/** + * This runner executes parallel scenario part in different threads. + * Supports running scenarios with `suspend` functions. + * + * It is pretty useful for stress testing or if you do not care about context switch expenses. + */ +internal actual open class ParallelThreadsRunner actual constructor( + strategy: Strategy, + testClass: TestClass, + validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, + private val timeoutMs: Long, + private val useClocks: UseClocks) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { + private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others + + private lateinit var testInstance: Any + private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash) // should be closed in `close()` + + private lateinit var testThreadExecutions: Array + + override fun initialize() { + super.initialize() + testThreadExecutions = Array(scenario.threads) { t -> + TestThreadExecution(this, t, scenario.parallelExecution[t]) + } + testThreadExecutions.forEach { it.allThreadExecutions = testThreadExecutions } + } + + private fun reset() { + testInstance = testClass.createInstance() + testThreadExecutions.forEachIndexed { t, ex -> + ex.testInstance = testInstance + val threads = scenario.threads + val actors = scenario.parallelExecution[t].size + ex.useClocks = if (useClocks == UseClocks.ALWAYS) true else Random.nextBoolean() + ex.curClock = 0 + ex.clocks = Array(actors) { emptyClockArray(threads) } + ex.results = arrayOfNulls(actors) + } + completedOrSuspendedThreads.set(0) + } + + override fun constructStateRepresentation() = + stateRepresentationFunction?.function?.invoke(testInstance) as String? + + override fun run(): InvocationResult { + reset() + val initResults = scenario.initExecution.mapIndexed { i, initActor -> + executeActor(testInstance, initActor).also { + executeValidationFunctions(testInstance, validationFunctions) { functionName, exception -> + val s = ExecutionScenario( + scenario.initExecution.subList(0, i + 1), + emptyList(), + emptyList() + ) + return ValidationFailureInvocationResult(s, functionName, exception) + } + } + } + val afterInitStateRepresentation = constructStateRepresentation() + try { + executor.submitAndAwait(testThreadExecutions, timeoutMs) + } catch (e: LincheckTimeoutException) { + val threadDump = collectThreadDump(this) + return DeadlockInvocationResult(threadDump) + } catch (e: LincheckExecutionException) { + return UnexpectedExceptionInvocationResult(e.cause!!) + } + val parallelResultsWithClock = testThreadExecutions.map { ex -> + ex.results.zip(ex.clocks).map { ResultWithClock(it.first!!, HBClock(it.second)) } + } + executeValidationFunctions(testInstance, validationFunctions) { functionName, exception -> + val s = ExecutionScenario( + scenario.initExecution, + scenario.parallelExecution, + emptyList() + ) + return ValidationFailureInvocationResult(s, functionName, exception) + } + val afterParallelStateRepresentation = constructStateRepresentation() + val dummyCompletion = Continuation(EmptyCoroutineContext) {} + var postPartSuspended = false + val postResults = scenario.postExecution.mapIndexed { i, postActor -> + // no actors are executed after suspension of a post part + val result = if (postPartSuspended) { + NoResult + } else { + // post part may contain suspendable actors if there aren't any in the parallel part, invoke with dummy continuation + executeActor(testInstance, postActor, dummyCompletion).also { + postPartSuspended = it.wasSuspended + } + } + executeValidationFunctions(testInstance, validationFunctions) { functionName, exception -> + val s = ExecutionScenario( + scenario.initExecution, + scenario.parallelExecution, + scenario.postExecution.subList(0, i + 1) + ) + return ValidationFailureInvocationResult(s, functionName, exception) + } + result + } + val afterPostStateRepresentation = constructStateRepresentation() + val results = ExecutionResult( + initResults, afterInitStateRepresentation, + parallelResultsWithClock, afterParallelStateRepresentation, + postResults, afterPostStateRepresentation + ) + return CompletedInvocationResult(results) + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt new file mode 100644 index 000000000..18b5601e4 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt @@ -0,0 +1,52 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import kotlinx.coroutines.* +import org.jetbrains.kotlinx.lincheck.* + +class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List): Runnable { + lateinit var testInstance: Any + lateinit var objArgs: Array + lateinit var allThreadExecutions: Array + + lateinit var results: Array // for ExecutionResult + lateinit var clocks: Array // for HBClock + + // TODO should be volatilie + var curClock = 0 + var useClocks = false + + fun readClocks(currentActor: Int) { + for (i in allThreadExecutions.indices) { + clocks[currentActor][i] = allThreadExecutions[i].curClock + } + } + + fun incClock() { + curClock++ + } + + + override fun run() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ThreadDump.kt similarity index 68% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt rename to src/native/main/org/jetbrains/kotlinx/lincheck/runner/ThreadDump.kt index 0041dd9aa..0321c0f94 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/JvmLTS.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ThreadDump.kt @@ -18,9 +18,6 @@ * */ -package org.jetbrains.kotlinx.lincheck.verifier +package org.jetbrains.kotlinx.lincheck.runner -import org.jetbrains.kotlinx.lincheck.* - -actual fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification<*>): SequentialSpecification = - TransformationClassLoader { cv -> CancellabilitySupportClassTransformer(cv) }.loadClass(sequentialSpecification.name)!! \ No newline at end of file +actual class ThreadDump \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/loadClassLoader.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/loadClassLoader.kt new file mode 100644 index 000000000..6602ef317 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/loadClassLoader.kt @@ -0,0 +1,46 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.runner + +import org.jetbrains.kotlinx.lincheck.* +import kotlin.native.concurrent.* + +// TODO check + +actual fun Runner.loadClassLoader(): Any { + return Any() +} + +actual fun loadClass(classLoader: Any, testClass: TestClass): TestClass { + return testClass +} + +actual class AtomicInteger actual constructor(value: Int) { + val atomicValue = AtomicInt(value) + + actual fun get(): Int { + return atomicValue.value + } + + actual fun set(value: Int): Unit { + atomicValue.value = value + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt new file mode 100644 index 000000000..d63c6caf7 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -0,0 +1,41 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.strategy + +import org.jetbrains.kotlinx.lincheck.execution.* + +/** + * Implementation of this class describes how to run the generated execution. + * + * Note that strategy can run execution several times. For strategy creating + * [.createStrategy] method is used. It is impossible to add a new strategy + * without any code change. + */ +actual abstract class Strategy protected actual constructor(actual val scenario: ExecutionScenario) { + + actual abstract fun run(): LincheckFailure? + + /** + * Is invoked before each actor execution. + */ + actual open fun onActorStart(iThread: Int) { + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Trace.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Trace.kt new file mode 100644 index 000000000..c0857b7f0 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/Trace.kt @@ -0,0 +1,23 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.strategy + +actual class Trace \ No newline at end of file diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt new file mode 100644 index 000000000..cae9ee6d5 --- /dev/null +++ b/src/native/test/FirstTest.kt @@ -0,0 +1,100 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +import kotlinx.atomicfu.locks.SynchronizedObject +import kotlinx.atomicfu.locks.synchronized +import kotlinx.cinterop.* +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.test.* + +class FirstTest: VerifierState() { + val state = A() + + override fun extractState(): Any { + return state + } + + @Test + fun test() { + val testClass = TestClass("FirstTest") { FirstTest() } + + val actorGenerator1 = ActorGenerator( + function = { + instance, arguments -> + (instance as FirstTest).state.a() + }, + parameterGenerators = listOf() + ) + val actorGenerator2 = ActorGenerator( + function = { + instance, arguments -> + (instance as FirstTest).state.b() + }, + parameterGenerators = listOf() + ) + val actorGenerators: List = listOf(actorGenerator1, actorGenerator2) + val operationGroups: List = listOf() + val validationFunctions: List = listOf() + val stateRepresentation: StateRepresentationFunction? = null + val testStructure = CTestStructure( + actorGenerators = actorGenerators, + operationGroups = operationGroups, + validationFunctions = validationFunctions, + stateRepresentation = stateRepresentation + ) + + val options = StressOptions().run { + iterations(30) + actorsBefore(2) + threads(3) + actorsPerThread(2) + actorsAfter(2) + minimizeFailedScenario(false) + } + + LinChecker.check(testClass = testClass, testStructure = testStructure, options = options) + } + + class A : SynchronizedObject() { + private var sharedState = false + + fun a() = synchronized(this) { + sharedState = true + if (!sharedState) throw AssertionError() + } + + fun b() = synchronized(this) { + sharedState = false + if (!sharedState) throw AssertionError() + } + + override fun equals(other: Any?): Boolean { + return this.sharedState == (other as A).sharedState + } + + override fun hashCode(): Int { + return this.sharedState.toByte().toInt() + } + } +} \ No newline at end of file From 71fce28b8d90f654e6d825d20f52f6efc4a81595 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 16 Mar 2021 16:01:25 +0300 Subject: [PATCH 23/72] another stable state --- .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 7 ++-- .../runner/FixedActiveThreadsExecutor.kt | 4 +- .../strategy/stress/StressStrategy.kt | 1 + .../kotlinx/lincheck/verifier/LTS.kt | 1 - .../org/jetbrains/kotlinx/lincheck/Utils.kt | 1 - .../org/jetbrains/kotlinx/lincheck/Actor.kt | 10 +++-- .../jetbrains/kotlinx/lincheck/NativeUtils.kt | 41 ++++++++++++++++++- .../jetbrains/kotlinx/lincheck/ValueResult.kt | 8 +--- .../kotlinx/lincheck/appendFailure.kt | 31 -------------- .../runner/FixedActiveThreadsExecutor.kt | 12 +++++- .../lincheck/runner/TestThreadExecution.kt | 20 +++++++-- src/native/test/FirstTest.kt | 18 ++++---- 12 files changed, 92 insertions(+), 62 deletions(-) delete mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index f623cf4f3..d0b9d4e7b 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* import kotlin.coroutines.* +import kotlin.jvm.* import kotlin.reflect.* expect class TestClass { @@ -34,13 +35,13 @@ expect class TestClass { } expect class SequentialSpecification + expect fun SequentialSpecification.getInitialState(): T expect fun chooseSequentialSpecification(sequentialSpecificationByUser: SequentialSpecification<*>?, testClass: TestClass): SequentialSpecification<*> -object CancellableContinuationHolder { - var storedLastCancellableCont: CancellableContinuation<*>? = null -} +//@Volatile +internal var storedLastCancellableCont: CancellableContinuation<*>? = null expect fun storeCancellableContinuation(cont: CancellableContinuation<*>) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 0dddd5c8d..6204565e9 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -136,7 +136,7 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: if (tasks[iThread].compareAndSet(null, task)) return // CAS failed => a test thread is parked. // Submit the task and unpark the waiting thread. - val thread = tasks[iThread].value as TestThread + val thread = tasks[iThread].value tasks[iThread].value = task LockSupport.unpark(thread) } @@ -148,7 +148,9 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: } private fun awaitTask(iThread: Int, deadline: Long) { + println("awaitTask $iThread") val result = getResult(iThread, deadline) + println("awaitTask $iThread RESULT GOT") // Check whether there was an exception during the execution. if (result != DONE) throw LincheckExecutionException(result as Throwable) } diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 53c3510c6..6ad137e54 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -60,6 +60,7 @@ class StressStrategy( runner.also { // Run invocations for (invocation in 0 until invocations) { + println("run invocaton $invocation") when (val ir = runner.run()) { is CompletedInvocationResult -> { if (!verifier.verifyResults(scenario, ir.results)) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index 1a91493f8..f26ddba34 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -22,7 +22,6 @@ package org.jetbrains.kotlinx.lincheck.verifier import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.CancellableContinuationHolder.storedLastCancellableCont import org.jetbrains.kotlinx.lincheck.verifier.LTS.* import org.jetbrains.kotlinx.lincheck.verifier.OperationType.* import kotlin.collections.HashMap diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index dfa6e44c4..7eaec9361 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -22,7 +22,6 @@ package org.jetbrains.kotlinx.lincheck import kotlinx.coroutines.* -import org.jetbrains.kotlinx.lincheck.CancellableContinuationHolder.storedLastCancellableCont import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.managed.* diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 9a4612777..e8c7d8d40 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -35,10 +35,12 @@ actual class Actor( actual val cancelOnSuspension: Boolean = false ) { - actual override fun toString(): String { - TODO("Not yet implemented") - } - + actual override fun toString(): String = + function.toString() + + arguments.joinToString(prefix = "(", postfix = ")", separator = ", ") { it.toString() } + + (if (cancelOnSuspension) " + " else "") + + (if (promptCancellation) "prompt_" else "") + + (if (cancelOnSuspension) "cancel" else "") } actual val Actor.isQuiescentConsistent: Boolean diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt index aec0ee4bb..ba3c6ae7d 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt @@ -23,6 +23,8 @@ package org.jetbrains.kotlinx.lincheck import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import platform.posix.* import kotlin.coroutines.* actual class TestClass( @@ -65,7 +67,7 @@ internal actual fun executeActor( actor: Actor, completion: Continuation? ): Result { - TODO("Not yet implemented") + return ValueResult(actor.function(instance, actor.arguments)) } /** @@ -73,5 +75,40 @@ internal actual fun executeActor( * threads that are related to the specified [runner]. */ internal actual fun collectThreadDump(runner: Runner): ThreadDump { - TODO("Not yet implemented") + return ThreadDump() // No thread dumps in kotlin native +} + +internal actual fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilder { + when (failure) { + is IncorrectResultsFailure -> appendIncorrectResultsFailure(failure) + is DeadlockWithDumpFailure -> appendDeadlockWithDumpFailure(failure) + is UnexpectedExceptionFailure -> appendUnexpectedExceptionFailure(failure) + is ValidationFailure -> appendValidationFailure(failure) + is ObstructionFreedomViolationFailure -> appendObstructionFreedomViolationFailure(failure) + } + val results = if (failure is IncorrectResultsFailure) failure.results else null + if (failure.trace != null) { + appendLine() + appendLine("= The following interleaving leads to the error =") + appendLine("Sorry, there are no trace in kotlin native :(") + if (failure is DeadlockWithDumpFailure) { + appendLine() + append("All threads are in deadlock") + } + } + return this +} + +internal actual fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDumpFailure): StringBuilder { + appendLine("= The execution has hung, see the thread dump =") + appendExecutionScenario(failure.scenario) + appendLine() + appendLine("Sorry, there are no threadDumps in kotlin native :(") + return this +} + +val STDERR = platform.posix.fdopen(2, "w") +fun printErr(message: String) { + fprintf(STDERR, message + "\n") + fflush(STDERR) } \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt index 74364d6d0..44c33e6b6 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt @@ -24,11 +24,7 @@ package org.jetbrains.kotlinx.lincheck * Type of result used if the actor invocation returns any value. */ actual class ValueResult(actual val value: Any?, override val wasSuspended: Boolean = false) : Result() { - actual override fun equals(other: Any?): Boolean { - TODO("Not yet implemented") - } + actual override fun equals(other: Any?): Boolean = if (other !is ValueResult) false else other.wasSuspended == wasSuspended && other.value == value - actual override fun hashCode(): Int { - TODO("Not yet implemented") - } + actual override fun hashCode(): Int = if (wasSuspended) 0 else 1 // We can't use value here } \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt deleted file mode 100644 index 47194a891..000000000 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/appendFailure.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2021 JetBrains s.r.o. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * - */ - -package org.jetbrains.kotlinx.lincheck - -import org.jetbrains.kotlinx.lincheck.strategy.* - -internal actual fun StringBuilder.appendFailure(failure: LincheckFailure): StringBuilder { - TODO("Not yet implemented") -} - -internal actual fun StringBuilder.appendDeadlockWithDumpFailure(failure: DeadlockWithDumpFailure): StringBuilder { - TODO("Not yet implemented") -} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 7d34adedf..c8d067bc5 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -21,16 +21,25 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* +import org.jetbrains.kotlinx.lincheck.* +import kotlin.native.ThreadLocal +import kotlin.native.concurrent.* import kotlin.system.* @ThreadLocal val currentThreadId = Any() -internal actual class TestThread actual constructor(iThread: Int, runnerHash: Int, r: Runnable) { +internal actual class TestThread actual constructor(val iThread: Int, val runnerHash: Int, val r: Runnable) { + val worker: Worker = Worker.start() + actual fun start() { + worker.execute(TransferMode.UNSAFE, { r }, { r -> r.run() }) } actual fun stop() { + printErr("stop() $iThread called") + worker.requestTermination(false) + printErr("stop() $iThread finished") } actual companion object { @@ -48,6 +57,7 @@ internal actual class LockSupport { } actual fun parkNanos(nanos: Long) { + platform.posix.sleep((nanos / 1_000_000).toUInt()) } } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt index 18b5601e4..041f28ebf 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt @@ -23,7 +23,7 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* -class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List): Runnable { +class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List) : Runnable { lateinit var testInstance: Any lateinit var objArgs: Array lateinit var allThreadExecutions: Array @@ -45,8 +45,20 @@ class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List curClock++ } - override fun run() { - TODO("Not yet implemented") + //printErr("RUN $iThread #1") + runner.onStart(iThread) + actors.forEachIndexed { index, actor -> + //printErr("RUN $iThread #2 $index") + readClocks(index) + // TODO add try-catch + runner.onActorStart(iThread) + // Load arguments for operation + val result = actor.function(testInstance, actor.arguments) + results[index] = ValueResult(result) + incClock() + } + //printErr("RUN $iThread #finish ") + runner.onFinish(iThread) } -} \ No newline at end of file +} diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index cae9ee6d5..23e7fd7b7 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.native.concurrent.* import kotlin.test.* class FirstTest: VerifierState() { @@ -65,7 +66,8 @@ class FirstTest: VerifierState() { ) val options = StressOptions().run { - iterations(30) + iterations(10) + invocationsPerIteration(10) actorsBefore(2) threads(3) actorsPerThread(2) @@ -77,24 +79,24 @@ class FirstTest: VerifierState() { } class A : SynchronizedObject() { - private var sharedState = false + private val sharedState: AtomicInt = AtomicInt(0) fun a() = synchronized(this) { - sharedState = true - if (!sharedState) throw AssertionError() + sharedState.increment() + //printErr("a(), sharedState = ${sharedState.toString()}") } fun b() = synchronized(this) { - sharedState = false - if (!sharedState) throw AssertionError() + sharedState.decrement() + //printErr("b(), sharedState = ${sharedState.toString()}") } override fun equals(other: Any?): Boolean { - return this.sharedState == (other as A).sharedState + return this.sharedState.value == (other as A).sharedState.value } override fun hashCode(): Int { - return this.sharedState.toByte().toInt() + return this.sharedState.value } } } \ No newline at end of file From e7232295b36cd424c7e0271e9e3797770d30dfea Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 16 Mar 2021 16:39:31 +0300 Subject: [PATCH 24/72] fix bug with many iterations --- .../kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt | 2 -- .../kotlinx/lincheck/strategy/stress/StressStrategy.kt | 2 +- .../kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt | 7 ++++--- .../kotlinx/lincheck/runner/ParallelThreadsRunner.kt | 5 +++++ src/native/test/FirstTest.kt | 4 ++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 6204565e9..41d61d745 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -148,9 +148,7 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: } private fun awaitTask(iThread: Int, deadline: Long) { - println("awaitTask $iThread") val result = getResult(iThread, deadline) - println("awaitTask $iThread RESULT GOT") // Check whether there was an exception during the execution. if (result != DONE) throw LincheckExecutionException(result as Throwable) } diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 6ad137e54..d784bf837 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -60,7 +60,7 @@ class StressStrategy( runner.also { // Run invocations for (invocation in 0 until invocations) { - println("run invocaton $invocation") + //println("run invocaton $invocation") when (val ir = runner.run()) { is CompletedInvocationResult -> { if (!verifier.verifyResults(scenario, ir.results)) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index c8d067bc5..2cb13cf52 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -33,13 +33,14 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner val worker: Worker = Worker.start() actual fun start() { + //printErr("start() $iThread called") worker.execute(TransferMode.UNSAFE, { r }, { r -> r.run() }) } actual fun stop() { - printErr("stop() $iThread called") + //printErr("stop() $iThread called") worker.requestTermination(false) - printErr("stop() $iThread finished") + //printErr("stop() $iThread finished") } actual companion object { @@ -57,7 +58,7 @@ internal actual class LockSupport { } actual fun parkNanos(nanos: Long) { - platform.posix.sleep((nanos / 1_000_000).toUInt()) + //platform.posix.sleep((nanos / 1_000_000_000).toUInt()) } } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 15bca0921..3ba8c0684 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -136,4 +136,9 @@ internal actual open class ParallelThreadsRunner actual constructor( ) return CompletedInvocationResult(results) } + + override fun close() { + super.close() + executor.close() + } } \ No newline at end of file diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 23e7fd7b7..db131ae0e 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -66,8 +66,8 @@ class FirstTest: VerifierState() { ) val options = StressOptions().run { - iterations(10) - invocationsPerIteration(10) + iterations(30) + invocationsPerIteration(100) actorsBefore(2) threads(3) actorsPerThread(2) From 1b4b001e3f00db7b110240ba44a275a91e279d78 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sun, 28 Mar 2021 22:57:41 +0300 Subject: [PATCH 25/72] change model, make it work --- build.gradle.kts | 6 ++ gradle.properties | 3 + gradle/native-targets.gradle | 18 ++--- .../runner/FixedActiveThreadsExecutor.kt | 8 +-- .../runner/FixedActiveThreadsExecutor.kt | 6 ++ .../kotlinx/lincheck/test/CompareSpeedTest.kt | 72 +++++++++++++++++++ .../runner/FixedActiveThreadsExecutor.kt | 20 ++++-- src/native/test/FirstTest.kt | 2 +- 8 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 src/jvm/test/org/jetbrains/kotlinx/lincheck/test/CompareSpeedTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index cd2d10263..888b1b568 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,11 @@ import org.gradle.jvm.tasks.Jar +allprojects { + ext { + set("hostManager", org.jetbrains.kotlin.konan.target.HostManager()) + } +} + apply(from = rootProject.file("gradle/native-targets.gradle")) // atomicfu diff --git a/gradle.properties b/gradle.properties index fe556fb37..84a19cd02 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,6 +16,9 @@ jctoolsVersion=2.1.0 native.deploy= +kotlin.mpp.enableGranularSourceSetsMetadata=true +kotlin.mpp.enableCompatibilityMetadataVariant=true + # Workaround for Bintray treating .sha512 files as artifacts # https://github.com/gradle/gradle/issues/11412 systemProp.org.gradle.internal.publish.checksums.insecure=true \ No newline at end of file diff --git a/gradle/native-targets.gradle b/gradle/native-targets.gradle index a345460e2..6ecef92b8 100644 --- a/gradle/native-targets.gradle +++ b/gradle/native-targets.gradle @@ -85,15 +85,15 @@ def getHostName() { kotlin { targets { - //def manager = project.ext.hostManager - //def linuxEnabled = manager.isEnabled(presets.linuxX64.konanTarget) - //def macosEnabled = manager.isEnabled(presets.macosX64.konanTarget) - //def winEnabled = manager.isEnabled(presets.mingwX64.konanTarget) - - //def ideaPreset = presets.linuxX64 - //if (macosEnabled) ideaPreset = presets.macosX64 - //if (winEnabled) ideaPreset = presets.mingwX64 - //project.ext.ideaPreset = ideaPreset + def manager = project.ext.hostManager + def linuxEnabled = manager.isEnabled(presets.linuxX64.konanTarget) + def macosEnabled = manager.isEnabled(presets.macosX64.konanTarget) + def winEnabled = manager.isEnabled(presets.mingwX64.konanTarget) + + def ideaPreset = presets.linuxX64 + if (macosEnabled) ideaPreset = presets.macosX64 + if (winEnabled) ideaPreset = presets.mingwX64 + project.ext.ideaPreset = ideaPreset project.ext.ideaPreset = presets.linuxX64 } diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 41d61d745..0b55fad01 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -30,8 +30,8 @@ import kotlin.math.* internal expect class TestThread(iThread: Int, runnerHash: Int, r: Runnable) { - fun start() - fun stop() + fun execute() + fun terminate(): Runnable? companion object { fun currentThread(): Any? @@ -89,7 +89,7 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: init { threads = (0 until nThreads).map { iThread -> - TestThread(iThread, runnerHash, testThreadRunnable(iThread)).also { it.start() } + TestThread(iThread, runnerHash, testThreadRunnable(iThread)).also { it.execute() } } } @@ -223,7 +223,7 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: fun close() { // submit the shutdown task. submitTasks(Array(nThreads) { SHUTDOWN }) - for (t in threads) t.stop() + for (t in threads) t.terminate() } inline fun use(block: (FixedActiveThreadsExecutor) -> R): R { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 6b07e2698..eb28d54b2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -28,6 +28,12 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner actual companion object { actual fun currentThread(): Any? = Thread.currentThread() // For storing identifier and then call unpark() } + + actual fun execute() = start() + actual fun terminate(): Runnable? { + stop() + return null + } } internal actual class LockSupport { diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/CompareSpeedTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/CompareSpeedTest.kt new file mode 100644 index 000000000..4e0ab72dc --- /dev/null +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/CompareSpeedTest.kt @@ -0,0 +1,72 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2021 JetBrains s.r.o. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * + */ + +package org.jetbrains.kotlinx.lincheck.test + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.junit.* +import java.util.concurrent.* +import java.util.concurrent.atomic.* + +class A { + private val sharedState: AtomicInteger = AtomicInteger(0) + + fun a() = synchronized(this) { + sharedState.incrementAndGet() + //printErr("a(), sharedState = ${sharedState.toString()}") + } + + fun b() = synchronized(this) { + sharedState.decrementAndGet() + //printErr("b(), sharedState = ${sharedState.toString()}") + } + + override fun equals(other: Any?): Boolean { + return this.sharedState.get() == (other as A).sharedState.get() + } + + override fun hashCode(): Int { + return this.sharedState.get() + } +} + +@StressCTest(iterations = 30, invocationsPerIteration = 10000, actorsBefore = 2, actorsPerThread = 2, actorsAfter = 2, threads = 3, minimizeFailedScenario = false) +class CompareSpeedTest: VerifierState() { + val state = A() + + override fun extractState(): Any { + return state + } + + @Operation + fun operation1() = state.a() + + @Operation + fun operation2() = state.b() + + @Test + fun test() { + LinChecker.check(CompareSpeedTest::class.java) + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 2cb13cf52..f9b895a53 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -22,6 +22,7 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* +import platform.posix.* import kotlin.native.ThreadLocal import kotlin.native.concurrent.* import kotlin.system.* @@ -30,34 +31,41 @@ import kotlin.system.* val currentThreadId = Any() internal actual class TestThread actual constructor(val iThread: Int, val runnerHash: Int, val r: Runnable) { - val worker: Worker = Worker.start() + val worker: Worker = Worker.start(true, "Worker $iThread $runnerHash") + var runnableFuture: Future? = null - actual fun start() { + actual fun execute() { //printErr("start() $iThread called") - worker.execute(TransferMode.UNSAFE, { r }, { r -> r.run() }) + worker.execute(TransferMode.UNSAFE, { r }, { r -> + r.run() + r + }) } - actual fun stop() { + actual fun terminate(): Runnable? { //printErr("stop() $iThread called") - worker.requestTermination(false) + val res = runnableFuture?.result + //worker.requestTermination(true) + return res //printErr("stop() $iThread finished") } actual companion object { actual fun currentThread(): Any? = currentThreadId } - } internal actual class LockSupport { actual companion object { actual fun park() { + //usleep(1000.toUInt()) // 1ms } actual fun unpark(thread: Any?) { } actual fun parkNanos(nanos: Long) { + //usleep(1000.toUInt()) // 1ms //platform.posix.sleep((nanos / 1_000_000_000).toUInt()) } } diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index db131ae0e..95b9b1053 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -66,7 +66,7 @@ class FirstTest: VerifierState() { ) val options = StressOptions().run { - iterations(30) + iterations(300) invocationsPerIteration(100) actorsBefore(2) threads(3) From 78f60cfeafc5a0374af742076ce4587215086c29 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 31 Mar 2021 03:56:33 +0300 Subject: [PATCH 26/72] Add native API, fix bugs, copy runner on every new iteration --- gradle.properties | 8 +- .../runner/FixedActiveThreadsExecutor.kt | 2 +- .../strategy/stress/StressStrategy.kt | 43 +++-- .../runner/FixedActiveThreadsExecutor.kt | 3 +- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 3 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 152 +++++++++++++++++- .../jetbrains/kotlinx/lincheck/NativeUtils.kt | 6 +- .../kotlinx/lincheck/ValidationFunction.kt | 6 +- .../jetbrains/kotlinx/lincheck/ValueResult.kt | 2 + .../lincheck/execution/ActorGenerator.kt | 2 + .../runner/FixedActiveThreadsExecutor.kt | 17 +- src/native/test/FirstTest.kt | 110 +++++++++---- 12 files changed, 281 insertions(+), 73 deletions(-) diff --git a/gradle.properties b/gradle.properties index 84a19cd02..c681f42cd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,10 +5,10 @@ version=2.13-SNAPSHOT inceptionYear=2019 lastCopyrightYear=2020 -kotlinVersion=1.4.0 -kotlinxCoroutinesVersion=1.4.0 -asmVersion=9.0 -atomicfuVersion=0.14.4 +kotlinVersion=1.4.32 +kotlinxCoroutinesVersion=1.4.3 +asmVersion=9.1 +atomicfuVersion=0.15.2 reflectionsVersion=0.9.12 junitVersion=4.13.1 diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 0b55fad01..cc1b9d1f5 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -31,7 +31,7 @@ import kotlin.math.* internal expect class TestThread(iThread: Int, runnerHash: Int, r: Runnable) { fun execute() - fun terminate(): Runnable? + fun terminate() companion object { fun currentThread(): Any? diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index d784bf837..36c73612e 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -28,15 +28,15 @@ import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* class StressStrategy( - testCfg: StressCTestConfiguration, - testClass: TestClass, + private val testCfg: StressCTestConfiguration, + private val testClass: TestClass, scenario: ExecutionScenario, - validationFunctions: List, - stateRepresentationFunction: StateRepresentationFunction?, + private val validationFunctions: List, + private val stateRepresentationFunction: StateRepresentationFunction?, private val verifier: Verifier ) : Strategy(scenario) { private val invocations = testCfg.invocationsPerIteration - private val runner: Runner + private var runner: Runner init { runner = ParallelThreadsRunner( @@ -55,12 +55,29 @@ class StressStrategy( } } - override fun run(): LincheckFailure? { + fun reset() { + runner = ParallelThreadsRunner( + strategy = this, + testClass = testClass, + validationFunctions = validationFunctions, + stateRepresentationFunction = stateRepresentationFunction, + timeoutMs = testCfg.timeoutMs, + useClocks = UseClocks.RANDOM + ) try { - runner.also { - // Run invocations - for (invocation in 0 until invocations) { - //println("run invocaton $invocation") + runner.initialize() + } catch (t: Throwable) { + runner.close() + throw t + } + } + + override fun run(): LincheckFailure? { + // Run invocations + for (invocation in 0 until invocations) { + reset() + try { + runner.also { when (val ir = runner.run()) { is CompletedInvocationResult -> { if (!verifier.verifyResults(scenario, ir.results)) @@ -69,10 +86,10 @@ class StressStrategy( else -> return ir.toLincheckFailure(scenario) } } - return null + } finally { + runner.close() } - } finally { - runner.close() } + return null } } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index eb28d54b2..ee3d4df06 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -30,9 +30,8 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner } actual fun execute() = start() - actual fun terminate(): Runnable? { + actual fun terminate() { stop() - return null } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt index e8c7d8d40..5eb0f2bac 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -29,6 +29,7 @@ package org.jetbrains.kotlinx.lincheck actual class Actor( val function: (Any, List) -> Any?, // (instance, arguments) -> result val arguments: List, + val functionName: String = function.toString(), actual val isSuspendable: Boolean = false, actual val allowExtraSuspension: Boolean = false, actual val promptCancellation: Boolean = false, @@ -36,7 +37,7 @@ actual class Actor( ) { actual override fun toString(): String = - function.toString() + + functionName + arguments.joinToString(prefix = "(", postfix = ")", separator = ", ") { it.toString() } + (if (cancelOnSuspension) " + " else "") + (if (promptCancellation) "prompt_" else "") + diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 04ee2dcb9..b57500e43 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -1,6 +1,8 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.jvm.* import kotlin.reflect.* @@ -25,11 +27,143 @@ import kotlin.reflect.* * */ +class LincheckStressConfiguration : StressOptions() { + /* + invocationsPerIteration + iterations + threads + actorsPerThread + actorsBefore + actorsAfter + executionGenerator(executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator) + verifier(verifier: (sequentialSpecification: SequentialSpecification<*>) -> Verifier) + requireStateEquivalenceImplCheck + minimizeFailedScenario + createTestConfigurations + logLevel(logLevel: LoggingLevel) + sequentialSpecification(clazz: SequentialSpecification<*>?) + */ + private var testClass: TestClass? = null + private var actorGenerators = mutableListOf() + private var operationGroups = mutableListOf() + private var validationFunctions = mutableListOf() + private var stateRepresentationFunction: StateRepresentationFunction? = null + + internal fun getTestClass(): TestClass { + return testClass ?: throw IllegalArgumentException("initialState should be specified") + } + + internal fun getTestStructure(): CTestStructure { + return CTestStructure( + actorGenerators, + operationGroups, + validationFunctions, + stateRepresentationFunction + ) + } + + fun runTest() { + LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) + } + + // =========================== Constructor + + fun initialState( + state: () -> Instance, + className: String = "default className" + ) { + testClass = TestClass(className, state) + } + + // =========================== Operation + + fun operation( + op: Instance.() -> R, + name: String = op.toString(), + useOnce: Boolean = false, + isSuspendable: Boolean = false + ) { + val actorGenerator = ActorGenerator( + function = { instance, arguments -> + instance as Instance // check that operation can be applied to instance + instance.op() + }, + parameterGenerators = listOf(), + functionName = name, + useOnce = useOnce, + isSuspendable = isSuspendable + ) + actorGenerators.add(actorGenerator) + } + + fun operation( + p1Gen: ParameterGenerator, + op: Instance.(p1: P1) -> R, + name: String = op.toString(), + useOnce: Boolean = false, + isSuspendable: Boolean = false + ) { + val actorGenerator = ActorGenerator( + function = { instance, arguments -> + instance as Instance // check that operation can be applied to instance + instance.op(arguments[0] as P1) // extract arguments and cast to type + }, + parameterGenerators = listOf(p1Gen), + functionName = name, + useOnce = useOnce, + isSuspendable = isSuspendable + ) + actorGenerators.add(actorGenerator) + } + + fun operation( + p1Gen: ParameterGenerator, + p2Gen: ParameterGenerator, + op: Instance.(p1: P1, p2: P2) -> R, + name: String = op.toString(), + useOnce: Boolean = false, + isSuspendable: Boolean = false + ) { + val actorGenerator = ActorGenerator( + function = { instance, arguments -> + instance as Instance // check that operation can be applied to instance + instance.op(arguments[0] as P1, arguments[1] as P2) // extract arguments and cast to type + }, + parameterGenerators = listOf(p1Gen, p2Gen), + functionName = name, + useOnce = useOnce, + isSuspendable = isSuspendable + ) + actorGenerators.add(actorGenerator) + } + + // ============================= Validation Function + + fun validationFunction( + validate: Instance.() -> Unit + ) { + validationFunctions.add(ValidationFunction { instance -> + instance as Instance // check that operation can be applied to instance + instance.validate() + }) + } + + // ============================= State Representation Function + + fun stateRepresentation( + state: Instance.() -> String + ) { + stateRepresentationFunction = StateRepresentationFunction { instance -> + instance as Instance // check that operation can be applied to instance + instance.state() + } + } +} /** * This class runs concurrent tests. */ -class LinChecker (private val testClass: TestClass, private val testStructure: CTestStructure, options: Options<*, *>) { +class LinChecker(private val testClass: TestClass, private val testStructure: CTestStructure, options: Options<*, *>) { private val testConfigurations: List private val reporter: Reporter @@ -63,6 +197,7 @@ class LinChecker (private val testClass: TestClass, private val testStructure: C val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> + println("Iteration $i") val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(i + 1, iterations, scenario) @@ -163,12 +298,15 @@ class LinChecker (private val testClass: TestClass, private val testStructure: C } } - private val ExecutionScenario.hasSuspendableActorsInInitPart get() = - initExecution.any(Actor::isSuspendable) - private val ExecutionScenario.hasPostPartAndSuspendableActors get() = - (parallelExecution.any { actors -> actors.any { it.isSuspendable } } && postExecution.size > 0) - private val ExecutionScenario.isParallelPartEmpty get() = - parallelExecution.map { it.size }.sum() == 0 + private val ExecutionScenario.hasSuspendableActorsInInitPart + get() = + initExecution.any(Actor::isSuspendable) + private val ExecutionScenario.hasPostPartAndSuspendableActors + get() = + (parallelExecution.any { actors -> actors.any { it.isSuspendable } } && postExecution.size > 0) + private val ExecutionScenario.isParallelPartEmpty + get() = + parallelExecution.map { it.size }.sum() == 0 private fun CTestConfiguration.createVerifier() = diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt index ba3c6ae7d..517661bcd 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt @@ -29,12 +29,12 @@ import kotlin.coroutines.* actual class TestClass( actual val name: String, - val function: () -> Any + val function: () -> Any? ) { - actual fun createInstance(): Any = function() + actual fun createInstance(): Any = function() ?: throw IllegalArgumentException("Constructor should not return null") } -actual class SequentialSpecification (val function: () -> Any) +actual class SequentialSpecification (val function: () -> Any?) actual fun loadSequentialSpecification(sequentialSpecification: SequentialSpecification<*>): SequentialSpecification = sequentialSpecification as SequentialSpecification diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt index 834058bc2..90de5f40b 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt @@ -22,13 +22,15 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.execution.* -actual class ValidationFunction +actual class ValidationFunction( + val function: (Any) -> Unit +) actual val ValidationFunction.name: String get() = TODO("Not yet implemented") actual class StateRepresentationFunction( - val function: (Any) -> Any? + val function: (Any) -> String ) /** diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt index 44c33e6b6..edfaf3c34 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt @@ -27,4 +27,6 @@ actual class ValueResult(actual val value: Any?, override val wasSuspended: Bool actual override fun equals(other: Any?): Boolean = if (other !is ValueResult) false else other.wasSuspended == wasSuspended && other.value == value actual override fun hashCode(): Int = if (wasSuspended) 0 else 1 // We can't use value here + + override fun toString() = wasSuspendedPrefix + "$value" } \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index 89c9548a9..413522b1c 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlinx.lincheck.paramgen.* actual class ActorGenerator( val function: (Any, List) -> Any?, // (instance, arguments) -> result val parameterGenerators: List>, + val functionName: String = function.toString(), actual val useOnce: Boolean = false, actual val isSuspendable: Boolean = false ) { @@ -38,6 +39,7 @@ actual class ActorGenerator( return Actor( function = function, arguments = arguments, + functionName = functionName, isSuspendable = isSuspendable ) } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index f9b895a53..72d4f004a 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -21,8 +21,6 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* -import org.jetbrains.kotlinx.lincheck.* -import platform.posix.* import kotlin.native.ThreadLocal import kotlin.native.concurrent.* import kotlin.system.* @@ -30,23 +28,28 @@ import kotlin.system.* @ThreadLocal val currentThreadId = Any() +val results = mutableSetOf() + internal actual class TestThread actual constructor(val iThread: Int, val runnerHash: Int, val r: Runnable) { val worker: Worker = Worker.start(true, "Worker $iThread $runnerHash") var runnableFuture: Future? = null actual fun execute() { //printErr("start() $iThread called") - worker.execute(TransferMode.UNSAFE, { r }, { r -> + runnableFuture = worker.execute(TransferMode.UNSAFE, { r }, { r -> r.run() r }) } - actual fun terminate(): Runnable? { + actual fun terminate() { + runnableFuture!!.state.value //printErr("stop() $iThread called") - val res = runnableFuture?.result - //worker.requestTermination(true) - return res + //val res = runnableFuture?.result + val result = runnableFuture?.result + results.add(result) // to prevent from garbage collecting + //worker.requestTermination(true).result + //return res //printErr("stop() $iThread finished") } diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 95b9b1053..8392d08fe 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -29,28 +29,93 @@ import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.native.concurrent.* import kotlin.test.* -class FirstTest: VerifierState() { - val state = A() +class TestClass : VerifierState() { + val atomicState: AtomicInt = AtomicInt(0) + var regularState: Int = 0 override fun extractState(): Any { - return state + return Pair(atomicState.value, regularState) } + override fun toString(): String { + return "$atomicState $regularState" + } + + fun increment(): Int { + regularState++ + return regularState + } + + fun decrement(): Int { + regularState-- + return regularState + } + + fun atomicIncrement() = atomicState.addAndGet(1) + + fun atomicDecrement() = atomicState.addAndGet(-1) +} + +class FirstTest { + @Test + fun test_failing() { + LincheckStressConfiguration().apply { + iterations(300) + invocationsPerIteration(50) + actorsBefore(2) + threads(3) + actorsPerThread(2) + actorsAfter(2) + minimizeFailedScenario(false) + + initialState({ + TestClass() + }) + stateRepresentation { this.toString() } + + operation(TestClass::increment, "add") + operation({ this.decrement() }, "decrement") + operation(IntGen(""), BooleanGen(""), { i, b -> + //println("Operation with arguments $i and $b has called") + }, "do_nothing") + }.runTest() + } + + @Test + fun test_working() { + LincheckStressConfiguration().apply { + iterations(20) + invocationsPerIteration(50) + actorsBefore(2) + threads(3) + actorsPerThread(2) + actorsAfter(2) + minimizeFailedScenario(false) + + initialState({ + TestClass() + }) + stateRepresentation { this.toString() } + + operation(TestClass::atomicIncrement, "atomicIncrement") + operation(TestClass::atomicDecrement, "atomicDecrement") + }.runTest() + } + +/* @Test fun test() { val testClass = TestClass("FirstTest") { FirstTest() } val actorGenerator1 = ActorGenerator( - function = { - instance, arguments -> - (instance as FirstTest).state.a() + function = { instance, arguments -> + (instance as TestClass).state.a() }, parameterGenerators = listOf() ) val actorGenerator2 = ActorGenerator( - function = { - instance, arguments -> - (instance as FirstTest).state.b() + function = { instance, arguments -> + (instance as TestClass).state.b() }, parameterGenerators = listOf() ) @@ -66,8 +131,8 @@ class FirstTest: VerifierState() { ) val options = StressOptions().run { - iterations(300) - invocationsPerIteration(100) + iterations(1) + invocationsPerIteration(50000) actorsBefore(2) threads(3) actorsPerThread(2) @@ -77,26 +142,5 @@ class FirstTest: VerifierState() { LinChecker.check(testClass = testClass, testStructure = testStructure, options = options) } - - class A : SynchronizedObject() { - private val sharedState: AtomicInt = AtomicInt(0) - - fun a() = synchronized(this) { - sharedState.increment() - //printErr("a(), sharedState = ${sharedState.toString()}") - } - - fun b() = synchronized(this) { - sharedState.decrement() - //printErr("b(), sharedState = ${sharedState.toString()}") - } - - override fun equals(other: Any?): Boolean { - return this.sharedState.value == (other as A).sharedState.value - } - - override fun hashCode(): Int { - return this.sharedState.value - } - } +*/ } \ No newline at end of file From ac517e15f8191bc8f0916dbd67d326e3a036904b Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 31 Mar 2021 04:16:08 +0300 Subject: [PATCH 27/72] update gradle version to fix atomicFu problems, separate StressStrategy for jvm and native --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 178 +++++++++--------- .../strategy/stress/StressStrategy.kt | 72 +------ .../jetbrains/kotlinx/lincheck/LinChecker.kt | 4 +- .../strategy/stress/StressStrategy.kt | 77 ++++++++ .../strategy/stress/StressStrategy.kt | 95 ++++++++++ 6 files changed, 272 insertions(+), 156 deletions(-) create mode 100644 src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt create mode 100644 src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 12d38de6a..442d9132e 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-6.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew.bat b/gradlew.bat index 107acd32c..ac1b06f93 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 36c73612e..f87c5c1fe 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -27,69 +27,13 @@ import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* -class StressStrategy( - private val testCfg: StressCTestConfiguration, - private val testClass: TestClass, +expect class StressStrategy( + testCfg: StressCTestConfiguration, + testClass: TestClass, scenario: ExecutionScenario, - private val validationFunctions: List, - private val stateRepresentationFunction: StateRepresentationFunction?, - private val verifier: Verifier -) : Strategy(scenario) { - private val invocations = testCfg.invocationsPerIteration - private var runner: Runner - - init { - runner = ParallelThreadsRunner( - strategy = this, - testClass = testClass, - validationFunctions = validationFunctions, - stateRepresentationFunction = stateRepresentationFunction, - timeoutMs = testCfg.timeoutMs, - useClocks = UseClocks.RANDOM - ) - try { - runner.initialize() - } catch (t: Throwable) { - runner.close() - throw t - } - } - - fun reset() { - runner = ParallelThreadsRunner( - strategy = this, - testClass = testClass, - validationFunctions = validationFunctions, - stateRepresentationFunction = stateRepresentationFunction, - timeoutMs = testCfg.timeoutMs, - useClocks = UseClocks.RANDOM - ) - try { - runner.initialize() - } catch (t: Throwable) { - runner.close() - throw t - } - } - - override fun run(): LincheckFailure? { - // Run invocations - for (invocation in 0 until invocations) { - reset() - try { - runner.also { - when (val ir = runner.run()) { - is CompletedInvocationResult -> { - if (!verifier.verifyResults(scenario, ir.results)) - return IncorrectResultsFailure(scenario, ir.results) - } - else -> return ir.toLincheckFailure(scenario) - } - } - } finally { - runner.close() - } - } - return null - } + validationFunctions: List, + stateRepresentationFunction: StateRepresentationFunction?, + verifier: Verifier +) : Strategy { + override fun run(): LincheckFailure? } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 834ccb67b..4edb2f3f3 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -167,9 +167,9 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { } private val ExecutionScenario.hasSuspendableActorsInInitPart get() = - initExecution.stream().anyMatch(Actor::isSuspendable) + initExecution.any(Actor::isSuspendable) private val ExecutionScenario.hasPostPartAndSuspendableActors get() = - (parallelExecution.stream().anyMatch { actors -> actors.stream().anyMatch { it.isSuspendable } } && postExecution.size > 0) + (parallelExecution.any { actors -> actors.any { it.isSuspendable } } && postExecution.size > 0) private val ExecutionScenario.isParallelPartEmpty get() = parallelExecution.map { it.size }.sum() == 0 diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt new file mode 100644 index 000000000..4f72f5132 --- /dev/null +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -0,0 +1,77 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.strategy.stress + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.runner.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* + +actual class StressStrategy actual constructor( + private val testCfg: StressCTestConfiguration, + private val testClass: TestClass, + scenario: ExecutionScenario, + private val validationFunctions: List, + private val stateRepresentationFunction: StateRepresentationFunction?, + private val verifier: Verifier +) : Strategy(scenario) { + private val invocations = testCfg.invocationsPerIteration + private var runner: Runner + + init { + runner = ParallelThreadsRunner( + strategy = this, + testClass = testClass, + validationFunctions = validationFunctions, + stateRepresentationFunction = stateRepresentationFunction, + timeoutMs = testCfg.timeoutMs, + useClocks = UseClocks.RANDOM + ) + try { + runner.initialize() + } catch (t: Throwable) { + runner.close() + throw t + } + } + + actual override fun run(): LincheckFailure? { + try { + // Run invocations + for (invocation in 0 until invocations) { + runner.also { + when (val ir = runner.run()) { + is CompletedInvocationResult -> { + if (!verifier.verifyResults(scenario, ir.results)) + return IncorrectResultsFailure(scenario, ir.results) + } + else -> return ir.toLincheckFailure(scenario) + } + } + } + return null + } finally { + runner.close() + } + } +} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt new file mode 100644 index 000000000..10aad8fb4 --- /dev/null +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -0,0 +1,95 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package org.jetbrains.kotlinx.lincheck.strategy.stress + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.runner.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* + +actual class StressStrategy actual constructor( + private val testCfg: StressCTestConfiguration, + private val testClass: TestClass, + scenario: ExecutionScenario, + private val validationFunctions: List, + private val stateRepresentationFunction: StateRepresentationFunction?, + private val verifier: Verifier +) : Strategy(scenario) { + private val invocations = testCfg.invocationsPerIteration + private var runner: Runner + + init { + runner = ParallelThreadsRunner( + strategy = this, + testClass = testClass, + validationFunctions = validationFunctions, + stateRepresentationFunction = stateRepresentationFunction, + timeoutMs = testCfg.timeoutMs, + useClocks = UseClocks.RANDOM + ) + try { + runner.initialize() + } catch (t: Throwable) { + runner.close() + throw t + } + } + + fun reset() { + runner = ParallelThreadsRunner( + strategy = this, + testClass = testClass, + validationFunctions = validationFunctions, + stateRepresentationFunction = stateRepresentationFunction, + timeoutMs = testCfg.timeoutMs, + useClocks = UseClocks.RANDOM + ) + try { + runner.initialize() + } catch (t: Throwable) { + runner.close() + throw t + } + } + + actual override fun run(): LincheckFailure? { + // Run invocations + for (invocation in 0 until invocations) { + reset() + try { + runner.also { + when (val ir = runner.run()) { + is CompletedInvocationResult -> { + if (!verifier.verifyResults(scenario, ir.results)) + return IncorrectResultsFailure(scenario, ir.results) + } + else -> return ir.toLincheckFailure(scenario) + } + } + } finally { + runner.close() + } + } + return null + } +} \ No newline at end of file From 664e12ca2bcb0fa6bb37467970dea56e25402a05 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 13 Apr 2021 16:05:26 +0300 Subject: [PATCH 28/72] add some tests --- .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 2 - .../org/jetbrains/kotlinx/lincheck/Utils.kt | 2 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 36 +++++++-- .../jetbrains/kotlinx/lincheck/NativeUtils.kt | 8 +- .../kotlinx/lincheck/ValidationFunction.kt | 6 +- .../lincheck/execution/ActorGenerator.kt | 6 +- .../runner/FixedActiveThreadsExecutor.kt | 3 + src/native/test/AbstractLincheckTest.kt | 81 +++++++++++++++++++ src/native/test/FirstTest.kt | 27 +++---- src/native/test/ThreadIdTest.kt | 49 +++++++++++ src/native/test/ValidateFunctionTest.kt | 68 ++++++++++++++++ 11 files changed, 253 insertions(+), 35 deletions(-) create mode 100644 src/native/test/AbstractLincheckTest.kt create mode 100644 src/native/test/ThreadIdTest.kt create mode 100644 src/native/test/ValidateFunctionTest.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index d0b9d4e7b..b7b788f5a 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -29,8 +29,6 @@ import kotlin.jvm.* import kotlin.reflect.* expect class TestClass { - val name: String - fun createInstance(): Any } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 7eaec9361..6b04cf7d5 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -39,7 +39,7 @@ import kotlin.reflect.full.* import kotlin.reflect.jvm.* actual class TestClass(val clazz: Class<*>) { - actual val name = clazz.name + val name = clazz.name actual fun createInstance(): Any = clazz.getDeclaredConstructor().newInstance() } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index b57500e43..b82fe9685 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -66,17 +66,38 @@ class LincheckStressConfiguration : StressOptions() { LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) } + fun checkImpl() = LinChecker(getTestClass(), getTestStructure(), this as StressOptions).checkImpl() + // =========================== Constructor fun initialState( - state: () -> Instance, - className: String = "default className" + state: () -> Instance ) { - testClass = TestClass(className, state) + testClass = TestClass(state) } // =========================== Operation + fun operation( + pGens: List>, + op: Instance.(List) -> R, + name: String = op.toString(), + useOnce: Boolean = false, + isSuspendable: Boolean = false + ) { + val actorGenerator = ActorGenerator( + function = { instance, arguments -> + instance as Instance // check that operation can be applied to instance + instance.op(arguments) + }, + parameterGenerators = pGens, + functionName = name, + useOnce = useOnce, + isSuspendable = isSuspendable + ) + actorGenerators.add(actorGenerator) + } + fun operation( op: Instance.() -> R, name: String = op.toString(), @@ -140,12 +161,13 @@ class LincheckStressConfiguration : StressOptions() { // ============================= Validation Function fun validationFunction( - validate: Instance.() -> Unit + validate: Instance.() -> Unit, + name: String = validate.toString() ) { - validationFunctions.add(ValidationFunction { instance -> + validationFunctions.add(ValidationFunction({ instance -> instance as Instance // check that operation can be applied to instance instance.validate() - }) + }, name)) } // ============================= State Representation Function @@ -197,7 +219,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> - println("Iteration $i") + println("Iteration $i") // TODO debug output val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(i + 1, iterations, scenario) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt index 517661bcd..440dd25fd 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt @@ -28,7 +28,6 @@ import platform.posix.* import kotlin.coroutines.* actual class TestClass( - actual val name: String, val function: () -> Any? ) { actual fun createInstance(): Any = function() ?: throw IllegalArgumentException("Constructor should not return null") @@ -45,7 +44,12 @@ internal actual fun createLincheckResult(res: Any?, wasSuspended: Boolean): Resu } internal actual fun executeValidationFunction(instance: Any, validationFunction: ValidationFunction): Throwable? { - TODO("Not yet implemented") + try { + validationFunction.function(instance) + } catch (e: Throwable) { + return e + } + return null } /** diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt index 90de5f40b..d71ae275b 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/ValidationFunction.kt @@ -23,11 +23,11 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.execution.* actual class ValidationFunction( - val function: (Any) -> Unit + val function: (Any) -> Unit, + val functionName: String ) -actual val ValidationFunction.name: String - get() = TODO("Not yet implemented") +actual val ValidationFunction.name: String get() = this.functionName actual class StateRepresentationFunction( val function: (Any) -> String diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index 413522b1c..e29972a7e 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -30,12 +30,10 @@ actual class ActorGenerator( actual val useOnce: Boolean = false, actual val isSuspendable: Boolean = false ) { - actual override fun toString(): String { - TODO("Not yet implemented") - } + actual override fun toString(): String = functionName actual fun generate(threadId: Int): Actor { - val arguments = parameterGenerators.map { it.generate() } + val arguments = parameterGenerators.map { it.generate() }.map { if (it === THREAD_ID_TOKEN) threadId else it } return Actor( function = function, arguments = arguments, diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 72d4f004a..4331710c9 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -20,6 +20,9 @@ package org.jetbrains.kotlinx.lincheck.runner +import kotlin.native.concurrent.* +import kotlinx.cinterop.* +import platform.posix.* import kotlinx.coroutines.* import kotlin.native.ThreadLocal import kotlin.native.concurrent.* diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt new file mode 100644 index 000000000..2304f148e --- /dev/null +++ b/src/native/test/AbstractLincheckTest.kt @@ -0,0 +1,81 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.native.* +import kotlin.reflect.* +import kotlin.test.* + +abstract class AbstractLincheckStressTest( + private vararg val expectedFailures: KClass +) : VerifierState() { + open fun > T.customize() {} + override fun extractState(): Any = this.identityHashCode() + + private fun , I> T.runInternalTest() { + val failure: LincheckFailure? = checkImpl() + if (failure === null) { + assert(expectedFailures.isEmpty()) { + "This test should fail, but no error has been occurred (see the logs for details)" + } + } else { + failure.trace?.let { checkTraceHasNoLincheckEvents(it.toString()) } + assert(expectedFailures.contains(failure::class)) { + "This test has failed with an unexpected error: \n $failure" + } + } + } + + @Test + fun testWithStressStrategy(): Unit = LincheckStressConfiguration().run { + invocationsPerIteration(5_000) + commonConfiguration() + runInternalTest() + } +/* + @Test + fun testWithModelCheckingStrategy(): Unit = ModelCheckingOptions().run { + invocationsPerIteration(1_000) + commonConfiguration() + runInternalTest() + } +*/ + private fun > T.commonConfiguration(): Unit = run { + iterations(2) + actorsBefore(2) + threads(3) + actorsPerThread(2) + actorsAfter(2) + minimizeFailedScenario(false) + customize() + } +} + + +fun checkTraceHasNoLincheckEvents(trace: String) { + val testPackageOccurrences = trace.split("org.jetbrains.kotlinx.lincheck.test.").size - 1 + val lincheckPackageOccurrences = trace.split("org.jetbrains.kotlinx.lincheck.").size - 1 + check(testPackageOccurrences == lincheckPackageOccurrences) { "Internal Lincheck events were found in the trace" } +} \ No newline at end of file diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 8392d08fe..d9dce81ff 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -18,13 +18,8 @@ * */ -import kotlinx.atomicfu.locks.SynchronizedObject -import kotlinx.atomicfu.locks.synchronized -import kotlinx.cinterop.* -import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.paramgen.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.strategy.IncorrectResultsFailure import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.native.concurrent.* import kotlin.test.* @@ -59,7 +54,7 @@ class TestClass : VerifierState() { class FirstTest { @Test fun test_failing() { - LincheckStressConfiguration().apply { + val f = LincheckStressConfiguration().apply { iterations(300) invocationsPerIteration(50) actorsBefore(2) @@ -68,23 +63,25 @@ class FirstTest { actorsAfter(2) minimizeFailedScenario(false) - initialState({ - TestClass() - }) + initialState { TestClass() } stateRepresentation { this.toString() } operation(TestClass::increment, "add") operation({ this.decrement() }, "decrement") - operation(IntGen(""), BooleanGen(""), { i, b -> + operation(IntGen(""), BooleanGen(""), { _, _ -> //println("Operation with arguments $i and $b has called") }, "do_nothing") - }.runTest() + }.checkImpl() + + assert(f != null && f is IncorrectResultsFailure) { + "This test should fail with a incorrect results error" + } } @Test fun test_working() { LincheckStressConfiguration().apply { - iterations(20) + iterations(5) invocationsPerIteration(50) actorsBefore(2) threads(3) @@ -92,9 +89,7 @@ class FirstTest { actorsAfter(2) minimizeFailedScenario(false) - initialState({ - TestClass() - }) + initialState { TestClass() } stateRepresentation { this.toString() } operation(TestClass::atomicIncrement, "atomicIncrement") diff --git a/src/native/test/ThreadIdTest.kt b/src/native/test/ThreadIdTest.kt new file mode 100644 index 000000000..807b0f364 --- /dev/null +++ b/src/native/test/ThreadIdTest.kt @@ -0,0 +1,49 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import kotlinx.atomicfu.* +import org.jetbrains.kotlinx.lincheck.paramgen.* + +class ThreadIdTest : AbstractLincheckStressTest() { + private val balances = IntArray(5) + private val counter = atomic(0) + + fun inc(threadId: Any): Int = counter.incrementAndGet() + .also { /*println("inc $threadId");*/ balances[threadId as Int]++ } + + fun decIfNotNegative(threadId: Any) { + //println("decIfNotNegative $threadId"); + if (balances[threadId as Int] == 0) return + balances[threadId]-- + val c = counter.decrementAndGet() + if (c < 0) error("The counter cannot be negative") + } + + override fun extractState() = balances.toList() to counter.value + + override fun > T.customize() { + initialState { ThreadIdTest() } + + operation(ThreadIdGen(""), ThreadIdTest::inc) + operation(ThreadIdGen(""), ThreadIdTest::decIfNotNegative) + } +} diff --git a/src/native/test/ValidateFunctionTest.kt b/src/native/test/ValidateFunctionTest.kt new file mode 100644 index 000000000..010143213 --- /dev/null +++ b/src/native/test/ValidateFunctionTest.kt @@ -0,0 +1,68 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.native.concurrent.* +import kotlin.test.* + +class ValidateFunctionTest : VerifierState() { + val c = AtomicInt(0) + + fun inc() = c.addAndGet(1) + + override fun extractState() = c.value + + fun validateNoError() {} + + var validateInvoked: Int = 0 + + // This function fails on the 5ht invocation + fun validateWithError() { + validateInvoked++ + if (validateInvoked == 5) error("Validation works!") + } + + @Test + fun test() { + val f = LincheckStressConfiguration().apply { + iterations(1) + invocationsPerIteration(1) + actorsBefore(3) + actorsAfter(10) + + initialState { ValidateFunctionTest() } + operation(ValidateFunctionTest::inc) + validationFunction(ValidateFunctionTest::validateNoError, "validateNoError") + validationFunction(ValidateFunctionTest::validateWithError, "validateWithError") + }.checkImpl() + assert(f != null && f is ValidationFailure && f.functionName == "validateWithError") { + "This test should fail with a validation error" + } + val validationInvocations = f!!.scenario.initExecution.size + f.scenario.postExecution.size + 1 + assert(validationInvocations == 5) { + "The scenario should have exactly 5 points to invoke validation functions, " + + "see the resulting scenario below: \n${f.scenario}" + } + } + +} From 43e8c0fe64e8f528cd080dfe5d6887e30368155e Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 13 Apr 2021 16:21:24 +0300 Subject: [PATCH 29/72] fix some tests --- gradle/native-targets.gradle | 1 - .../kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt | 3 +++ src/native/test/AbstractLincheckTest.kt | 5 ++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gradle/native-targets.gradle b/gradle/native-targets.gradle index 6ecef92b8..560d1d38a 100644 --- a/gradle/native-targets.gradle +++ b/gradle/native-targets.gradle @@ -94,7 +94,6 @@ kotlin { if (macosEnabled) ideaPreset = presets.macosX64 if (winEnabled) ideaPreset = presets.mingwX64 project.ext.ideaPreset = ideaPreset - project.ext.ideaPreset = presets.linuxX64 } targets.metaClass.addTarget = { preset -> diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 4331710c9..8813d5ae9 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -51,6 +51,9 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner //val res = runnableFuture?.result val result = runnableFuture?.result results.add(result) // to prevent from garbage collecting + /*worker.execute(TransferMode.UNSAFE, { }, { + sleep(1000000) + })*/ //worker.requestTermination(true).result //return res //printErr("stop() $iThread finished") diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index 2304f148e..82621c6d5 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -20,9 +20,7 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.strategy.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.native.* import kotlin.reflect.* @@ -63,7 +61,8 @@ abstract class AbstractLincheckStressTest( } */ private fun > T.commonConfiguration(): Unit = run { - iterations(2) + iterations(5) + invocationsPerIteration(100) actorsBefore(2) threads(3) actorsPerThread(2) From 0bdfbd705c4869dfa876004d644c67e57d020de0 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 13 Apr 2021 16:45:42 +0300 Subject: [PATCH 30/72] update performance --- .../main/org/jetbrains/kotlinx/lincheck/LinChecker.kt | 2 +- .../lincheck/runner/FixedActiveThreadsExecutor.kt | 10 ++++------ .../kotlinx/lincheck/strategy/stress/StressStrategy.kt | 10 +++++----- src/native/test/FirstTest.kt | 4 ++-- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index b82fe9685..03b035a93 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -219,7 +219,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> - println("Iteration $i") // TODO debug output + printErr("Iteration $i") // TODO debug output val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(i + 1, iterations, scenario) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 8813d5ae9..cd70de485 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -46,14 +46,12 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner } actual fun terminate() { - runnableFuture!!.state.value //printErr("stop() $iThread called") //val res = runnableFuture?.result - val result = runnableFuture?.result - results.add(result) // to prevent from garbage collecting - /*worker.execute(TransferMode.UNSAFE, { }, { - sleep(1000000) - })*/ + //println("terminate $iThread start") + val result = runnableFuture!!.result + //worker.execute(TransferMode.UNSAFE, { }, { sleep(1000000000) }) + //println("terminate $iThread end") //worker.requestTermination(true).result //return res //printErr("stop() $iThread finished") diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 10aad8fb4..88995f00d 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -74,9 +74,9 @@ actual class StressStrategy actual constructor( actual override fun run(): LincheckFailure? { // Run invocations - for (invocation in 0 until invocations) { - reset() - try { + // reset() + try { + for (invocation in 0 until invocations) { runner.also { when (val ir = runner.run()) { is CompletedInvocationResult -> { @@ -86,9 +86,9 @@ actual class StressStrategy actual constructor( else -> return ir.toLincheckFailure(scenario) } } - } finally { - runner.close() } + } finally { + runner.close() } return null } diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index d9dce81ff..889cfeb08 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -81,8 +81,8 @@ class FirstTest { @Test fun test_working() { LincheckStressConfiguration().apply { - iterations(5) - invocationsPerIteration(50) + iterations(50) + invocationsPerIteration(5000) actorsBefore(2) threads(3) actorsPerThread(2) From 9eec6e4d28599980e66671be072b8b43e9dec7e4 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 13 Apr 2021 16:55:37 +0300 Subject: [PATCH 31/72] reset runner on every invocation --- .../lincheck/runner/FixedActiveThreadsExecutor.kt | 2 +- .../kotlinx/lincheck/strategy/stress/StressStrategy.kt | 10 +++++----- src/native/test/FirstTest.kt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index cd70de485..cf43107f7 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -50,7 +50,7 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner //val res = runnableFuture?.result //println("terminate $iThread start") val result = runnableFuture!!.result - //worker.execute(TransferMode.UNSAFE, { }, { sleep(1000000000) }) + worker.execute(TransferMode.UNSAFE, { }, { sleep(1000000000) }) //println("terminate $iThread end") //worker.requestTermination(true).result //return res diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 88995f00d..7df037708 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -74,9 +74,9 @@ actual class StressStrategy actual constructor( actual override fun run(): LincheckFailure? { // Run invocations - // reset() - try { - for (invocation in 0 until invocations) { + for (invocation in 0 until invocations) { + try { + reset() runner.also { when (val ir = runner.run()) { is CompletedInvocationResult -> { @@ -86,9 +86,9 @@ actual class StressStrategy actual constructor( else -> return ir.toLincheckFailure(scenario) } } + } finally { + runner.close() } - } finally { - runner.close() } return null } diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 889cfeb08..3c575df13 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -82,7 +82,7 @@ class FirstTest { fun test_working() { LincheckStressConfiguration().apply { iterations(50) - invocationsPerIteration(5000) + invocationsPerIteration(500) actorsBefore(2) threads(3) actorsPerThread(2) From f4a7020ee8e8650ae296f01defb487e2cb68e1f5 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Tue, 13 Apr 2021 17:20:26 +0300 Subject: [PATCH 32/72] stable state with bug --- .../kotlinx/lincheck/strategy/stress/StressStrategy.kt | 10 +++++----- src/native/test/AbstractLincheckTest.kt | 4 ++-- src/native/test/FirstTest.kt | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 7df037708..88995f00d 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -74,9 +74,9 @@ actual class StressStrategy actual constructor( actual override fun run(): LincheckFailure? { // Run invocations - for (invocation in 0 until invocations) { - try { - reset() + // reset() + try { + for (invocation in 0 until invocations) { runner.also { when (val ir = runner.run()) { is CompletedInvocationResult -> { @@ -86,9 +86,9 @@ actual class StressStrategy actual constructor( else -> return ir.toLincheckFailure(scenario) } } - } finally { - runner.close() } + } finally { + runner.close() } return null } diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index 82621c6d5..b14c18422 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -61,8 +61,8 @@ abstract class AbstractLincheckStressTest( } */ private fun > T.commonConfiguration(): Unit = run { - iterations(5) - invocationsPerIteration(100) + iterations(200) + invocationsPerIteration(1000) actorsBefore(2) threads(3) actorsPerThread(2) diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 3c575df13..fc9cebe3b 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -81,11 +81,11 @@ class FirstTest { @Test fun test_working() { LincheckStressConfiguration().apply { - iterations(50) - invocationsPerIteration(500) + iterations(20) + invocationsPerIteration(1000) actorsBefore(2) threads(3) - actorsPerThread(2) + actorsPerThread(5) actorsAfter(2) minimizeFailedScenario(false) From d99a3555778927acc3b5d952f9b559e13f3d211b Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 14 Apr 2021 17:54:21 +0300 Subject: [PATCH 33/72] Add more tests. Add exception handling. Improve code structure --- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 2 + .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 5 ++ .../lincheck/execution/ActorGenerator.kt | 3 + .../org/jetbrains/kotlinx/lincheck/Actor.kt | 3 +- .../kotlinx/lincheck/CTestStructure.kt | 3 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 6 +- .../lincheck/execution/ActorGenerator.kt | 3 +- .../runner/TestThreadExecutionGenerator.java | 6 +- .../lincheck/test/ExceptionAsResultTest.java | 1 + .../runner/TestThreadExecutionHelperTest.java | 5 +- .../test/verifier/CustomScenarioDSL.kt | 2 +- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 5 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 38 +++++++---- .../jetbrains/kotlinx/lincheck/NativeUtils.kt | 12 +++- .../lincheck/execution/ActorGenerator.kt | 7 +- .../runner/FixedActiveThreadsExecutor.kt | 8 ++- .../lincheck/runner/TestThreadExecution.kt | 18 ++++- src/native/test/AbstractLincheckTest.kt | 3 +- src/native/test/ExceptionAsResultTest.kt | 58 ++++++++++++++++ src/native/test/FirstTest.kt | 3 +- .../test/HangingTest.kt} | 26 ++++++-- src/native/test/RunOnceTest.kt | 66 +++++++++++++++++++ src/native/test/ThreadIdTest.kt | 1 + src/native/test/UnexpectedExceptionTest.kt | 46 +++++++++++++ src/native/test/ValidateFunctionTest.kt | 1 + 25 files changed, 291 insertions(+), 40 deletions(-) create mode 100644 src/native/test/ExceptionAsResultTest.kt rename src/{jvm/main/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt => native/test/HangingTest.kt} (57%) create mode 100644 src/native/test/RunOnceTest.kt create mode 100644 src/native/test/UnexpectedExceptionTest.kt diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 7b565f0b1..9764b91e7 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -21,6 +21,7 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.verifier.quiescent.* +import kotlin.reflect.KClass /** * The actor entity describe the operation with its parameters @@ -33,6 +34,7 @@ expect class Actor { val allowExtraSuspension: Boolean val promptCancellation: Boolean val cancelOnSuspension: Boolean + val handledExceptions: List> override fun toString(): String } diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index b7b788f5a..ab3df8d22 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -24,6 +24,7 @@ package org.jetbrains.kotlinx.lincheck import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* +import org.jetbrains.kotlinx.lincheck.strategy.* import kotlin.coroutines.* import kotlin.jvm.* import kotlin.reflect.* @@ -88,6 +89,10 @@ internal class StoreExceptionHandler : AbstractCoroutineContextElement(Coroutine } } +class LincheckAssertionError( + failure: LincheckFailure +) : AssertionError("\n" + failure) + @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") internal fun CancellableContinuation.cancelByLincheck(promptCancellation: Boolean): CancellationResult { val exceptionHandler = context[CoroutineExceptionHandler] as StoreExceptionHandler diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index 9827b524e..4f6842de7 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -2,11 +2,14 @@ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* import kotlin.random.* +import kotlin.reflect.KClass expect class ActorGenerator { override fun toString(): String val isSuspendable: Boolean val useOnce: Boolean + val handledExceptions: List> + fun generate(threadId: Int): Actor } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 107c6579f..8c2de7337 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -24,6 +24,7 @@ package org.jetbrains.kotlinx.lincheck import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.verifier.quiescent.* import java.lang.reflect.* +import kotlin.reflect.KClass import kotlin.reflect.jvm.kotlinFunction /** @@ -35,7 +36,7 @@ import kotlin.reflect.jvm.kotlinFunction actual data class Actor @JvmOverloads constructor( val method: Method, val arguments: List, - val handledExceptions: List> = emptyList(), + actual val handledExceptions: List> = emptyList(), actual val cancelOnSuspension: Boolean = false, actual val allowExtraSuspension: Boolean = false, val blocking: Boolean = false, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt index f674bcac6..84387dcb2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestStructure.kt @@ -30,6 +30,7 @@ import java.lang.reflect.Parameter import java.util.* import java.util.stream.Collectors import kotlin.collections.HashMap +import kotlin.reflect.KClass actual typealias ValidationFunction = Method @@ -110,7 +111,7 @@ actual class CTestStructure private constructor( gens.add(getOrCreateGenerator(m, m.parameters[i], nameInOperation, namedGens, defaultGens)) } // Get list of handled exceptions if they are presented - val handledExceptions: List> = opAnn.handleExceptionsAsResult.map { it.java } + val handledExceptions: List> = opAnn.handleExceptionsAsResult.toList() val actorGenerator = ActorGenerator(m, gens, handledExceptions, opAnn.runOnce, opAnn.cancellableOnSuspension, opAnn.allowExtraSuspension, opAnn.blocking, opAnn.causesBlocking, opAnn.promptCancellation) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 6b04cf7d5..3a2a776cb 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -35,6 +35,7 @@ import java.lang.reflect.Method import java.util.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* +import kotlin.reflect.KClass import kotlin.reflect.full.* import kotlin.reflect.jvm.* @@ -68,7 +69,7 @@ internal actual fun executeActor( } catch (invE: Throwable) { val eClass = (invE.cause ?: invE).javaClass.normalize() for (ec in actor.handledExceptions) { - if (ec.isAssignableFrom(eClass)) + if (ec.java.isAssignableFrom(eClass)) return createExceptionResult(eClass) } throw IllegalStateException("Invalid exception as a result of $actor", invE) @@ -220,6 +221,9 @@ internal fun ByteArray.deserialize(loader: ClassLoader) = ByteArrayInputStream(t CustomObjectInputStream(loader, it).run { readObject() } } +internal fun getClassFromKClass(clazz: KClass) = clazz.java +internal fun getKClassFromClass(clazz: Class) = clazz.kotlin + /** * ObjectInputStream that uses custom class loader. */ diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index 76ce97019..7c54703c3 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -22,6 +22,7 @@ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.paramgen.* import java.lang.reflect.* +import kotlin.reflect.KClass /** * Implementations of this class generate [actors][Actor] @@ -30,7 +31,7 @@ import java.lang.reflect.* actual class ActorGenerator( private val method: Method, private val parameterGenerators: List>, - private val handledExceptions: List>, + actual val handledExceptions: List>, actual val useOnce: Boolean, cancellableOnSuspension: Boolean, private val allowExtraSuspension: Boolean, diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java index fcc18d6bd..db1da0c0b 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java @@ -23,6 +23,7 @@ */ import kotlin.coroutines.Continuation; +import kotlin.reflect.KClass; import org.jetbrains.kotlinx.lincheck.*; import org.jetbrains.kotlinx.lincheck.runner.ParallelThreadsRunner.*; import org.objectweb.asm.*; @@ -35,6 +36,7 @@ import java.util.ArrayList; import java.util.List; +import static org.jetbrains.kotlinx.lincheck.UtilsKt.getClassFromKClass; import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.Type.*; @@ -191,8 +193,8 @@ private static void generateRun(ClassVisitor cv, Type testType, int iThread, Lis Label actorCatchBlockEnd = mv.newLabel(); if (actor.getHandlesExceptions()) { handledExceptionHandler = mv.newLabel(); - for (Class ec : actor.getHandledExceptions()) - mv.visitTryCatchBlock(actorCatchBlockStart, actorCatchBlockEnd, handledExceptionHandler, getType(ec).getInternalName()); + for (KClass ec : actor.getHandledExceptions()) + mv.visitTryCatchBlock(actorCatchBlockStart, actorCatchBlockEnd, handledExceptionHandler, getType(getClassFromKClass(ec)).getInternalName()); } // Catch those exceptions that has not been caught yet Label unexpectedExceptionHandler = mv.newLabel(); diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java index 1d85ba724..d0bb56d6b 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java @@ -54,6 +54,7 @@ public void test() { fail("Should fail with AssertionError"); } catch (AssertionError e) { String m = e.getMessage(); + assertTrue(m.contains("Invalid execution results")); assertTrue(m.contains("IllegalStateException") || m.contains("IOException")); assertFalse(m.contains(MESSAGE)); } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java index 5bca061e2..6c5e46d8c 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java @@ -32,6 +32,7 @@ import static java.util.Arrays.*; import static java.util.Collections.*; +import static org.jetbrains.kotlinx.lincheck.UtilsKt.getKClassFromClass; public class TestThreadExecutionHelperTest { private Runner runner; @@ -96,8 +97,8 @@ public void testActorExceptionHandling() throws Exception { asList( new Actor(ArrayDeque.class.getMethod("addLast", Object.class), asList(1)), new Actor(Queue.class.getMethod("remove"), emptyList()), - new Actor(Queue.class.getMethod("remove"), emptyList(), asList(NoSuchElementException.class)), - new Actor(Queue.class.getMethod("remove"), emptyList(), asList(Exception.class, (NoSuchElementException.class))) + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(getKClassFromClass(NoSuchElementException.class))), + new Actor(Queue.class.getMethod("remove"), emptyList(), asList(getKClassFromClass(Exception.class), getKClassFromClass(NoSuchElementException.class))) ), emptyList(), false); ex.testInstance = new ArrayDeque<>(); ex.results = new Result[4]; diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt index 33e7cc857..f9e73a81d 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt @@ -74,7 +74,7 @@ fun actor(function: KFunction<*>, vararg args: Any?, cancelOnSuspension: Boolean return Actor( method = method, arguments = args.toList(), - handledExceptions = (method.exceptionTypes as Array>).toList(), + handledExceptions = (method.exceptionTypes as Array>).toList().map { it.kotlin }, cancelOnSuspension = cancelOnSuspension ) } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 5eb0f2bac..7d22264dd 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -20,6 +20,8 @@ package org.jetbrains.kotlinx.lincheck +import kotlin.reflect.KClass + /** * The actor entity describe the operation with its parameters * which is executed during the testing. @@ -33,7 +35,8 @@ actual class Actor( actual val isSuspendable: Boolean = false, actual val allowExtraSuspension: Boolean = false, actual val promptCancellation: Boolean = false, - actual val cancelOnSuspension: Boolean = false + actual val cancelOnSuspension: Boolean = false, + actual val handledExceptions: List> ) { actual override fun toString(): String = diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 03b035a93..b452f44b2 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -1,12 +1,3 @@ -import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.execution.* -import org.jetbrains.kotlinx.lincheck.paramgen.* -import org.jetbrains.kotlinx.lincheck.strategy.* -import org.jetbrains.kotlinx.lincheck.strategy.stress.* -import org.jetbrains.kotlinx.lincheck.verifier.* -import kotlin.jvm.* -import kotlin.reflect.* - /* * Lincheck * @@ -27,6 +18,17 @@ import kotlin.reflect.* * */ +package org.jetbrains.kotlinx.lincheck + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.jvm.* +import kotlin.reflect.* + class LincheckStressConfiguration : StressOptions() { /* invocationsPerIteration @@ -82,6 +84,7 @@ class LincheckStressConfiguration : StressOptions() { pGens: List>, op: Instance.(List) -> R, name: String = op.toString(), + handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false ) { @@ -93,7 +96,8 @@ class LincheckStressConfiguration : StressOptions() { parameterGenerators = pGens, functionName = name, useOnce = useOnce, - isSuspendable = isSuspendable + isSuspendable = isSuspendable, + handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) } @@ -101,6 +105,7 @@ class LincheckStressConfiguration : StressOptions() { fun operation( op: Instance.() -> R, name: String = op.toString(), + handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false ) { @@ -112,7 +117,8 @@ class LincheckStressConfiguration : StressOptions() { parameterGenerators = listOf(), functionName = name, useOnce = useOnce, - isSuspendable = isSuspendable + isSuspendable = isSuspendable, + handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) } @@ -121,6 +127,7 @@ class LincheckStressConfiguration : StressOptions() { p1Gen: ParameterGenerator, op: Instance.(p1: P1) -> R, name: String = op.toString(), + handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false ) { @@ -132,7 +139,8 @@ class LincheckStressConfiguration : StressOptions() { parameterGenerators = listOf(p1Gen), functionName = name, useOnce = useOnce, - isSuspendable = isSuspendable + isSuspendable = isSuspendable, + handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) } @@ -142,6 +150,7 @@ class LincheckStressConfiguration : StressOptions() { p2Gen: ParameterGenerator, op: Instance.(p1: P1, p2: P2) -> R, name: String = op.toString(), + handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false ) { @@ -153,7 +162,8 @@ class LincheckStressConfiguration : StressOptions() { parameterGenerators = listOf(p1Gen, p2Gen), functionName = name, useOnce = useOnce, - isSuspendable = isSuspendable + isSuspendable = isSuspendable, + handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) } @@ -200,7 +210,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT */ fun check() { val failure = checkImpl() ?: return - throw RuntimeException("testing data structure is incorrect $failure") // TODO rewrite throw LincheckAssertionError(failure) + throw LincheckAssertionError(failure) } /** diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt index 440dd25fd..b309b33b3 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* import platform.posix.* import kotlin.coroutines.* +import kotlin.reflect.* actual class TestClass( val function: () -> Any? @@ -71,7 +72,16 @@ internal actual fun executeActor( actor: Actor, completion: Continuation? ): Result { - return ValueResult(actor.function(instance, actor.arguments)) + return try { + val r = actor.function(instance, actor.arguments) + ValueResult(r) + } catch (e: Throwable) { + if (actor.handledExceptions.any { it.safeCast(e) != null }) { // Do a cast. If == null, cast failed and e is not an instance of it + ExceptionResult(e::class, false) + } else { + throw e + } + } } /** diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt index e29972a7e..998f84852 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/execution/ActorGenerator.kt @@ -22,13 +22,15 @@ package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.paramgen.* +import kotlin.reflect.KClass actual class ActorGenerator( val function: (Any, List) -> Any?, // (instance, arguments) -> result val parameterGenerators: List>, val functionName: String = function.toString(), actual val useOnce: Boolean = false, - actual val isSuspendable: Boolean = false + actual val isSuspendable: Boolean = false, + actual val handledExceptions: List> ) { actual override fun toString(): String = functionName @@ -38,7 +40,8 @@ actual class ActorGenerator( function = function, arguments = arguments, functionName = functionName, - isSuspendable = isSuspendable + isSuspendable = isSuspendable, + handledExceptions = handledExceptions ) } } \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index cf43107f7..e2c80b62d 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -46,13 +46,15 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner } actual fun terminate() { + // Don't want to terminate threads because of GC. Don't want to wait for result because of hanging + //printErr("stop() $iThread called") //val res = runnableFuture?.result //println("terminate $iThread start") - val result = runnableFuture!!.result - worker.execute(TransferMode.UNSAFE, { }, { sleep(1000000000) }) + //val result = runnableFuture!!.result + //worker.execute(TransferMode.UNSAFE, { }, { sleep(1000000000) }) //println("terminate $iThread end") - //worker.requestTermination(true).result + //worker.requestTermination(false).result //return res //printErr("stop() $iThread finished") } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt index 041f28ebf..982fbe0de 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt @@ -22,6 +22,7 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* +import kotlin.reflect.* class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List) : Runnable { lateinit var testInstance: Any @@ -54,8 +55,21 @@ class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List // TODO add try-catch runner.onActorStart(iThread) // Load arguments for operation - val result = actor.function(testInstance, actor.arguments) - results[index] = ValueResult(result) + val result: Result = try { + val r = actor.function(testInstance, actor.arguments) + //printErr("ValueResult") + ValueResult(r) + } catch (e: Throwable) { + if (actor.handledExceptions.any { it.safeCast(e) != null }) { // Do a cast. If == null, cast failed and e is not an instance of it + //printErr("ExceptionResult") + ExceptionResult(e::class, false) + } else { + //printErr("FailureResult with $e") + runner.onFailure(iThread, e) + throw e + } + } + results[index] = result incClock() } //printErr("RUN $iThread #finish ") diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index b14c18422..6e8405e7a 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -20,6 +20,7 @@ * #L% */ +import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.native.* @@ -61,7 +62,7 @@ abstract class AbstractLincheckStressTest( } */ private fun > T.commonConfiguration(): Unit = run { - iterations(200) + iterations(10) invocationsPerIteration(1000) actorsBefore(2) threads(3) diff --git a/src/native/test/ExceptionAsResultTest.kt b/src/native/test/ExceptionAsResultTest.kt new file mode 100644 index 000000000..e1313d6f0 --- /dev/null +++ b/src/native/test/ExceptionAsResultTest.kt @@ -0,0 +1,58 @@ +import org.jetbrains.kotlinx.lincheck.* +import kotlin.test.* + +/* +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ + +class ExceptionAsResultTest { + fun npeIsOk() { + (null as String?)!![0] + } + + fun subclassExceptionIsOk() { + if ((0..1).random() == 1) throw IndexOutOfBoundsException(MESSAGE) else throw IllegalStateException(MESSAGE) + } + + @Test + fun test() { + try { + LincheckStressConfiguration().apply { + iterations(1) + requireStateEquivalenceImplCheck(false) + + initialState { ExceptionAsResultTest() } + + operation(ExceptionAsResultTest::npeIsOk, "npeIsOk", listOf(NullPointerException::class)) + operation(ExceptionAsResultTest::subclassExceptionIsOk, "subclassExceptionIsOk", listOf(Throwable::class)) + }.runTest() + fail("Should fail with AssertionError") + } catch (e: AssertionError) { + val m = e.message + assertTrue(m!!.contains("IllegalStateException") || m.contains("IndexOutOfBoundsException")) + assertFalse(m.contains(MESSAGE)) + } + } + + companion object { + private const val MESSAGE = "iujdhfgilurtybfu" + } +} \ No newline at end of file diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index fc9cebe3b..937b29429 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -18,6 +18,7 @@ * */ +import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.IncorrectResultsFailure import org.jetbrains.kotlinx.lincheck.verifier.* @@ -81,7 +82,7 @@ class FirstTest { @Test fun test_working() { LincheckStressConfiguration().apply { - iterations(20) + iterations(10) invocationsPerIteration(1000) actorsBefore(2) threads(3) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt b/src/native/test/HangingTest.kt similarity index 57% rename from src/jvm/main/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt rename to src/native/test/HangingTest.kt index 08af2b5be..70d6f046d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LincheckAssertionError.kt +++ b/src/native/test/HangingTest.kt @@ -2,7 +2,7 @@ * #%L * Lincheck * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * Copyright (C) 2019 JetBrains s.r.o. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as @@ -19,11 +19,25 @@ * . * #L% */ -package org.jetbrains.kotlinx.lincheck +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.strategy.* -import java.lang.AssertionError -class LincheckAssertionError( - failure: LincheckFailure -) : AssertionError("\n" + failure) \ No newline at end of file +class HangingTest : AbstractLincheckStressTest(DeadlockWithDumpFailure::class) { + fun badOperation() { + while (true) {} + } + + override fun > T.customize() { + iterations(1) + actorsBefore(0) + actorsAfter(0) + requireStateEquivalenceImplCheck(false) + minimizeFailedScenario(false) + invocationTimeout(100) + + initialState { HangingTest() } + + operation(HangingTest::badOperation) + } +} diff --git a/src/native/test/RunOnceTest.kt b/src/native/test/RunOnceTest.kt new file mode 100644 index 000000000..bf98552c3 --- /dev/null +++ b/src/native/test/RunOnceTest.kt @@ -0,0 +1,66 @@ +/* +* #%L +* Lincheck +* %% +* Copyright (C) 2015 - 2018 Devexperts, LLC +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ + +import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration +import kotlinx.atomicfu.locks.* +import kotlin.test.Test + +class RunOnceTest { + private val state: A = A() + + fun a() { + state.a() + } + + fun b() { + state.b() + } + + @Test + fun test() { + LincheckStressConfiguration().apply { + threads(3) + iterations(10) + invocationsPerIteration(10) + requireStateEquivalenceImplCheck(false) + + initialState { RunOnceTest() } + operation(RunOnceTest::a, useOnce = true) + operation(RunOnceTest::b, useOnce = true) + }.runTest() + } + + internal inner class A : SynchronizedObject() { + private var a = false + private var b = false + + fun a() = synchronized(this) { + if (a) throw AssertionError() + a = true + } + + fun b() = synchronized(this) { + if (b) throw AssertionError() + b = true + } + } +} \ No newline at end of file diff --git a/src/native/test/ThreadIdTest.kt b/src/native/test/ThreadIdTest.kt index 807b0f364..6c0f82c02 100644 --- a/src/native/test/ThreadIdTest.kt +++ b/src/native/test/ThreadIdTest.kt @@ -21,6 +21,7 @@ */ import kotlinx.atomicfu.* +import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration import org.jetbrains.kotlinx.lincheck.paramgen.* class ThreadIdTest : AbstractLincheckStressTest() { diff --git a/src/native/test/UnexpectedExceptionTest.kt b/src/native/test/UnexpectedExceptionTest.kt new file mode 100644 index 000000000..b213271cf --- /dev/null +++ b/src/native/test/UnexpectedExceptionTest.kt @@ -0,0 +1,46 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration +import org.jetbrains.kotlinx.lincheck.strategy.* + +class UnexpectedExceptionTest : AbstractLincheckStressTest(UnexpectedExceptionFailure::class) { + private var canEnterForbiddenSection = false + + fun operation1() { + canEnterForbiddenSection = true + canEnterForbiddenSection = false + } + + fun operation2() { + check(!canEnterForbiddenSection) + } + + override fun extractState(): Any = canEnterForbiddenSection + + override fun > T.customize() { + initialState { UnexpectedExceptionTest() } + + operation(UnexpectedExceptionTest::operation1) + operation(UnexpectedExceptionTest::operation2, "operation2", listOf(IllegalArgumentException::class)) + } +} \ No newline at end of file diff --git a/src/native/test/ValidateFunctionTest.kt b/src/native/test/ValidateFunctionTest.kt index 010143213..ae5e1d7f9 100644 --- a/src/native/test/ValidateFunctionTest.kt +++ b/src/native/test/ValidateFunctionTest.kt @@ -20,6 +20,7 @@ * #L% */ +import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.native.concurrent.* From 7b52e46e49996659442129017ebe92f6bcb9dbd9 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 14 Apr 2021 21:33:00 +0300 Subject: [PATCH 34/72] Add 2 simple tests --- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 15 ++--- ...cenarioGenerationParameterDiversityTest.kt | 60 +++++++++++++++++++ .../test/StateEquivalenceImplCheckTest.kt | 45 ++++++++++++++ 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 src/native/test/ScenarioGenerationParameterDiversityTest.kt create mode 100644 src/native/test/StateEquivalenceImplCheckTest.kt diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index b452f44b2..52f1ed42c 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.jvm.* import kotlin.reflect.* +// TODO StressOptions methods cast this class to StressOptions class LincheckStressConfiguration : StressOptions() { /* invocationsPerIteration @@ -74,7 +75,7 @@ class LincheckStressConfiguration : StressOptions() { fun initialState( state: () -> Instance - ) { + ) = apply { testClass = TestClass(state) } @@ -87,7 +88,7 @@ class LincheckStressConfiguration : StressOptions() { handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false - ) { + ) = apply { val actorGenerator = ActorGenerator( function = { instance, arguments -> instance as Instance // check that operation can be applied to instance @@ -108,7 +109,7 @@ class LincheckStressConfiguration : StressOptions() { handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false - ) { + ) = apply { val actorGenerator = ActorGenerator( function = { instance, arguments -> instance as Instance // check that operation can be applied to instance @@ -130,7 +131,7 @@ class LincheckStressConfiguration : StressOptions() { handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false - ) { + ) = apply { val actorGenerator = ActorGenerator( function = { instance, arguments -> instance as Instance // check that operation can be applied to instance @@ -153,7 +154,7 @@ class LincheckStressConfiguration : StressOptions() { handleExceptionsAsResult: List> = emptyList(), useOnce: Boolean = false, isSuspendable: Boolean = false - ) { + ) = apply { val actorGenerator = ActorGenerator( function = { instance, arguments -> instance as Instance // check that operation can be applied to instance @@ -173,7 +174,7 @@ class LincheckStressConfiguration : StressOptions() { fun validationFunction( validate: Instance.() -> Unit, name: String = validate.toString() - ) { + ) = apply { validationFunctions.add(ValidationFunction({ instance -> instance as Instance // check that operation can be applied to instance instance.validate() @@ -184,7 +185,7 @@ class LincheckStressConfiguration : StressOptions() { fun stateRepresentation( state: Instance.() -> String - ) { + ) = apply { stateRepresentationFunction = StateRepresentationFunction { instance -> instance as Instance // check that operation can be applied to instance instance.state() diff --git a/src/native/test/ScenarioGenerationParameterDiversityTest.kt b/src/native/test/ScenarioGenerationParameterDiversityTest.kt new file mode 100644 index 000000000..34f4a736e --- /dev/null +++ b/src/native/test/ScenarioGenerationParameterDiversityTest.kt @@ -0,0 +1,60 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.test.* + +/** + * This test checks that parameters in generated scenarios are diversified + */ +class ScenarioGenerationParameterDiversityTest : VerifierState() { + fun foo(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int) { + check(setOf(a, b, c, d, e, f).size > 1) { "At least 2 parameters should be different w.h.p." } + } + + fun bar(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int) { + check(setOf(a, b, c, d, e, f).size > 1) { "At least 2 parameters should be different w.h.p." } + } + + @Test + fun test() { + // TODO maybe create API like DefaultGens.getDefaultIntGen or something + val defaultGen = IntGen("") + val paramGen = IntGen("") + LincheckStressConfiguration().apply { + invocationsPerIteration(1) + iterations(100) + threads(1) + + initialState { ScenarioGenerationParameterDiversityTest() } + + operation(listOf(defaultGen, defaultGen, defaultGen, defaultGen, defaultGen, defaultGen), + { arg -> foo(arg[0] as Int, arg[1] as Int, arg[2] as Int, arg[3] as Int, arg[4] as Int, arg[5] as Int) }) + operation(listOf(paramGen, paramGen, paramGen, paramGen, paramGen, paramGen), + { arg -> bar(arg[0] as Int, arg[1] as Int, arg[2] as Int, arg[3] as Int, arg[4] as Int, arg[5] as Int) }) + }.runTest() + } + + override fun extractState(): Any = 0 // constant state +} diff --git a/src/native/test/StateEquivalenceImplCheckTest.kt b/src/native/test/StateEquivalenceImplCheckTest.kt new file mode 100644 index 000000000..b2695add0 --- /dev/null +++ b/src/native/test/StateEquivalenceImplCheckTest.kt @@ -0,0 +1,45 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.jetbrains.kotlinx.lincheck.* +import kotlin.native.concurrent.* +import kotlin.test.* + +class StateEquivalenceImplCheckTest { + private var i = AtomicInt(0) + + fun incAndGet() = i.addAndGet(1) + + @Test + fun test() { + try { + LincheckStressConfiguration().apply { + initialState { StateEquivalenceImplCheckTest() } + + operation(StateEquivalenceImplCheckTest::incAndGet) + }.runTest() + } catch(e: IllegalStateException) { + // Check that IllegalStateException is thrown if `requireStateEquivalenceImplCheck` option is true by default + // and hashCode/equals methods are not implemented + } + } +} From 563d50fbfb37bf2de0d91eab76b4f65885915789 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 17 Apr 2021 01:24:19 +0300 Subject: [PATCH 35/72] Add more tests. Reduce invocationsPerIteration to 500 --- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 2 +- .../strategy/stress/StressStrategy.kt | 6 +- src/native/test/AbstractLincheckTest.kt | 6 +- src/native/test/ExceptionAsResultTest.kt | 1 + src/native/test/FirstTest.kt | 2 +- src/native/test/HangingTest.kt | 1 + .../test/StateEquivalenceImplCheckTest.kt | 1 + .../test/verifier/EpsilonVerifierTest.kt | 48 +++++++ .../verifier/SequentialSpecificationTest.kt | 67 +++++++++ .../verifier/linearizability/ClocksTest.kt | 103 ++++++++++++++ .../verifier/linearizability/CounterTests.kt | 114 +++++++++++++++ .../verifier/linearizability/HashMapTest.kt | 47 ++++++ .../linearizability/LockBasedSetTests.kt | 134 ++++++++++++++++++ 13 files changed, 525 insertions(+), 7 deletions(-) create mode 100644 src/native/test/verifier/EpsilonVerifierTest.kt create mode 100644 src/native/test/verifier/SequentialSpecificationTest.kt create mode 100644 src/native/test/verifier/linearizability/ClocksTest.kt create mode 100644 src/native/test/verifier/linearizability/CounterTests.kt create mode 100644 src/native/test/verifier/linearizability/HashMapTest.kt create mode 100644 src/native/test/verifier/linearizability/LockBasedSetTests.kt diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 7d22264dd..696ea8582 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -36,7 +36,7 @@ actual class Actor( actual val allowExtraSuspension: Boolean = false, actual val promptCancellation: Boolean = false, actual val cancelOnSuspension: Boolean = false, - actual val handledExceptions: List> + actual val handledExceptions: List> = emptyList() ) { actual override fun toString(): String = diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 88995f00d..81799e99f 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -35,10 +35,14 @@ actual class StressStrategy actual constructor( private val stateRepresentationFunction: StateRepresentationFunction?, private val verifier: Verifier ) : Strategy(scenario) { - private val invocations = testCfg.invocationsPerIteration + private var invocations = testCfg.invocationsPerIteration private var runner: Runner init { + if(invocations > 500) { + printErr("invocations count has been reduced from $invocations to 500") // TODO remove when bug with GC will be fixed + invocations = 500 + } runner = ParallelThreadsRunner( strategy = this, testClass = testClass, diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index 6e8405e7a..a6504cab5 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -20,7 +20,7 @@ * #L% */ -import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.native.* @@ -49,21 +49,19 @@ abstract class AbstractLincheckStressTest( @Test fun testWithStressStrategy(): Unit = LincheckStressConfiguration().run { - invocationsPerIteration(5_000) commonConfiguration() runInternalTest() } /* @Test fun testWithModelCheckingStrategy(): Unit = ModelCheckingOptions().run { - invocationsPerIteration(1_000) commonConfiguration() runInternalTest() } */ private fun > T.commonConfiguration(): Unit = run { iterations(10) - invocationsPerIteration(1000) + invocationsPerIteration(500) actorsBefore(2) threads(3) actorsPerThread(2) diff --git a/src/native/test/ExceptionAsResultTest.kt b/src/native/test/ExceptionAsResultTest.kt index e1313d6f0..f61cefd4a 100644 --- a/src/native/test/ExceptionAsResultTest.kt +++ b/src/native/test/ExceptionAsResultTest.kt @@ -37,6 +37,7 @@ class ExceptionAsResultTest { try { LincheckStressConfiguration().apply { iterations(1) + invocationsPerIteration(500) requireStateEquivalenceImplCheck(false) initialState { ExceptionAsResultTest() } diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 937b29429..c2e4cda54 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -83,7 +83,7 @@ class FirstTest { fun test_working() { LincheckStressConfiguration().apply { iterations(10) - invocationsPerIteration(1000) + invocationsPerIteration(500) actorsBefore(2) threads(3) actorsPerThread(5) diff --git a/src/native/test/HangingTest.kt b/src/native/test/HangingTest.kt index 70d6f046d..c4c414852 100644 --- a/src/native/test/HangingTest.kt +++ b/src/native/test/HangingTest.kt @@ -30,6 +30,7 @@ class HangingTest : AbstractLincheckStressTest(DeadlockWithDumpFail override fun > T.customize() { iterations(1) + invocationsPerIteration(500) actorsBefore(0) actorsAfter(0) requireStateEquivalenceImplCheck(false) diff --git a/src/native/test/StateEquivalenceImplCheckTest.kt b/src/native/test/StateEquivalenceImplCheckTest.kt index b2695add0..2c105f247 100644 --- a/src/native/test/StateEquivalenceImplCheckTest.kt +++ b/src/native/test/StateEquivalenceImplCheckTest.kt @@ -34,6 +34,7 @@ class StateEquivalenceImplCheckTest { try { LincheckStressConfiguration().apply { initialState { StateEquivalenceImplCheckTest() } + invocationsPerIteration(500) operation(StateEquivalenceImplCheckTest::incAndGet) }.runTest() diff --git a/src/native/test/verifier/EpsilonVerifierTest.kt b/src/native/test/verifier/EpsilonVerifierTest.kt new file mode 100644 index 000000000..f5a51be8c --- /dev/null +++ b/src/native/test/verifier/EpsilonVerifierTest.kt @@ -0,0 +1,48 @@ +/* + * #%L + * Lincheck + * %% + * Copyright (C) 2015 - 2018 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package verifier + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.test.* + +class EpsilonVerifierTest : VerifierState() { + private var i = 0 + + fun incAndGet() = i++ // non-atomic! + + @Test + fun test() { + LincheckStressConfiguration().apply { + iterations(5) + threads(2) + actorsPerThread(2) + verifier { s -> EpsilonVerifier(s) } + + initialState { EpsilonVerifierTest() } + + operation(EpsilonVerifierTest::incAndGet) + }.runTest() + } + + override fun extractState() = i +} diff --git a/src/native/test/verifier/SequentialSpecificationTest.kt b/src/native/test/verifier/SequentialSpecificationTest.kt new file mode 100644 index 000000000..59483eb23 --- /dev/null +++ b/src/native/test/verifier/SequentialSpecificationTest.kt @@ -0,0 +1,67 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package verifier + +import AbstractLincheckStressTest +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.native.concurrent.* + +interface CounterInterface { + fun set(value: Int) + fun get(): Int +} + +class TestCounter : VerifierState(), CounterInterface { + private val c = AtomicInt(0) + + override fun set(value: Int) { + c.value = value + } + + override fun get() = c.value + 1 + override fun extractState() = c.value +} + +class SequentialSpecificationTest : AbstractLincheckStressTest(IncorrectResultsFailure::class) { + override fun > T.customize() { + sequentialSpecification(SequentialSpecification { CorrectCounter() }) + + initialState { TestCounter() } + + operation(CounterInterface::get) + operation(IntGen(""), CounterInterface::set) + } +} + + +class CorrectCounter : VerifierState(), CounterInterface { + private var c = 0 + override fun set(value: Int) { + c = value + } + + override fun get(): Int = c + override fun extractState() = c +} diff --git a/src/native/test/verifier/linearizability/ClocksTest.kt b/src/native/test/verifier/linearizability/ClocksTest.kt new file mode 100644 index 000000000..77c885742 --- /dev/null +++ b/src/native/test/verifier/linearizability/ClocksTest.kt @@ -0,0 +1,103 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package verifier.linearizability + +import AbstractLincheckStressTest +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.strategy.* + +interface Clocks { + fun a() + fun b() + fun c() + fun d(): Int +} + +class ClocksTest : AbstractLincheckStressTest(IncorrectResultsFailure::class), Clocks { + private var bStarted = false + + override fun a() { + // do nothing + } + + override fun b() { + bStarted = true + } + + override fun c() { + while (!bStarted) { + } // wait until `a()` is completed + } + + override fun d(): Int { + return 0 // cannot return 0, should fail + } + + override fun > T.customize() { + executionGenerator { c: CTestConfiguration, s: CTestStructure -> ClocksTestScenarioGenerator(c, s) } + iterations(1) + invocationsPerIteration(500) + sequentialSpecification(SequentialSpecification { ClocksTestSequential() }) + requireStateEquivalenceImplCheck(false) + minimizeFailedScenario(false) + + initialState { ClocksTest() } + + operation(Clocks::a) + operation(Clocks::b) + operation(Clocks::c) + operation(Clocks::d) + } +} + +class ClocksTestScenarioGenerator(testCfg: CTestConfiguration, testStructure: CTestStructure) + : ExecutionGenerator(testCfg, testStructure) { + override fun nextExecution() = ExecutionScenario( + emptyList(), + listOf( + listOf( + Actor(function = {instance, _ -> (instance as Clocks).a()}, arguments = emptyList()), + Actor(function = {instance, _ -> (instance as Clocks).b()}, arguments = emptyList()) + ), + listOf( + Actor(function = {instance, _ -> (instance as Clocks).c()}, arguments = emptyList()), + Actor(function = {instance, _ -> (instance as Clocks).d()}, arguments = emptyList()) + ) + ), + emptyList() + ) + +} + +class ClocksTestSequential : Clocks { + private var x = 0 + + override fun a() { + x = 1 + } + + override fun b() {} + override fun c() {} + + override fun d(): Int = x +} diff --git a/src/native/test/verifier/linearizability/CounterTests.kt b/src/native/test/verifier/linearizability/CounterTests.kt new file mode 100644 index 000000000..18b41d38c --- /dev/null +++ b/src/native/test/verifier/linearizability/CounterTests.kt @@ -0,0 +1,114 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package verifier.linearizability + +import AbstractLincheckStressTest +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import kotlin.native.concurrent.* +import kotlin.reflect.KClass + +interface CounterTest { + fun incAndGet(): Int +} + +abstract class AbstractCounterTest( + private val counter: Counter, + vararg expectedErrors: KClass +) : AbstractLincheckStressTest(*expectedErrors), CounterTest { + override fun incAndGet(): Int = counter.incAndGet() + + fun > T.customizeOperations() { + operation(CounterTest::incAndGet) + } + + override fun extractState(): Any = counter.get() +} + +class CounterCorrectTest : AbstractCounterTest(CounterCorrect()) { + override fun > T.customize() { + customizeOperations() + + initialState { CounterCorrectTest() } + } +} + +class CounterWrong0Test : AbstractCounterTest(CounterWrong0(), IncorrectResultsFailure::class) { + override fun > T.customize() { + customizeOperations() + + initialState { CounterWrong0Test() } + } +} + +class CounterWrong1Test : AbstractCounterTest(CounterWrong1(), IncorrectResultsFailure::class) { + override fun > T.customize() { + customizeOperations() + + initialState { CounterWrong1Test() } + } +} + +class CounterWrong2Test : AbstractCounterTest(CounterWrong2(), IncorrectResultsFailure::class) { + override fun > T.customize() { + customizeOperations() + + initialState { CounterWrong2Test() } + } +} + +interface Counter { + fun incAndGet(): Int + fun get(): Int +} + +private class CounterWrong0 : Counter { + private var c: Int = 0 + + override fun incAndGet(): Int = ++c + override fun get(): Int = c +} + +private class CounterWrong1 : Counter { + private var c: Int = 0 + + override fun incAndGet(): Int { + c++ + return c + } + + override fun get(): Int = c +} + +private class CounterWrong2 : Counter { + private var c: Int = 0 + + override fun incAndGet(): Int = ++c + override fun get(): Int = c +} + +private class CounterCorrect : Counter { + private val c = AtomicInt(0) + + override fun incAndGet(): Int = c.addAndGet(1) + override fun get(): Int = c.value +} diff --git a/src/native/test/verifier/linearizability/HashMapTest.kt b/src/native/test/verifier/linearizability/HashMapTest.kt new file mode 100644 index 000000000..67a92c3be --- /dev/null +++ b/src/native/test/verifier/linearizability/HashMapTest.kt @@ -0,0 +1,47 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package verifier.linearizability + +import AbstractLincheckStressTest +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.* + +class HashMapTest : AbstractLincheckStressTest(IncorrectResultsFailure::class) { + private val m = HashMap() + + fun put(key: Int, value: Int): Int? = m.put(key, value) + + operator fun get(key: Int?): Int? = m[key] + + override fun > T.customize() { + initialState { HashMapTest() } + + val keyGen = IntGen("") + + operation(IntGen(""), keyGen, HashMapTest::put) + operation(keyGen, HashMapTest::get) + } + + override fun extractState(): Any = m +} + diff --git a/src/native/test/verifier/linearizability/LockBasedSetTests.kt b/src/native/test/verifier/linearizability/LockBasedSetTests.kt new file mode 100644 index 000000000..e84e9a4e2 --- /dev/null +++ b/src/native/test/verifier/linearizability/LockBasedSetTests.kt @@ -0,0 +1,134 @@ +/*- + * #%L + * Lincheck + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package verifier.linearizability + +import AbstractLincheckStressTest +import kotlinx.atomicfu.locks.* +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import kotlin.native.concurrent.* + +abstract class AbstractSetTest(private val set: Set) : AbstractLincheckStressTest() { + fun add(key: Int): Boolean = set.add(key) + + fun remove(key: Int): Boolean = set.remove(key) + + operator fun contains(key: Int): Boolean = set.contains(key) + + fun > T.customizeOperations() { + val keyGen = IntGen("1:5") + operation(keyGen, AbstractSetTest::add) + operation(keyGen, AbstractSetTest::remove) + operation(keyGen, AbstractSetTest::contains) + } + + override fun extractState(): Any = (1..5).map { set.contains(it) } +} + +class SpinLockSetTest : AbstractSetTest(SpinLockBasedSet()) { + override fun > T.customize() { + initialState { SpinLockSetTest() } + + customizeOperations() + } +} + +class ReentrantLockSetTest : AbstractSetTest(ReentrantLockBasedSet()) { + override fun > T.customize() { + initialState { ReentrantLockSetTest() } + + customizeOperations() + } +} + +class SynchronizedLockSetTest : AbstractSetTest(SynchronizedBlockBasedSet()) { + override fun > T.customize() { + initialState { SynchronizedLockSetTest() } + + customizeOperations() + } +} + +interface Set { + fun add(key: Int): Boolean + fun remove(key: Int): Boolean + fun contains(key: Int): Boolean +} + +internal class SpinLockBasedSet : Set { + private val set = mutableSetOf() + private val locked = AtomicInt(0) + + override fun add(key: Int): Boolean = withSpinLock { + set.add(key) + } + + override fun remove(key: Int): Boolean = withSpinLock { + set.remove(key) + } + + override fun contains(key: Int): Boolean = withSpinLock { + set.contains(key) + } + + private fun withSpinLock(block: () -> Boolean): Boolean { + while (!locked.compareAndSet(0, 1)) { /* Thread.yield() in java */ } + try { + return block() + } finally { + locked.value = 0 + } + } +} + +internal class ReentrantLockBasedSet : Set { + private val set = mutableSetOf() + private val lock = ReentrantLock() + + override fun add(key: Int): Boolean = lock.withLock { + set.add(key) + } + + override fun remove(key: Int): Boolean = lock.withLock { + set.remove(key) + } + + override fun contains(key: Int): Boolean = lock.withLock { + set.contains(key) + } +} + +internal class SynchronizedBlockBasedSet : SynchronizedObject(), Set { + private val set = mutableSetOf() + + override fun add(key: Int): Boolean = synchronized(this) { + set.add(key) + } + + override fun remove(key: Int): Boolean = synchronized(this) { + set.remove(key) + } + + override fun contains(key: Int): Boolean = synchronized(this) { + set.contains(key) + } +} \ No newline at end of file From 37d4951cc392b1d2a92e4bf6d157a9ba0de64aa6 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 17 Apr 2021 14:34:51 +0300 Subject: [PATCH 36/72] change debug output. Add test names --- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 19 ++++++++++++++----- src/native/test/ExceptionAsResultTest.kt | 2 +- src/native/test/FirstTest.kt | 4 ++-- src/native/test/RunOnceTest.kt | 2 +- ...cenarioGenerationParameterDiversityTest.kt | 2 +- .../test/StateEquivalenceImplCheckTest.kt | 2 +- src/native/test/ValidateFunctionTest.kt | 2 +- .../test/verifier/EpsilonVerifierTest.kt | 3 ++- 8 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 52f1ed42c..547b88d73 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -30,7 +30,7 @@ import kotlin.jvm.* import kotlin.reflect.* // TODO StressOptions methods cast this class to StressOptions -class LincheckStressConfiguration : StressOptions() { +class LincheckStressConfiguration(private val testName: String = "") : StressOptions() { /* invocationsPerIteration iterations @@ -52,11 +52,11 @@ class LincheckStressConfiguration : StressOptions() { private var validationFunctions = mutableListOf() private var stateRepresentationFunction: StateRepresentationFunction? = null - internal fun getTestClass(): TestClass { + private fun getTestClass(): TestClass { return testClass ?: throw IllegalArgumentException("initialState should be specified") } - internal fun getTestStructure(): CTestStructure { + private fun getTestStructure(): CTestStructure { return CTestStructure( actorGenerators, operationGroups, @@ -67,9 +67,18 @@ class LincheckStressConfiguration : StressOptions() { fun runTest() { LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) + if(testName.isNotEmpty()) { + println("Finished test $testName") + } } - fun checkImpl() = LinChecker(getTestClass(), getTestStructure(), this as StressOptions).checkImpl() + fun checkImpl(): LincheckFailure? { + val result = LinChecker(getTestClass(), getTestStructure(), this as StressOptions).checkImpl() + if(testName.isNotEmpty()) { + println("Finished test $testName") + } + return result + } // =========================== Constructor @@ -230,7 +239,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> - printErr("Iteration $i") // TODO debug output + //printErr("Iteration $i") // TODO debug output val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(i + 1, iterations, scenario) diff --git a/src/native/test/ExceptionAsResultTest.kt b/src/native/test/ExceptionAsResultTest.kt index f61cefd4a..6fb45783c 100644 --- a/src/native/test/ExceptionAsResultTest.kt +++ b/src/native/test/ExceptionAsResultTest.kt @@ -35,7 +35,7 @@ class ExceptionAsResultTest { @Test fun test() { try { - LincheckStressConfiguration().apply { + LincheckStressConfiguration("ExceptionAsResultTest").apply { iterations(1) invocationsPerIteration(500) requireStateEquivalenceImplCheck(false) diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index c2e4cda54..0c3a48517 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -55,7 +55,7 @@ class TestClass : VerifierState() { class FirstTest { @Test fun test_failing() { - val f = LincheckStressConfiguration().apply { + val f = LincheckStressConfiguration("FirstTest_1").apply { iterations(300) invocationsPerIteration(50) actorsBefore(2) @@ -81,7 +81,7 @@ class FirstTest { @Test fun test_working() { - LincheckStressConfiguration().apply { + LincheckStressConfiguration("FirstTest_2").apply { iterations(10) invocationsPerIteration(500) actorsBefore(2) diff --git a/src/native/test/RunOnceTest.kt b/src/native/test/RunOnceTest.kt index bf98552c3..2f8277727 100644 --- a/src/native/test/RunOnceTest.kt +++ b/src/native/test/RunOnceTest.kt @@ -37,7 +37,7 @@ class RunOnceTest { @Test fun test() { - LincheckStressConfiguration().apply { + LincheckStressConfiguration("RunOnceTest").apply { threads(3) iterations(10) invocationsPerIteration(10) diff --git a/src/native/test/ScenarioGenerationParameterDiversityTest.kt b/src/native/test/ScenarioGenerationParameterDiversityTest.kt index 34f4a736e..3f2d4795b 100644 --- a/src/native/test/ScenarioGenerationParameterDiversityTest.kt +++ b/src/native/test/ScenarioGenerationParameterDiversityTest.kt @@ -42,7 +42,7 @@ class ScenarioGenerationParameterDiversityTest : VerifierState() { // TODO maybe create API like DefaultGens.getDefaultIntGen or something val defaultGen = IntGen("") val paramGen = IntGen("") - LincheckStressConfiguration().apply { + LincheckStressConfiguration("ScenarioGenerationParameterDiversityTest").apply { invocationsPerIteration(1) iterations(100) threads(1) diff --git a/src/native/test/StateEquivalenceImplCheckTest.kt b/src/native/test/StateEquivalenceImplCheckTest.kt index 2c105f247..a918c30c8 100644 --- a/src/native/test/StateEquivalenceImplCheckTest.kt +++ b/src/native/test/StateEquivalenceImplCheckTest.kt @@ -32,7 +32,7 @@ class StateEquivalenceImplCheckTest { @Test fun test() { try { - LincheckStressConfiguration().apply { + LincheckStressConfiguration("StateEquivalenceImplCheckTest").apply { initialState { StateEquivalenceImplCheckTest() } invocationsPerIteration(500) diff --git a/src/native/test/ValidateFunctionTest.kt b/src/native/test/ValidateFunctionTest.kt index ae5e1d7f9..d2493c96a 100644 --- a/src/native/test/ValidateFunctionTest.kt +++ b/src/native/test/ValidateFunctionTest.kt @@ -45,7 +45,7 @@ class ValidateFunctionTest : VerifierState() { @Test fun test() { - val f = LincheckStressConfiguration().apply { + val f = LincheckStressConfiguration("ValidateFunctionTest").apply { iterations(1) invocationsPerIteration(1) actorsBefore(3) diff --git a/src/native/test/verifier/EpsilonVerifierTest.kt b/src/native/test/verifier/EpsilonVerifierTest.kt index f5a51be8c..cfbbde0d0 100644 --- a/src/native/test/verifier/EpsilonVerifierTest.kt +++ b/src/native/test/verifier/EpsilonVerifierTest.kt @@ -32,8 +32,9 @@ class EpsilonVerifierTest : VerifierState() { @Test fun test() { - LincheckStressConfiguration().apply { + LincheckStressConfiguration("EpsilonVerifierTest").apply { iterations(5) + invocationsPerIteration(500) threads(2) actorsPerThread(2) verifier { s -> EpsilonVerifier(s) } From 0d32c518e77cf8893ab880bd36bdaa849985b038 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 19 Apr 2021 16:24:11 +0300 Subject: [PATCH 37/72] Change gradle version, add sharedLib to native project --- build.gradle.kts | 9 ++- gradle/native-targets.gradle | 9 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 67 ++++++++++++++++--- 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 888b1b568..d10b5abb5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,8 +6,6 @@ allprojects { } } -apply(from = rootProject.file("gradle/native-targets.gradle")) - // atomicfu buildscript { val atomicfuVersion: String by project @@ -17,6 +15,8 @@ buildscript { } apply(plugin = "kotlinx-atomicfu") +apply(from = rootProject.file("gradle/native-targets.gradle")) + plugins { java kotlin("multiplatform") @@ -116,6 +116,11 @@ sourceSets.test { java.srcDirs("src/jvm/test") } +tasks.wrapper { + gradleVersion = "6.7.1" + distributionType = Wrapper.DistributionType.ALL +} + tasks { // empty xxx-javadoc.jar register("javadocJar") { diff --git a/gradle/native-targets.gradle b/gradle/native-targets.gradle index 560d1d38a..04e5349ba 100644 --- a/gradle/native-targets.gradle +++ b/gradle/native-targets.gradle @@ -105,7 +105,14 @@ kotlin { targets { if (project.ext.nativeState == NativeState.DISABLED) return if (project.ext.singleTargetMode) { - fromPreset(project.ext.ideaPreset, 'native') + fromPreset(project.ext.ideaPreset, 'native') { + binaries { + sharedLib { + baseName = "native" // on Linux and macOS + // baseName = "libnative" //on Windows + } + } + } } else { // Linux addTarget(presets.linuxX64) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 442d9132e..1f3fdbc52 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-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 547b88d73..61a06e10c 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -20,17 +20,64 @@ package org.jetbrains.kotlinx.lincheck -import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* -import kotlin.jvm.* import kotlin.reflect.* +class NativeAPIStressConfiguration: LincheckStressConfiguration() { + fun runNativeTest() { + LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) + } + + fun setupInitialState( + state: () -> Any + ) = apply { + testClass = TestClass(state) + } + + fun setupOperationWithoutArguments( + op: () -> Unit + ) = apply { + val actorGenerator = ActorGenerator( + function = { _, _ -> + op() + }, + parameterGenerators = emptyList(), + functionName = "Operation", + useOnce = false, + isSuspendable = false, + handledExceptions = emptyList() + ) + actorGenerators.add(actorGenerator) + } + + fun setupOperation( + pGens: List>, + op: (Any, List) -> Any, + name: String = op.toString(), + handleExceptionsAsResult: List> = emptyList(), + useOnce: Boolean = false, + isSuspendable: Boolean = false + ) = apply { + val actorGenerator = ActorGenerator( + function = { instance, arguments -> + op(instance, arguments) + }, + parameterGenerators = pGens, + functionName = name, + useOnce = useOnce, + isSuspendable = isSuspendable, + handledExceptions = handleExceptionsAsResult + ) + actorGenerators.add(actorGenerator) + } +} + // TODO StressOptions methods cast this class to StressOptions -class LincheckStressConfiguration(private val testName: String = "") : StressOptions() { +open class LincheckStressConfiguration(protected val testName: String = "") : StressOptions() { /* invocationsPerIteration iterations @@ -46,17 +93,17 @@ class LincheckStressConfiguration(private val testName: String = "") : logLevel(logLevel: LoggingLevel) sequentialSpecification(clazz: SequentialSpecification<*>?) */ - private var testClass: TestClass? = null - private var actorGenerators = mutableListOf() - private var operationGroups = mutableListOf() - private var validationFunctions = mutableListOf() - private var stateRepresentationFunction: StateRepresentationFunction? = null + protected var testClass: TestClass? = null + protected var actorGenerators = mutableListOf() + protected var operationGroups = mutableListOf() + protected var validationFunctions = mutableListOf() + protected var stateRepresentationFunction: StateRepresentationFunction? = null - private fun getTestClass(): TestClass { + protected fun getTestClass(): TestClass { return testClass ?: throw IllegalArgumentException("initialState should be specified") } - private fun getTestStructure(): CTestStructure { + protected fun getTestStructure(): CTestStructure { return CTestStructure( actorGenerators, operationGroups, From 0c712f7230b785d93b5ef6867de70ecdb040bac3 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 19 Apr 2021 16:55:08 +0300 Subject: [PATCH 38/72] Add simple API for native --- .../main/org/jetbrains/kotlinx/lincheck/LinChecker.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 61a06e10c..00dd6a80e 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -20,6 +20,7 @@ package org.jetbrains.kotlinx.lincheck +import kotlinx.cinterop.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.* @@ -33,20 +34,20 @@ class NativeAPIStressConfiguration: LincheckStressConfiguration() { } fun setupInitialState( - state: () -> Any ) = apply { - testClass = TestClass(state) + testClass = TestClass({}) } fun setupOperationWithoutArguments( - op: () -> Unit + op: CPointer Unit>>, + functionName: String ) = apply { val actorGenerator = ActorGenerator( function = { _, _ -> - op() + op.invoke() }, parameterGenerators = emptyList(), - functionName = "Operation", + functionName = functionName, useOnce = false, isSuspendable = false, handledExceptions = emptyList() From 310c8890d3c66087ae4955330eb2b2275dfee0c3 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 19 Apr 2021 20:31:43 +0300 Subject: [PATCH 39/72] Add simple c++ example --- cpp/CMakeLists.txt | 9 ++++ cpp/README.MD | 6 +++ cpp/main.cpp | 45 +++++++++++++++++++ cpp/run.sh | 1 + .../jetbrains/kotlinx/lincheck/LinChecker.kt | 20 ++++++--- .../strategy/stress/StressStrategy.kt | 3 +- 6 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 cpp/CMakeLists.txt create mode 100644 cpp/README.MD create mode 100644 cpp/main.cpp create mode 100644 cpp/run.sh diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 000000000..65204bfd5 --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.19) +project(project_name) + +set(CMAKE_CXX_STANDARD 20) + +add_executable(project_name main.cpp) + +link_directories(/home/krock21/IdeaProjects/kotlinx-lincheck//build/bin/native/debugShared/) +target_link_libraries(project_name native) diff --git a/cpp/README.MD b/cpp/README.MD new file mode 100644 index 000000000..4991b8ea9 --- /dev/null +++ b/cpp/README.MD @@ -0,0 +1,6 @@ +1. In the kotlinx-lincheck directory run `./gradlew linkNative`. It generates `build/bin/native/debugShared` and `build/bin/native/releaseShared` + +Run this sample: +``` +g++ main.cpp ../build/bin/native/releaseShared/libnative.so && time ./a.out +``` \ No newline at end of file diff --git a/cpp/main.cpp b/cpp/main.cpp new file mode 100644 index 000000000..71e755be4 --- /dev/null +++ b/cpp/main.cpp @@ -0,0 +1,45 @@ +#include +#include "../build/bin/native/releaseShared/libnative_api.h" + +extern libnative_ExportedSymbols* libnative_symbols(void); + +std::atomic_int inc_count; +std::atomic_int dec_count; +int sharedState = 0; +std::atomic_int sharedState2(0); + +void inc() { + sharedState++; + //std::cout << "operation1 changed to " << sharedState << std::endl; + inc_count++; + sharedState2++; +} + +void dec() { + sharedState--; + //std::cout << "operation2 changed to " << sharedState << std::endl; + dec_count++; + sharedState2--; +} + +int main(int argc, char** argv) { + libnative_ExportedSymbols* lib = libnative_symbols(); + + auto configuration = lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.NativeAPIStressConfiguration(); + + //lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialState(configuration); + + // void* cast is broken. https://stackoverflow.com/questions/36645660/why-cant-i-cast-a-function-pointer-to-void + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperationWithoutArguments(configuration, (void *)(inc), "operation1"); + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperationWithoutArguments(configuration, (void *)(dec), "operation2"); + + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.runNativeTest(configuration); + + std::cout << "inc_count " << inc_count << "\n"; + std::cout << "dec_count " << dec_count << "\n"; + std::cout << "correct_diff " << inc_count - dec_count << "\n"; + + std::cout << "non-atomic: " << sharedState << " vs atomic " << sharedState2; + + return 0; +} \ No newline at end of file diff --git a/cpp/run.sh b/cpp/run.sh new file mode 100644 index 000000000..212c4ba23 --- /dev/null +++ b/cpp/run.sh @@ -0,0 +1 @@ +#!/usr/bin/env bash \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 00dd6a80e..26e55bb27 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -29,13 +29,18 @@ import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.reflect.* class NativeAPIStressConfiguration: LincheckStressConfiguration() { - fun runNativeTest() { - LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) + init { + // simple configuration + iterations(1) + invocationsPerIteration(100_000) // 100k + + initialState { } // empty state + requireStateEquivalenceImplCheck(false) } - fun setupInitialState( - ) = apply { - testClass = TestClass({}) + + fun runNativeTest() { + LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) } fun setupOperationWithoutArguments( @@ -100,6 +105,11 @@ open class LincheckStressConfiguration(protected val testName: String protected var validationFunctions = mutableListOf() protected var stateRepresentationFunction: StateRepresentationFunction? = null + init { + // override invocationsPerIteration + invocationsPerIteration(500) + } + protected fun getTestClass(): TestClass { return testClass ?: throw IllegalArgumentException("initialState should be specified") } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 81799e99f..fb97f5f06 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -40,8 +40,7 @@ actual class StressStrategy actual constructor( init { if(invocations > 500) { - printErr("invocations count has been reduced from $invocations to 500") // TODO remove when bug with GC will be fixed - invocations = 500 + printErr("WARNING invocations count is bigger than 500, that may lead to crash") // TODO remove when bug with GC will be fixed } runner = ParallelThreadsRunner( strategy = this, From 44991e1ec4bbd760ecea81b8d2afa55cbd7cc184 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 22 Apr 2021 16:36:27 +0300 Subject: [PATCH 40/72] Improve API for native, add lincheck.h wrapper --- cpp/CMakeLists.txt | 11 +- cpp/README.MD | 10 +- cpp/lincheck.h | 155 +++++++++++ cpp/main.cpp | 79 +++--- cpp/run.sh | 1 - .../lincheck/strategy/stress/StressOptions.kt | 2 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 249 ++++++++++++++++-- .../strategy/stress/StressStrategy.kt | 3 - src/native/test/FirstTest.kt | 38 ++- 9 files changed, 474 insertions(+), 74 deletions(-) create mode 100644 cpp/lincheck.h delete mode 100644 cpp/run.sh diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 65204bfd5..252be6f31 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,9 +1,10 @@ -cmake_minimum_required(VERSION 3.19) -project(project_name) +cmake_minimum_required(VERSION 3.16) +project(lincheck_test) set(CMAKE_CXX_STANDARD 20) -add_executable(project_name main.cpp) +add_executable(lincheck_test main.cpp lincheck.h) -link_directories(/home/krock21/IdeaProjects/kotlinx-lincheck//build/bin/native/debugShared/) -target_link_libraries(project_name native) +add_library(native SHARED IMPORTED GLOBAL) +set_target_properties(native PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/debugShared/libnative.so") +target_link_libraries(lincheck_test native) diff --git a/cpp/README.MD b/cpp/README.MD index 4991b8ea9..7ee1bca19 100644 --- a/cpp/README.MD +++ b/cpp/README.MD @@ -1,6 +1,8 @@ 1. In the kotlinx-lincheck directory run `./gradlew linkNative`. It generates `build/bin/native/debugShared` and `build/bin/native/releaseShared` -Run this sample: -``` -g++ main.cpp ../build/bin/native/releaseShared/libnative.so && time ./a.out -``` \ No newline at end of file +run this as a CMake application +1. `mkdir build` +2. `cd build` +3. `cmake ..` +4. `make` +5. `./lincheck_test` diff --git a/cpp/lincheck.h b/cpp/lincheck.h new file mode 100644 index 000000000..f005840ab --- /dev/null +++ b/cpp/lincheck.h @@ -0,0 +1,155 @@ +#pragma once + +#include +#include "../build/bin/native/debugShared/libnative_api.h" + +extern libnative_ExportedSymbols *libnative_symbols(void); + +namespace Lincheck { + class UnsignedLongGen { + std::mt19937 rnd; + public: + unsigned long generate() { + return rnd(); + } + }; + + template + class LincheckConfiguration { + libnative_ExportedSymbols *lib = libnative_symbols(); + libnative_kref_org_jetbrains_kotlinx_lincheck_NativeAPIStressConfiguration configuration = lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.NativeAPIStressConfiguration(); + + typedef void *(*constructor_pointer)(); + + typedef void (*destructor_pointer)(void *); + + typedef bool (*equals_pointer)(void *, void *); + + typedef int (*hashCode_pointer)(void *); + + public: + LincheckConfiguration() { + constructor_pointer *constructor = new constructor_pointer(); + *constructor = []() -> void * { return new TestClass(); }; + + destructor_pointer *destructor = new destructor_pointer(); + *destructor = [](void *p) { delete (TestClass *) p; }; + + equals_pointer *equals = new equals_pointer(); + *equals = [](void *a, void *b) -> bool { return *(TestClass *) a == *(TestClass *) b; }; + + hashCode_pointer *hashCode = new hashCode_pointer(); + *hashCode = [](void *) -> int { return 0; }; + + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialStateAndSequentialSpecification( + configuration, + (void *) *constructor, // constructor + (void *) *destructor, // destructor + (void *) *equals, // equals + (void *) *hashCode // hashCode + ); + } + + void iterations(int count) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupIterations( + configuration, count); + } + + void invocationsPerIteration(int count) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInvocationsPerIteration( + configuration, count); + } + + void minimizeFailedScenario(bool minimizeFailedScenario) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupMinimizeFailedScenario( + configuration, minimizeFailedScenario); + } + + template + void operation(const char *operationName, bool useOnce = false) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation1( + configuration, + (void *) (void *(*)(void *)) [](void *instance) -> void * { // operation + auto *obj = (TestClass *) instance; // add type to void* + Ret res = (obj->*op)(); // invoke op method + return new Ret(res); // copy from stack to heap and return + }, + (void *) (void *(*)(void *)) [](void *instance) -> void * { // sequential specification + auto *obj = (SequentialSpecification *) instance; // add type to void* + Ret res = (obj->*seq_spec)(); // invoke op method + return new Ret(res); // copy from stack to heap and return + }, + (void *) (void (*)(void *)) [](void *ret) { // Ret destructor + Ret *obj = (Ret *) ret; // add type to void* + delete obj; // destructor + }, + (void *) (bool (*)(void *, void *)) [](void *a, void *b) -> bool { // Ret equals + Ret *obj_a = (Ret *) a; // add type to void* + Ret *obj_b = (Ret *) b; // add type to void* + return *obj_a == *obj_b; + }, + (void *) (int (*)(void *)) [](void *) -> int { // Ret hashCode + return 0; + }, + (void *) (int (*)(void *)) [](void *ret) -> int { // Ret toString + return (int) (*(Ret *) ret); + }, + operationName, + useOnce + ); + } + + template + void operation(const char *operationName, bool useOnce = false) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation2( + configuration, + (void *) (void *(*)()) []() -> void * { // arg1_gen_initial_state + return new Arg1Gen(); + }, + (void *) (void *(*)(void *)) [](void *gen) -> void * { // arg1_gen_generate + auto *obj = (Arg1Gen *) gen; // add type to void* + Arg1 arg = obj->generate(); // invoke generate method + return new Arg1(arg); // copy from stack to heap and return + }, + (void *) (int (*)(void *)) [](void *arg) -> int { // arg1_toString + return (int) (*(Arg1 *) arg); // cast to int + }, + (void *) (void *(*)(void *, void *)) [](void *instance, void *arg1) -> void * { // operation + auto *obj = (TestClass *) instance; // add type to void* + auto *a1 = (Arg1 *) arg1; + Ret res = (obj->*op)(*a1); // invoke op method + return new Ret(res); // copy from stack to heap and return + }, + (void *) (void *(*)(void *, void *)) [](void *instance, + void *arg1) -> void * { // sequential specification + auto *obj = (SequentialSpecification *) instance; // add type to void* + auto *a1 = (Arg1 *) arg1; + Ret res = (obj->*seq_spec)(*a1); // invoke op method + return new Ret(res); // copy from stack to heap and return + }, + (void *) (void (*)(void *)) [](void *ret) { // Ret destructor + Ret *obj = (Ret *) ret; // add type to void* + delete obj; // destructor + }, + (void *) (bool (*)(void *, void *)) [](void *a, void *b) -> bool { // Ret equals + Ret *obj_a = (Ret *) a; // add type to void* + Ret *obj_b = (Ret *) b; // add type to void* + return *obj_a == *obj_b; + }, + (void *) (int (*)(void *)) [](void *) -> int { // Ret hashCode + return 0; + }, + (void *) (int (*)(void *)) [](void *ret) -> int { // Ret toString + return (int) (*(Ret *) ret); + }, + operationName, + useOnce + ); + } + + void runTest() { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.runNativeTest(configuration); + } + }; +} \ No newline at end of file diff --git a/cpp/main.cpp b/cpp/main.cpp index 71e755be4..75c500d7a 100644 --- a/cpp/main.cpp +++ b/cpp/main.cpp @@ -1,45 +1,64 @@ #include -#include "../build/bin/native/releaseShared/libnative_api.h" +#include "lincheck.h" -extern libnative_ExportedSymbols* libnative_symbols(void); +class Instance { +public: + int sharedState = 0; + std::atomic_int sharedAtomicState = 0; -std::atomic_int inc_count; -std::atomic_int dec_count; -int sharedState = 0; -std::atomic_int sharedState2(0); + int inc() { + auto ans = ++sharedState; + //std::cout << "inc " << ans << std::endl; + return ans; + } -void inc() { - sharedState++; - //std::cout << "operation1 changed to " << sharedState << std::endl; - inc_count++; - sharedState2++; -} + int dec() { + auto ans = --sharedState; + //std::cout << "dec " << ans << std::endl; + return ans; + } -void dec() { - sharedState--; - //std::cout << "operation2 changed to " << sharedState << std::endl; - dec_count++; - sharedState2--; -} + int add(unsigned long value) { + auto ans = sharedState += value; + return ans; + } -int main(int argc, char** argv) { - libnative_ExportedSymbols* lib = libnative_symbols(); + int atomic_inc() { + return ++sharedAtomicState; + } - auto configuration = lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.NativeAPIStressConfiguration(); + int atomic_dec() { + return --sharedAtomicState; + } - //lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialState(configuration); + int atomic_add(unsigned long value) { + auto ans = sharedAtomicState += value; + return ans; + } - // void* cast is broken. https://stackoverflow.com/questions/36645660/why-cant-i-cast-a-function-pointer-to-void - lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperationWithoutArguments(configuration, (void *)(inc), "operation1"); - lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperationWithoutArguments(configuration, (void *)(dec), "operation2"); + Instance() { + //std::cout << "SharedInstance has created" << std::endl; + } - lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.runNativeTest(configuration); + ~Instance() { + std::cout << "SharedInstance has destructed" << std::endl; // To ensure that destructor is never called + } +}; - std::cout << "inc_count " << inc_count << "\n"; - std::cout << "dec_count " << dec_count << "\n"; - std::cout << "correct_diff " << inc_count - dec_count << "\n"; +bool operator==(const Instance &a, const Instance &b) { + return a.sharedAtomicState == b.sharedAtomicState; +} - std::cout << "non-atomic: " << sharedState << " vs atomic " << sharedState2; +int main(int argc, char **argv) { + using namespace Lincheck; + LincheckConfiguration conf; + conf.iterations(1); + conf.invocationsPerIteration(10000); + conf.minimizeFailedScenario(false); + conf.operation("inc"); + conf.operation("dec"); + conf.operation("add"); + conf.runTest(); return 0; } \ No newline at end of file diff --git a/cpp/run.sh b/cpp/run.sh deleted file mode 100644 index 212c4ba23..000000000 --- a/cpp/run.sh +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/env bash \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt index c102ad455..774b4c3f3 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt @@ -25,7 +25,7 @@ import org.jetbrains.kotlinx.lincheck.* * Options for [stress][StressStrategy] strategy. */ open class StressOptions : Options() { - private var invocationsPerIteration = StressCTestConfiguration.DEFAULT_INVOCATIONS + protected var invocationsPerIteration = StressCTestConfiguration.DEFAULT_INVOCATIONS /** * Run each test scenario the specified number of times. diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 26e55bb27..75c57f8e1 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -26,57 +26,246 @@ import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.native.concurrent.* import kotlin.reflect.* -class NativeAPIStressConfiguration: LincheckStressConfiguration() { +typealias CCreator = CPointer CPointer<*>>> +typealias CDestructor = CPointer) -> Unit>> +typealias EqualsCFunction = CPointer, CPointer<*>) -> Boolean>> +typealias HashCodeCFunction = CPointer) -> Int>> +typealias ToStringCFunction = CPointer) -> Int>> + +internal class ObjectWithDestructorAndEqualsAndHashcodeAndToString(val obj: CPointer<*>, + val destructor: CDestructor, + val equals: EqualsCFunction, + val hashCode: HashCodeCFunction, + val toString: ToStringCFunction) { + + override fun equals(other: Any?): Boolean { + return if (other is ObjectWithDestructorAndEqualsAndHashcodeAndToString) { + equals.invoke(obj, other.obj) + } else { + false + } + } + + override fun hashCode(): Int { + return hashCode.invoke(obj) + } + + override fun toString(): String { + return toString.invoke(obj).toString() + } + + protected fun finalize() { + // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 + destructor.invoke(obj) + } +} + +internal class ParameterGeneratorArgument(val arg: CPointer<*>, + val toString: ToStringCFunction) { + override fun toString(): String { + return toString.invoke(arg).toString() + } +} + +internal class ConcurrentInstance(val obj: CPointer<*>, val destructor: CDestructor) { + + protected fun finalize() { + // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 + destructor.invoke(obj) + } +} + +internal class SequentialSpecificationInstance(val obj: CPointer<*>, + val destructor: CDestructor, + val equalsFunction: EqualsCFunction, + val hashCodeFunction: HashCodeCFunction) { + override fun equals(other: Any?): Boolean { + return if (other is SequentialSpecificationInstance) { + equalsFunction.invoke(obj, other.obj) + } else { + false + } + } + + override fun hashCode(): Int { + return hashCodeFunction.invoke(obj) + } + + protected fun finalize() { + // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 + destructor.invoke(obj) + } +} + +class NativeAPIStressConfiguration : LincheckStressConfiguration() { + private var initialStateCreator: CCreator? = null + private var initialStateDestructor: CDestructor? = null + private var sequentialSpecificationCreator: CCreator? = null + private var sequentialSpecificationDestructor: CDestructor? = null + private var sequentialSpecificationEquals: EqualsCFunction? = null + private var sequentialSpecificationHashCode: HashCodeCFunction? = null + private var initialStateSet: Boolean = false + init { - // simple configuration + // default configuration iterations(1) - invocationsPerIteration(100_000) // 100k + invocationsPerIteration(10000) + } + + private fun extractObjectWithEqualsAndHashcodeAndToString(pointer: CPointer<*>): ObjectWithDestructorAndEqualsAndHashcodeAndToString { + val ref = pointer.asStableRef() + return ref.get().freeze() + } + + fun setupIterations(count: Int) { + iterations(count) + } - initialState { } // empty state - requireStateEquivalenceImplCheck(false) + fun setupInvocationsPerIteration(count: Int) { + invocationsPerIteration(count) } + fun setupMinimizeFailedScenario(minimizeFailedScenario: Boolean) { + minimizeFailedScenario(minimizeFailedScenario) + } fun runNativeTest() { - LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) + if (!initialStateSet) { + printErr("Please provide initialState, skipping...") + return + } + if (sequentialSpecificationCreator == null) { + printErr("Please provide sequentialSpecification, skipping...") + return + } + if (initialStateCreator != null) { + // different initialState and sequentialSpecification + initialState { + ConcurrentInstance(initialStateCreator!!.invoke(), sequentialSpecificationDestructor!!) + } + } else { + initialState { + ConcurrentInstance(sequentialSpecificationCreator!!.invoke(), sequentialSpecificationDestructor!!) + } + } + sequentialSpecification( + SequentialSpecification { + SequentialSpecificationInstance(sequentialSpecificationCreator!!.invoke(), sequentialSpecificationDestructor!!, sequentialSpecificationEquals!!, sequentialSpecificationHashCode!!) + } + ) + runTest() + } + + fun setupInitialState( + initialStateCreator: CCreator, + initialStateDestructor: CDestructor + ) { + initialStateSet = true + this.initialStateCreator = initialStateCreator + this.initialStateDestructor = initialStateDestructor + } + + fun setupInitialStateAndSequentialSpecification( + initialStateCreator: CCreator, + initialStateDestructor: CDestructor, + equals: EqualsCFunction, + hashCode: HashCodeCFunction + ) { + initialStateSet = true + this.initialStateCreator = null + this.initialStateDestructor = null + this.sequentialSpecificationCreator = initialStateCreator + this.sequentialSpecificationDestructor = initialStateDestructor + this.sequentialSpecificationEquals = equals + this.sequentialSpecificationHashCode = hashCode + } + + fun setupSequentialSpecification( + sequentialSpecificationCreator: CCreator, + initialStateDestructor: CDestructor, + equals: EqualsCFunction, + hashCode: HashCodeCFunction + ) { + this.sequentialSpecificationCreator = sequentialSpecificationCreator + this.sequentialSpecificationDestructor = initialStateDestructor + this.sequentialSpecificationEquals = equals + this.sequentialSpecificationHashCode = hashCode } - fun setupOperationWithoutArguments( - op: CPointer Unit>>, - functionName: String + fun setupOperation1( + op: CPointer) -> CPointer<*>>>, + seq_spec: CPointer) -> CPointer<*>>>, + result_destructor: CDestructor, + result_equals: EqualsCFunction, + result_hashCode: HashCodeCFunction, + result_toString: ToStringCFunction, + operationName: String, + useOnce: Boolean = false, ) = apply { val actorGenerator = ActorGenerator( - function = { _, _ -> - op.invoke() + function = { instance, _ -> + when (instance) { + is ConcurrentInstance -> { + ObjectWithDestructorAndEqualsAndHashcodeAndToString(op.invoke(instance.obj), result_destructor, result_equals, result_hashCode, result_toString) + } + is SequentialSpecificationInstance -> { + ObjectWithDestructorAndEqualsAndHashcodeAndToString(seq_spec.invoke(instance.obj), result_destructor, result_equals, result_hashCode, result_toString) + } + else -> { + throw RuntimeException("Internal error. instance has not expected type") + } + } }, parameterGenerators = emptyList(), - functionName = functionName, - useOnce = false, + functionName = operationName, + useOnce = useOnce, isSuspendable = false, handledExceptions = emptyList() ) actorGenerators.add(actorGenerator) } - fun setupOperation( - pGens: List>, - op: (Any, List) -> Any, - name: String = op.toString(), - handleExceptionsAsResult: List> = emptyList(), + fun setupOperation2( + arg1_gen_initial_state: CCreator, + arg1_gen_generate: CPointer) -> CPointer<*>>>, + arg1_toString: ToStringCFunction, + op: CPointer, CPointer<*>) -> CPointer<*>>>, + seq_spec: CPointer, CPointer<*>) -> CPointer<*>>>, + result_destructor: CDestructor, + result_equals: EqualsCFunction, + result_hashCode: HashCodeCFunction, + result_toString: ToStringCFunction, + operationName: String, useOnce: Boolean = false, - isSuspendable: Boolean = false ) = apply { + val arg1_paramgen = object : ParameterGenerator { + val state = arg1_gen_initial_state.invoke() + override fun generate(): ParameterGeneratorArgument { + return ParameterGeneratorArgument(arg1_gen_generate.invoke(state), arg1_toString) + } + } val actorGenerator = ActorGenerator( function = { instance, arguments -> - op(instance, arguments) + when (instance) { + is ConcurrentInstance -> { + ObjectWithDestructorAndEqualsAndHashcodeAndToString(op.invoke(instance.obj, (arguments[0] as ParameterGeneratorArgument).arg), result_destructor, result_equals, result_hashCode, result_toString) + } + is SequentialSpecificationInstance -> { + ObjectWithDestructorAndEqualsAndHashcodeAndToString(seq_spec.invoke(instance.obj, (arguments[0] as ParameterGeneratorArgument).arg), result_destructor, result_equals, result_hashCode, result_toString) + } + else -> { + throw RuntimeException("Internal error. instance has not expected type") + } + } }, - parameterGenerators = pGens, - functionName = name, + parameterGenerators = listOf(arg1_paramgen), + functionName = operationName, useOnce = useOnce, - isSuspendable = isSuspendable, - handledExceptions = handleExceptionsAsResult + isSuspendable = false, + handledExceptions = emptyList() ) actorGenerators.add(actorGenerator) } @@ -124,15 +313,17 @@ open class LincheckStressConfiguration(protected val testName: String } fun runTest() { - LinChecker.check(getTestClass(), getTestStructure(), this as StressOptions) - if(testName.isNotEmpty()) { - println("Finished test $testName") - } + val failure = checkImpl() ?: return + throw LincheckAssertionError(failure) } fun checkImpl(): LincheckFailure? { + //printErr("iterations: $iterations, invocations: $invocationsPerIteration") + if (invocationsPerIteration > 500) { + printErr("WARNING invocations count is bigger than 500, that may lead to crash") // TODO remove when bug with GC will be fixed + } val result = LinChecker(getTestClass(), getTestStructure(), this as StressOptions).checkImpl() - if(testName.isNotEmpty()) { + if (testName.isNotEmpty()) { println("Finished test $testName") } return result diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index fb97f5f06..199e74321 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -39,9 +39,6 @@ actual class StressStrategy actual constructor( private var runner: Runner init { - if(invocations > 500) { - printErr("WARNING invocations count is bigger than 500, that may lead to crash") // TODO remove when bug with GC will be fixed - } runner = ParallelThreadsRunner( strategy = this, testClass = testClass, diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 0c3a48517..8e4332f88 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -18,13 +18,28 @@ * */ -import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration +import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.IncorrectResultsFailure import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.native.concurrent.* import kotlin.test.* +class ComplexMutableClass(val i: Int, val j: String) { + + override fun equals(other: Any?): Boolean { + return if (other is ComplexMutableClass) { + i == other.i && j == other.j; + } else { + false; + } + } + + override fun toString(): String { + return "ComplexMutableClass $i $j" + } +} + class TestClass : VerifierState() { val atomicState: AtomicInt = AtomicInt(0) var regularState: Int = 0 @@ -47,6 +62,10 @@ class TestClass : VerifierState() { return regularState } + fun complexOperation(): ComplexMutableClass { + return ComplexMutableClass(239, "Hello, world!") + } + fun atomicIncrement() = atomicState.addAndGet(1) fun atomicDecrement() = atomicState.addAndGet(-1) @@ -98,6 +117,23 @@ class FirstTest { }.runTest() } + @Test + fun test_complex() { + LincheckStressConfiguration("FirstTest_3").apply { + iterations(10) + invocationsPerIteration(500) + actorsBefore(2) + threads(3) + actorsPerThread(5) + actorsAfter(2) + minimizeFailedScenario(false) + + initialState { TestClass() } + + operation(TestClass::complexOperation, "complexOperation") + }.runTest() + } + /* @Test fun test() { From c4215214a7ed934d40c4bb1606c7bcd7c2007d90 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 22 Apr 2021 18:04:34 +0300 Subject: [PATCH 41/72] Update gradle wrapper version because of problems with atomicfu --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1f3fdbc52..8cf6eb5ad 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-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From d39eed64a0d1cad777fdbe96fc6135c79a3afdb9 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Fri, 23 Apr 2021 18:19:20 +0300 Subject: [PATCH 42/72] Add cppTest gradle task. Add GTEST tests. Improve C++ API. Fix toString function. --- build.gradle.kts | 22 +++ cpp/CMakeLists.txt | 37 +++- cpp/counter_test.cpp | 130 +++++++++++++ cpp/lincheck.h | 171 ++++++++++++++++-- cpp/main.cpp | 64 ------- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 111 +++++++++--- 6 files changed, 424 insertions(+), 111 deletions(-) create mode 100644 cpp/counter_test.cpp delete mode 100644 cpp/main.cpp diff --git a/build.gradle.kts b/build.gradle.kts index d10b5abb5..424843b84 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -145,6 +145,28 @@ tasks { } } +tasks.register("cppTest") { + dependsOn("linkNative") + doLast { + exec { + workingDir("cpp") + commandLine("mkdir", "-p", "build") + } + exec { + workingDir("cpp/build") + commandLine("cmake", "..") + } + exec { + workingDir("cpp/build") + commandLine("make") + } + exec { + workingDir("cpp/build") + commandLine("ctest") + } + } +} + publishing { publications.withType { // add empty javadoc diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 252be6f31..b30005ac7 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -3,8 +3,37 @@ project(lincheck_test) set(CMAKE_CXX_STANDARD 20) -add_executable(lincheck_test main.cpp lincheck.h) +# add lincheck shared library +add_library(lincheck_library SHARED IMPORTED GLOBAL) +set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/releaseShared/libnative.so") -add_library(native SHARED IMPORTED GLOBAL) -set_target_properties(native PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/debugShared/libnative.so") -target_link_libraries(lincheck_test native) +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +enable_testing() + +add_executable( + counter_test + counter_test.cpp +) +target_link_libraries( + counter_test + gtest_main +) +target_link_libraries( + counter_test + gmock_main +) +target_link_libraries( + counter_test + lincheck_library +) + +include(GoogleTest) +gtest_discover_tests(counter_test) diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp new file mode 100644 index 000000000..6ff7a1b7f --- /dev/null +++ b/cpp/counter_test.cpp @@ -0,0 +1,130 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "lincheck.h" + +class ComplexArg { +public: + int value; + ComplexArg() { + value = 29; + } +}; + +std::string to_string(const ComplexArg &arg) { + return std::to_string(arg.value); +} + +template<> +class Lincheck::ParameterGenerator { +public: + ComplexArg generate() { + return ComplexArg(); + } +}; + +class Counter { +public: + int sharedState = 0; + std::atomic_int sharedAtomicState = 0; + + int inc() { + auto ans = ++sharedState; + return ans; + } + + int dec() { + auto ans = --sharedState; + return ans; + } + + int add(int value) { + auto ans = sharedState += value; + return ans; + } + + int double_op(int value1, ComplexArg value2) { + return 0; + } + + int atomic_inc() { + return ++sharedAtomicState; + } + + int atomic_dec() { + return --sharedAtomicState; + } + + int atomic_add(int value) { + //std::cout << "atomic_add " << value << " started" << std::endl; + auto ans = sharedAtomicState += value; + //std::cout << "atomic_add " << value << " finished" << std::endl; + return ans; + } + + Counter() { + } + + ~Counter() { + //std::cout << "SharedInstance has destructed" << std::endl; // To ensure that destructor is never called + } +}; + +template<> +struct std::hash { + std::size_t operator()(Counter const& s) const noexcept + { + return std::hash()(s.sharedAtomicState); + } +}; + +bool operator==(const Counter &a, const Counter &b) { + return a.sharedAtomicState == b.sharedAtomicState; +} + +using namespace Lincheck; +using ::testing::HasSubstr; + +TEST(CounterTest, BadInc) { + LincheckConfiguration conf; + conf.minimizeFailedScenario(false); + conf.operation("inc"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(CounterTest, BadDec) { + LincheckConfiguration conf; + conf.minimizeFailedScenario(false); + conf.operation("dec"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(CounterTest, BadAdd) { + LincheckConfiguration conf; + conf.minimizeFailedScenario(false); + conf.operation("add"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(CounterTest, GoodDoubleOp) { + LincheckConfiguration conf; + conf.operation("double_op"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(CounterTest, GoodAtomicInc) { + LincheckConfiguration conf; + conf.operation("inc"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(CounterTest, GoodAtomicDec) { + LincheckConfiguration conf; + conf.operation("dec"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(CounterTest, GoodAtomicAdd) { + LincheckConfiguration conf; + conf.operation("add"); + ASSERT_EQ(conf.runTest(false), ""); +} \ No newline at end of file diff --git a/cpp/lincheck.h b/cpp/lincheck.h index f005840ab..a5e8c8c2d 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -1,12 +1,74 @@ #pragma once #include -#include "../build/bin/native/debugShared/libnative_api.h" +#include +#include "../build/bin/native/releaseShared/libnative_api.h" extern libnative_ExportedSymbols *libnative_symbols(void); namespace Lincheck { - class UnsignedLongGen { + // Setup generic toString https://www.fluentcpp.com/2017/06/06/using-tostring-custom-types-cpp/ +// 1- detecting if std::to_string is valid on T + + template + using std_to_string_expression = decltype(std::to_string(std::declval())); + + template + constexpr bool has_std_to_string = std::experimental::is_detected(); + + +// 2- detecting if to_string is valid on T + + template + using to_string_expression = decltype(to_string(std::declval())); + + template + constexpr bool has_to_string = std::experimental::is_detected(); + + +// 3- detecting if T can be sent to an ostringstream + + template + using ostringstream_expression = decltype(std::declval() << std::declval()); + + template + constexpr bool has_ostringstream = std::experimental::is_detected(); + +// 1- std::to_string is valid on T + template, int>::type = 0> + std::string toString(T const &t) { + return std::to_string(t); + } + +// 2- std::to_string is not valid on T, but to_string is + template && has_to_string, int>::type = 0> + std::string toString(T const &t) { + return to_string(t); + } + +// 3- neither std::string nor to_string work on T, let's stream it then + template && !has_to_string && has_ostringstream, int>::type = 0> + std::string toString(T const &t) { + std::ostringstream oss; + oss << t; + return oss.str(); + } + + template + class ParameterGenerator; + + template<> + class ParameterGenerator { + std::mt19937 rnd; + public: + int generate() { + return rnd(); + } + }; + + template<> + class ParameterGenerator { std::mt19937 rnd; public: unsigned long generate() { @@ -39,7 +101,9 @@ namespace Lincheck { *equals = [](void *a, void *b) -> bool { return *(TestClass *) a == *(TestClass *) b; }; hashCode_pointer *hashCode = new hashCode_pointer(); - *hashCode = [](void *) -> int { return 0; }; + *hashCode = [](void *instance) -> int { + return std::hash()(*(SequentialSpecification *) instance); + }; lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialStateAndSequentialSpecification( configuration, @@ -88,32 +152,32 @@ namespace Lincheck { Ret *obj_b = (Ret *) b; // add type to void* return *obj_a == *obj_b; }, - (void *) (int (*)(void *)) [](void *) -> int { // Ret hashCode - return 0; + (void *) (int (*)(void *)) [](void *ret) -> int { // Ret hashCode + return std::hash()(*(Ret *) ret); }, - (void *) (int (*)(void *)) [](void *ret) -> int { // Ret toString - return (int) (*(Ret *) ret); + (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString + strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); }, operationName, useOnce ); } - template + template void operation(const char *operationName, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation2( configuration, (void *) (void *(*)()) []() -> void * { // arg1_gen_initial_state - return new Arg1Gen(); + return new ParameterGenerator(); }, (void *) (void *(*)(void *)) [](void *gen) -> void * { // arg1_gen_generate - auto *obj = (Arg1Gen *) gen; // add type to void* + auto *obj = (ParameterGenerator *) gen; // add type to void* Arg1 arg = obj->generate(); // invoke generate method return new Arg1(arg); // copy from stack to heap and return }, - (void *) (int (*)(void *)) [](void *arg) -> int { // arg1_toString - return (int) (*(Arg1 *) arg); // cast to int + + (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg1_toString + strncpy(dest, toString(*(Arg1 *) arg).c_str(), destSize); }, (void *) (void *(*)(void *, void *)) [](void *instance, void *arg1) -> void * { // operation auto *obj = (TestClass *) instance; // add type to void* @@ -137,19 +201,88 @@ namespace Lincheck { Ret *obj_b = (Ret *) b; // add type to void* return *obj_a == *obj_b; }, - (void *) (int (*)(void *)) [](void *) -> int { // Ret hashCode - return 0; + (void *) (int (*)(void *)) [](void *ret) -> int { // Ret hashCode + return std::hash()(*(Ret *) ret); + }, + (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString + strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); + }, + operationName, + useOnce + ); + } + + + template + void operation(const char *operationName, bool useOnce = false) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation3( + configuration, + (void *) (void *(*)()) []() -> void * { // arg1_gen_initial_state + return new ParameterGenerator(); + }, + (void *) (void *(*)(void *)) [](void *gen) -> void * { // arg1_gen_generate + auto *obj = (ParameterGenerator *) gen; // add type to void* + Arg1 arg = obj->generate(); // invoke generate method + return new Arg1(arg); // copy from stack to heap and return + }, + + (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg1_toString + strncpy(dest, toString(*(Arg1 *) arg).c_str(), destSize); + }, + (void *) (void *(*)()) []() -> void * { // arg2_gen_initial_state + return new ParameterGenerator(); + }, + (void *) (void *(*)(void *)) [](void *gen) -> void * { // arg2_gen_generate + auto *obj = (ParameterGenerator *) gen; // add type to void* + Arg2 arg = obj->generate(); // invoke generate method + return new Arg2(arg); // copy from stack to heap and return + }, + + (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg2_toString + strncpy(dest, toString(*(Arg2 *) arg).c_str(), destSize); + }, + (void *) (void *(*)(void *, void *, void *)) [](void *instance, void *arg1, + void *arg2) -> void * { // operation + auto *obj = (TestClass *) instance; // add type to void* + auto *a1 = (Arg1 *) arg1; + auto *a2 = (Arg2 *) arg2; + Ret res = (obj->*op)(*a1, *a2); // invoke op method + return new Ret(res); // copy from stack to heap and return + }, + (void *) (void *(*)(void *, void *, void *)) [](void *instance, + void *arg1, + void *arg2) -> void * { // sequential specification + auto *obj = (SequentialSpecification *) instance; // add type to void* + auto *a1 = (Arg1 *) arg1; + auto *a2 = (Arg2 *) arg2; + Ret res = (obj->*seq_spec)(*a1, *a2); // invoke op method + return new Ret(res); // copy from stack to heap and return + }, + (void *) (void (*)(void *)) [](void *ret) { // Ret destructor + Ret *obj = (Ret *) ret; // add type to void* + delete obj; // destructor + }, + (void *) (bool (*)(void *, void *)) [](void *a, void *b) -> bool { // Ret equals + Ret *obj_a = (Ret *) a; // add type to void* + Ret *obj_b = (Ret *) b; // add type to void* + return *obj_a == *obj_b; + }, + (void *) (int (*)(void *)) [](void *ret) -> int { // Ret hashCode + return std::hash()(*(Ret *) ret); }, - (void *) (int (*)(void *)) [](void *ret) -> int { // Ret toString - return (int) (*(Ret *) ret); + (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString + strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); }, operationName, useOnce ); } - void runTest() { - lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.runNativeTest(configuration); + std::string runTest(bool printErrorToStderr = true) { + return lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.runNativeTest( + configuration, printErrorToStderr); } }; } \ No newline at end of file diff --git a/cpp/main.cpp b/cpp/main.cpp deleted file mode 100644 index 75c500d7a..000000000 --- a/cpp/main.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include "lincheck.h" - -class Instance { -public: - int sharedState = 0; - std::atomic_int sharedAtomicState = 0; - - int inc() { - auto ans = ++sharedState; - //std::cout << "inc " << ans << std::endl; - return ans; - } - - int dec() { - auto ans = --sharedState; - //std::cout << "dec " << ans << std::endl; - return ans; - } - - int add(unsigned long value) { - auto ans = sharedState += value; - return ans; - } - - int atomic_inc() { - return ++sharedAtomicState; - } - - int atomic_dec() { - return --sharedAtomicState; - } - - int atomic_add(unsigned long value) { - auto ans = sharedAtomicState += value; - return ans; - } - - Instance() { - //std::cout << "SharedInstance has created" << std::endl; - } - - ~Instance() { - std::cout << "SharedInstance has destructed" << std::endl; // To ensure that destructor is never called - } -}; - -bool operator==(const Instance &a, const Instance &b) { - return a.sharedAtomicState == b.sharedAtomicState; -} - -int main(int argc, char **argv) { - using namespace Lincheck; - LincheckConfiguration conf; - conf.iterations(1); - conf.invocationsPerIteration(10000); - conf.minimizeFailedScenario(false); - - conf.operation("inc"); - conf.operation("dec"); - conf.operation("add"); - conf.runTest(); - return 0; -} \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 75c57f8e1..e33e58840 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -26,20 +26,28 @@ import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* -import kotlin.native.concurrent.* import kotlin.reflect.* typealias CCreator = CPointer CPointer<*>>> typealias CDestructor = CPointer) -> Unit>> typealias EqualsCFunction = CPointer, CPointer<*>) -> Boolean>> typealias HashCodeCFunction = CPointer) -> Int>> -typealias ToStringCFunction = CPointer) -> Int>> +// write first argument represented as string to second argument preallocated memory. Third argument is second's argument size +typealias ToStringCFunction = CPointer, CPointer, Int) -> Unit>> + +internal fun applyToStringFunction(pointer: CPointer<*>, toStringFunction: ToStringCFunction, maxLen: Int = 500): String { + val buf = ByteArray(maxLen) + buf.usePinned { pinned -> + toStringFunction(pointer, pinned.addressOf(0), buf.size - 1) + } + return buf.toKString() +} internal class ObjectWithDestructorAndEqualsAndHashcodeAndToString(val obj: CPointer<*>, - val destructor: CDestructor, - val equals: EqualsCFunction, - val hashCode: HashCodeCFunction, - val toString: ToStringCFunction) { + val destructor: CDestructor, + val equals: EqualsCFunction, + val hashCode: HashCodeCFunction, + val toString: ToStringCFunction) { override fun equals(other: Any?): Boolean { return if (other is ObjectWithDestructorAndEqualsAndHashcodeAndToString) { @@ -54,7 +62,7 @@ internal class ObjectWithDestructorAndEqualsAndHashcodeAndToString(val obj: CPoi } override fun toString(): String { - return toString.invoke(obj).toString() + return applyToStringFunction(obj, toString) } protected fun finalize() { @@ -66,7 +74,7 @@ internal class ObjectWithDestructorAndEqualsAndHashcodeAndToString(val obj: CPoi internal class ParameterGeneratorArgument(val arg: CPointer<*>, val toString: ToStringCFunction) { override fun toString(): String { - return toString.invoke(arg).toString() + return applyToStringFunction(arg, toString) } } @@ -79,9 +87,9 @@ internal class ConcurrentInstance(val obj: CPointer<*>, val destructor: CDestruc } internal class SequentialSpecificationInstance(val obj: CPointer<*>, - val destructor: CDestructor, - val equalsFunction: EqualsCFunction, - val hashCodeFunction: HashCodeCFunction) { + val destructor: CDestructor, + val equalsFunction: EqualsCFunction, + val hashCodeFunction: HashCodeCFunction) { override fun equals(other: Any?): Boolean { return if (other is SequentialSpecificationInstance) { equalsFunction.invoke(obj, other.obj) @@ -111,13 +119,8 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { init { // default configuration - iterations(1) - invocationsPerIteration(10000) - } - - private fun extractObjectWithEqualsAndHashcodeAndToString(pointer: CPointer<*>): ObjectWithDestructorAndEqualsAndHashcodeAndToString { - val ref = pointer.asStableRef() - return ref.get().freeze() + iterations(5) + invocationsPerIteration(500) } fun setupIterations(count: Int) { @@ -132,14 +135,16 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { minimizeFailedScenario(minimizeFailedScenario) } - fun runNativeTest() { + fun runNativeTest(printErrorToStderr: Boolean): String { if (!initialStateSet) { - printErr("Please provide initialState, skipping...") - return + val errorMessage = "Please provide initialState, skipping..." + if (printErrorToStderr) printErr(errorMessage) + return errorMessage } if (sequentialSpecificationCreator == null) { - printErr("Please provide sequentialSpecification, skipping...") - return + val errorMessage = "Please provide sequentialSpecification, skipping..." + if (printErrorToStderr) printErr(errorMessage) + return errorMessage } if (initialStateCreator != null) { // different initialState and sequentialSpecification @@ -156,7 +161,14 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { SequentialSpecificationInstance(sequentialSpecificationCreator!!.invoke(), sequentialSpecificationDestructor!!, sequentialSpecificationEquals!!, sequentialSpecificationHashCode!!) } ) - runTest() + try { + runTest() + } catch (e: LincheckAssertionError) { + val errorMessage = e.toString() + if (printErrorToStderr) printErr(errorMessage) + return errorMessage + } + return "" } fun setupInitialState( @@ -269,6 +281,57 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { ) actorGenerators.add(actorGenerator) } + + fun setupOperation3( + arg1_gen_initial_state: CCreator, + arg1_gen_generate: CPointer) -> CPointer<*>>>, + arg1_toString: ToStringCFunction, + arg2_gen_initial_state: CCreator, + arg2_gen_generate: CPointer) -> CPointer<*>>>, + arg2_toString: ToStringCFunction, + op: CPointer, CPointer<*>, CPointer<*>) -> CPointer<*>>>, + seq_spec: CPointer, CPointer<*>, CPointer<*>) -> CPointer<*>>>, + result_destructor: CDestructor, + result_equals: EqualsCFunction, + result_hashCode: HashCodeCFunction, + result_toString: ToStringCFunction, + operationName: String, + useOnce: Boolean = false, + ) = apply { + val arg1Paramgen = object : ParameterGenerator { + val state = arg1_gen_initial_state.invoke() + override fun generate(): ParameterGeneratorArgument { + return ParameterGeneratorArgument(arg1_gen_generate.invoke(state), arg1_toString) + } + } + val arg2Paramgen = object : ParameterGenerator { + val state = arg2_gen_initial_state.invoke() + override fun generate(): ParameterGeneratorArgument { + return ParameterGeneratorArgument(arg2_gen_generate.invoke(state), arg2_toString) + } + } + val actorGenerator = ActorGenerator( + function = { instance, arguments -> + when (instance) { + is ConcurrentInstance -> { + ObjectWithDestructorAndEqualsAndHashcodeAndToString(op.invoke(instance.obj, (arguments[0] as ParameterGeneratorArgument).arg, (arguments[1] as ParameterGeneratorArgument).arg), result_destructor, result_equals, result_hashCode, result_toString) + } + is SequentialSpecificationInstance -> { + ObjectWithDestructorAndEqualsAndHashcodeAndToString(seq_spec.invoke(instance.obj, (arguments[0] as ParameterGeneratorArgument).arg, (arguments[1] as ParameterGeneratorArgument).arg), result_destructor, result_equals, result_hashCode, result_toString) + } + else -> { + throw RuntimeException("Internal error. instance has not expected type") + } + } + }, + parameterGenerators = listOf(arg1Paramgen, arg2Paramgen), + functionName = operationName, + useOnce = useOnce, + isSuspendable = false, + handledExceptions = emptyList() + ) + actorGenerators.add(actorGenerator) + } } // TODO StressOptions methods cast this class to StressOptions From 7686f24af75bb7c66ae9b8fc562d3da4920d4f03 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 24 Apr 2021 05:16:57 +0300 Subject: [PATCH 43/72] Add init and finish thread functions --- .../runner/FixedActiveThreadsExecutor.kt | 11 +++++++-- .../lincheck/runner/ParallelThreadsRunner.kt | 4 +++- .../stress/StressCTestConfiguration.kt | 19 +++++++++++---- .../lincheck/strategy/stress/StressOptions.kt | 18 +++++++++++++- .../lincheck/runner/ParallelThreadsRunner.kt | 6 +++-- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 11 ++++++++- .../lincheck/runner/ParallelThreadsRunner.kt | 4 +++- .../strategy/stress/StressStrategy.kt | 24 ++++--------------- 8 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index cc1b9d1f5..5ce3a1975 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -54,7 +54,10 @@ internal expect fun currentTimeMillis(): Long * is that this executor keeps the re-using threads "hot" (active) as long as * possible, so that they should not be parked and unparked between invocations. */ -internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: Int) { +internal class FixedActiveThreadsExecutor(private val nThreads: Int, + runnerHash: Int, + private val initThreadFunction: (() -> Unit)? = null, + private val finishThreadFunction: (() -> Unit)? = null) { // Threads used in this runner. private val threads: List /** @@ -171,9 +174,13 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: } private fun testThreadRunnable(iThread: Int) = Runnable { + initThreadFunction?.invoke() loop@while (true) { val task = getTask(iThread) - if (task == SHUTDOWN) return@Runnable + if (task == SHUTDOWN) { + finishThreadFunction?.invoke() + return@Runnable + } tasks[iThread].value = null // reset task val runnable = task as Runnable try { diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 08075f0ec..8a520bf54 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -37,7 +37,9 @@ internal expect open class ParallelThreadsRunner( validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, timeoutMs: Long, // for deadlock or livelock detection - useClocks: UseClocks // specifies whether `HBClock`-s should always be used or with some probability + useClocks: UseClocks, // specifies whether `HBClock`-s should always be used or with some probability + initThreadFunction: (() -> Unit)? = null, + finishThreadFunction: (() -> Unit)? = null ) : Runner internal class LincheckExecutionException(cause: Throwable?): Exception(cause) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index 8d9c5a704..4e5c18dfd 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -28,10 +28,21 @@ import org.jetbrains.kotlinx.lincheck.verifier.* /** * Configuration for [stress][StressStrategy] strategy. */ -class StressCTestConfiguration(testClass: TestClass, iterations: Int, threads: Int, actorsPerThread: Int, actorsBefore: Int, actorsAfter: Int, - executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator, verifierGenerator: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, - val invocationsPerIteration: Int, requireStateEquivalenceCheck: Boolean, minimizeFailedScenario: Boolean, - sequentialSpecification: SequentialSpecification<*>, timeoutMs: Long +class StressCTestConfiguration(testClass: TestClass, + iterations: Int, + threads: Int, + actorsPerThread: Int, + actorsBefore: Int, + actorsAfter: Int, + executionGenerator: (testConfiguration: CTestConfiguration, testStructure: CTestStructure) -> ExecutionGenerator, + verifierGenerator: (sequentialSpecification: SequentialSpecification<*>) -> Verifier, + val invocationsPerIteration: Int, + requireStateEquivalenceCheck: Boolean, + minimizeFailedScenario: Boolean, + sequentialSpecification: SequentialSpecification<*>, + timeoutMs: Long, + val initThreadFunction: (() -> Unit)? = null, + val finishThreadFunction: (() -> Unit)? = null ) : CTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGenerator, verifierGenerator, requireStateEquivalenceCheck, minimizeFailedScenario, sequentialSpecification, timeoutMs) { override fun createStrategy(testClass: TestClass, scenario: ExecutionScenario, validationFunctions: List, diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt index 774b4c3f3..4b0e00972 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressOptions.kt @@ -26,6 +26,8 @@ import org.jetbrains.kotlinx.lincheck.* */ open class StressOptions : Options() { protected var invocationsPerIteration = StressCTestConfiguration.DEFAULT_INVOCATIONS + protected var initThreadFunction: (() -> Unit)? = null + protected var finishThreadFunction: (() -> Unit)? = null /** * Run each test scenario the specified number of times. @@ -34,9 +36,23 @@ open class StressOptions : Options() { invocationsPerIteration = invocations } + /** + * Setup init function that will be invoked once in every worker thread before operations. + */ + fun initThreadFunction(function: () -> Unit): StressOptions = apply { + initThreadFunction = function + } + + /** + * Setup finish function that will be invoked once in every worker thread after operations. + */ + fun finishThreadFunction(function: () -> Unit): StressOptions = apply { + finishThreadFunction = function + } + override fun createTestConfigurations(testClass: TestClass): StressCTestConfiguration { return StressCTestConfiguration(testClass, iterations, threads, actorsPerThread, actorsBefore, actorsAfter, executionGeneratorGenerator, verifierGenerator, invocationsPerIteration, requireStateEquivalenceImplementationCheck, minimizeFailedScenario, - chooseSequentialSpecification(sequentialSpecification, testClass), timeoutMs) + chooseSequentialSpecification(sequentialSpecification, testClass), timeoutMs, initThreadFunction, finishThreadFunction) } } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 15ff1f76d..32d16167a 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -47,10 +47,12 @@ internal actual open class ParallelThreadsRunner actual constructor( validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, private val timeoutMs: Long, // for deadlock or livelock detection - private val useClocks: UseClocks // specifies whether `HBClock`-s should always be used or with some probability + private val useClocks: UseClocks, // specifies whether `HBClock`-s should always be used or with some probability + initThreadFunction: (() -> Unit)?, + finishThreadFunction: (() -> Unit)? ) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others - private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash) // should be closed in `close()` + private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash, initThreadFunction, finishThreadFunction) // should be closed in `close()` private lateinit var testInstance: Any private lateinit var testThreadExecutions: Array diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index e33e58840..d2e5e722c 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -135,6 +135,14 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { minimizeFailedScenario(minimizeFailedScenario) } + fun setupInitThreadFunction(function: CPointer Unit>>) { + initThreadFunction { function.invoke() } + } + + fun setupFinishThreadFunction(function: CPointer Unit>>) { + finishThreadFunction { function.invoke() } + } + fun runNativeTest(printErrorToStderr: Boolean): String { if (!initialStateSet) { val errorMessage = "Please provide initialState, skipping..." @@ -520,6 +528,8 @@ open class LincheckStressConfiguration(protected val testName: String class LinChecker(private val testClass: TestClass, private val testStructure: CTestStructure, options: Options<*, *>) { private val testConfigurations: List private val reporter: Reporter + var initThreadFunction: CPointer Unit>>? = null + var finishThreadFunction: CPointer Unit>>? = null init { val logLevel = options?.logLevel ?: DEFAULT_LOG_LEVEL @@ -551,7 +561,6 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> - //printErr("Iteration $i") // TODO debug output val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(i + 1, iterations, scenario) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 3ba8c0684..81d323394 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -38,7 +38,9 @@ internal actual open class ParallelThreadsRunner actual constructor( validationFunctions: List, stateRepresentationFunction: StateRepresentationFunction?, private val timeoutMs: Long, - private val useClocks: UseClocks) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { + private val useClocks: UseClocks, + private val initThreadFunction: (() -> Unit)?, + private val finishThreadFunction: (() -> Unit)?) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others private lateinit var testInstance: Any diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 199e74321..ddf50cb61 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -36,6 +36,8 @@ actual class StressStrategy actual constructor( private val verifier: Verifier ) : Strategy(scenario) { private var invocations = testCfg.invocationsPerIteration + private var initThreadFunction = testCfg.initThreadFunction + private var finishThreadFunction = testCfg.finishThreadFunction private var runner: Runner init { @@ -45,24 +47,9 @@ actual class StressStrategy actual constructor( validationFunctions = validationFunctions, stateRepresentationFunction = stateRepresentationFunction, timeoutMs = testCfg.timeoutMs, - useClocks = UseClocks.RANDOM - ) - try { - runner.initialize() - } catch (t: Throwable) { - runner.close() - throw t - } - } - - fun reset() { - runner = ParallelThreadsRunner( - strategy = this, - testClass = testClass, - validationFunctions = validationFunctions, - stateRepresentationFunction = stateRepresentationFunction, - timeoutMs = testCfg.timeoutMs, - useClocks = UseClocks.RANDOM + useClocks = UseClocks.RANDOM, + initThreadFunction = initThreadFunction, + finishThreadFunction = finishThreadFunction ) try { runner.initialize() @@ -74,7 +61,6 @@ actual class StressStrategy actual constructor( actual override fun run(): LincheckFailure? { // Run invocations - // reset() try { for (invocation in 0 until invocations) { runner.also { From 0c59a61249c761bfe4e0f84b57d39b7b5e259518 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 24 Apr 2021 19:30:03 +0300 Subject: [PATCH 44/72] Add libcds msqueue test --- cpp/CMakeLists.txt | 32 ++++- cpp/counter_test.cpp | 8 +- cpp/libcds_msqueue_dhp_test.cpp | 118 ++++++++++++++++++ cpp/lincheck.h | 39 +++--- .../lincheck/runner/ParallelThreadsRunner.kt | 6 +- 5 files changed, 174 insertions(+), 29 deletions(-) create mode 100644 cpp/libcds_msqueue_dhp_test.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index b30005ac7..59059f910 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -5,8 +5,9 @@ set(CMAKE_CXX_STANDARD 20) # add lincheck shared library add_library(lincheck_library SHARED IMPORTED GLOBAL) -set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/releaseShared/libnative.so") +set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/debugShared/libnative.so") +# add gtest include(FetchContent) FetchContent_Declare( googletest @@ -16,24 +17,43 @@ FetchContent_Declare( set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) +# add libcds +FetchContent_Declare( + libcds + GIT_REPOSITORY https://github.com/khizmax/libcds + GIT_TAG master + GIT_SHALLOW TRUE +) + +FetchContent_GetProperties(libcds) +if(NOT libcds_POPULATED) + FetchContent_Populate(libcds) + add_subdirectory(${libcds_SOURCE_DIR} ${libcds_BINARY_DIR}) +endif() + enable_testing() add_executable( - counter_test + tests counter_test.cpp + libcds_msqueue_dhp_test.cpp ) target_link_libraries( - counter_test + tests gtest_main ) target_link_libraries( - counter_test + tests gmock_main ) target_link_libraries( - counter_test + tests lincheck_library ) +target_link_libraries( + tests + cds +) include(GoogleTest) -gtest_discover_tests(counter_test) +gtest_discover_tests(tests) diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index 6ff7a1b7f..cfb75e0e7 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -5,6 +5,7 @@ class ComplexArg { public: int value; + ComplexArg() { value = 29; } @@ -70,10 +71,9 @@ class Counter { }; template<> -struct std::hash { - std::size_t operator()(Counter const& s) const noexcept - { - return std::hash()(s.sharedAtomicState); +struct Lincheck::hash { + std::size_t operator()(Counter const &s) const noexcept { + return Lincheck::hash()(s.sharedAtomicState); } }; diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp new file mode 100644 index 000000000..14ae4c77b --- /dev/null +++ b/cpp/libcds_msqueue_dhp_test.cpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include + +std::string to_string(const std::pair &ret) { + return ret.first ? "false" : std::to_string(ret.second); +} + +#include "lincheck.h" + +using namespace Lincheck; +using ::testing::HasSubstr; + +using queue_type = cds::container::MSQueue; + +std::vector queue_to_vector(queue_type &queue) { + std::vector ans; + while (queue.dequeue_with([&ans](int &src) { ans.push_back(src); })) {} + for(auto elem : ans) { + queue.enqueue(elem); + } + return ans; +} + +template<> +struct std::hash> { + std::size_t operator()(const std::vector &v) const noexcept { + std::string s; + for(auto elem : v) { + s += std::to_string(elem) + ","; + } + return std::hash()(s); + } +}; + + +template<> +struct std::hash { + std::size_t operator()(queue_type &q) const noexcept { + return std::hash>()(queue_to_vector(q)); + } +}; + +class LibcdsQueue { +public: + queue_type queue; + + bool push(queue_type::value_type val) { + return queue.enqueue(val); + } + + std::pair pop() { + queue_type::value_type ans = 0; + bool success = queue.dequeue(ans); + return {success, ans}; + } +}; + +template<> +struct Lincheck::hash> { + std::size_t operator()(std::pair &p) const noexcept { + return p.first ? Lincheck::hash()(p.second) : -1 * Lincheck::hash()(p.second); + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(LibcdsQueue &q) const noexcept { + return Lincheck::hash>()(queue_to_vector(q.queue)); + } +}; + +bool operator==(LibcdsQueue &a, LibcdsQueue &b) { + return queue_to_vector(a.queue) == queue_to_vector(b.queue); +} + +void myAttach() { + //std::string val = "attached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; + //std::cerr << val; + cds::threading::Manager::attachThread(); +} + +void myDetach() { + //std::string val = "detached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; + //std::cerr << val; + cds::threading::Manager::detachThread(); +} + +TEST(LibcdsMSQueueTest, QueueTest) { + cds::Initialize(); + + { + cds::gc::DHP dhpGC( + 16 //dhp_init_guard_count + ); + + myAttach(); + + LincheckConfiguration conf; + conf.iterations(10); + conf.invocationsPerIteration(500); + conf.minimizeFailedScenario(false); + + conf.initThreadFunction(); + conf.finishThreadFunction(); + + conf.operation("push"); + conf.operation, &LibcdsQueue::pop, &LibcdsQueue::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); + + myDetach(); + } + + cds::Terminate(); +} diff --git a/cpp/lincheck.h b/cpp/lincheck.h index a5e8c8c2d..dd3faadcf 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -2,7 +2,7 @@ #include #include -#include "../build/bin/native/releaseShared/libnative_api.h" +#include "../build/bin/native/debugShared/libnative_api.h" extern libnative_ExportedSymbols *libnative_symbols(void); @@ -56,22 +56,17 @@ namespace Lincheck { } template - class ParameterGenerator; - - template<> - class ParameterGenerator { - std::mt19937 rnd; - public: - int generate() { - return rnd(); + struct hash { + std::size_t operator()(const type &val) const noexcept { + return std::hash()(val) % 200 - 100; } }; - template<> - class ParameterGenerator { + template + class ParameterGenerator { std::mt19937 rnd; public: - unsigned long generate() { + type generate() { return rnd(); } }; @@ -102,7 +97,7 @@ namespace Lincheck { hashCode_pointer *hashCode = new hashCode_pointer(); *hashCode = [](void *instance) -> int { - return std::hash()(*(SequentialSpecification *) instance); + return Lincheck::hash()(*(SequentialSpecification *) instance); }; lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialStateAndSequentialSpecification( @@ -129,6 +124,18 @@ namespace Lincheck { configuration, minimizeFailedScenario); } + template + void initThreadFunction() { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitThreadFunction( + configuration, (void *) (void (*)()) []() {f();}); + } + + template + void finishThreadFunction() { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupFinishThreadFunction( + configuration, (void *) (void (*)()) []() {f();}); + } + template void operation(const char *operationName, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation1( @@ -153,7 +160,7 @@ namespace Lincheck { return *obj_a == *obj_b; }, (void *) (int (*)(void *)) [](void *ret) -> int { // Ret hashCode - return std::hash()(*(Ret *) ret); + return Lincheck::hash()(*(Ret *) ret); }, (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); @@ -202,7 +209,7 @@ namespace Lincheck { return *obj_a == *obj_b; }, (void *) (int (*)(void *)) [](void *ret) -> int { // Ret hashCode - return std::hash()(*(Ret *) ret); + return Lincheck::hash()(*(Ret *) ret); }, (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); @@ -270,7 +277,7 @@ namespace Lincheck { return *obj_a == *obj_b; }, (void *) (int (*)(void *)) [](void *ret) -> int { // Ret hashCode - return std::hash()(*(Ret *) ret); + return Lincheck::hash()(*(Ret *) ret); }, (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 81d323394..189c5d030 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -39,12 +39,12 @@ internal actual open class ParallelThreadsRunner actual constructor( stateRepresentationFunction: StateRepresentationFunction?, private val timeoutMs: Long, private val useClocks: UseClocks, - private val initThreadFunction: (() -> Unit)?, - private val finishThreadFunction: (() -> Unit)?) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { + initThreadFunction: (() -> Unit)?, + finishThreadFunction: (() -> Unit)?) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others private lateinit var testInstance: Any - private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash) // should be closed in `close()` + private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash, initThreadFunction, finishThreadFunction) // should be closed in `close()` private lateinit var testThreadExecutions: Array From bd7126793d6d8e2b6dbc4820d88c40b8e7c3c3dc Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Mon, 26 Apr 2021 15:44:49 +0300 Subject: [PATCH 45/72] useless commit --- cpp/counter_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index cfb75e0e7..e0d28f3fb 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -127,4 +127,4 @@ TEST(CounterTest, GoodAtomicAdd) { LincheckConfiguration conf; conf.operation("add"); ASSERT_EQ(conf.runTest(false), ""); -} \ No newline at end of file +} From a1d7f64867a5fdd1e198d107ef2189782b247869 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 28 Apr 2021 04:31:31 +0300 Subject: [PATCH 46/72] minor fixes --- build.gradle.kts | 2 +- cpp/CMakeLists.txt | 30 ++++++++++++++ cpp/README.MD | 4 +- cpp/counter_test.cpp | 9 +++-- cpp/libcds_msqueue_dhp_test.cpp | 23 +++++------ cpp/lincheck.h | 69 +++++++-------------------------- src/native/test/FirstTest.kt | 42 -------------------- 7 files changed, 65 insertions(+), 114 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 424843b84..c8967d4fc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -162,7 +162,7 @@ tasks.register("cppTest") { } exec { workingDir("cpp/build") - commandLine("ctest") + commandLine("ctest", "--output-on-failure") } } } diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 59059f910..e6b7e85a7 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -31,6 +31,36 @@ if(NOT libcds_POPULATED) add_subdirectory(${libcds_SOURCE_DIR} ${libcds_BINARY_DIR}) endif() +# add turf for junction +FetchContent_Declare( + turf + GIT_REPOSITORY https://github.com/preshing/turf + GIT_TAG master + GIT_SHALLOW TRUE +) + +FetchContent_GetProperties(turf) +if(NOT turf_POPULATED) + FetchContent_Populate(turf) + add_subdirectory(${turf_SOURCE_DIR} ${turf_BINARY_DIR}) +endif() + +set(TURF_ROOT "${turf_SOURCE_DIR}" CACHE STRING "Path to Turf") + +# add junction +FetchContent_Declare( + junction + GIT_REPOSITORY https://github.com/Krock21rus/junction + GIT_TAG master + GIT_SHALLOW TRUE +) + +FetchContent_GetProperties(junction) +if(NOT junction_POPULATED) + FetchContent_Populate(junction) + add_subdirectory(${junction_SOURCE_DIR} ${junction_BINARY_DIR}) +endif() + enable_testing() add_executable( diff --git a/cpp/README.MD b/cpp/README.MD index 7ee1bca19..e976cf1c5 100644 --- a/cpp/README.MD +++ b/cpp/README.MD @@ -1,6 +1,6 @@ -1. In the kotlinx-lincheck directory run `./gradlew linkNative`. It generates `build/bin/native/debugShared` and `build/bin/native/releaseShared` +In the kotlinx-lincheck directory run `./gradlew linkNative`. It generates `build/bin/native/debugShared` and `build/bin/native/releaseShared` -run this as a CMake application +Run this as a CMake application 1. `mkdir build` 2. `cd build` 3. `cmake ..` diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index cfb75e0e7..659bd16b0 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -11,9 +11,12 @@ class ComplexArg { } }; -std::string to_string(const ComplexArg &arg) { - return std::to_string(arg.value); -} +template<> +struct Lincheck::to_string { + std::string operator()(const ComplexArg &arg) const noexcept { + return std::to_string(arg.value); + } +}; template<> class Lincheck::ParameterGenerator { diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp index 14ae4c77b..3045a6ac4 100644 --- a/cpp/libcds_msqueue_dhp_test.cpp +++ b/cpp/libcds_msqueue_dhp_test.cpp @@ -4,11 +4,6 @@ #include #include #include - -std::string to_string(const std::pair &ret) { - return ret.first ? "false" : std::to_string(ret.second); -} - #include "lincheck.h" using namespace Lincheck; @@ -26,7 +21,7 @@ std::vector queue_to_vector(queue_type &queue) { } template<> -struct std::hash> { +struct Lincheck::hash> { std::size_t operator()(const std::vector &v) const noexcept { std::string s; for(auto elem : v) { @@ -36,11 +31,17 @@ struct std::hash> { } }; +template<> +struct Lincheck::to_string> { + std::string operator()(const std::pair &ret) const noexcept { + return ret.first ? "{true, " + std::to_string(ret.second) + "}" : "{false, " + std::to_string(ret.second) + "}"; + } +}; template<> -struct std::hash { +struct Lincheck::hash { std::size_t operator()(queue_type &q) const noexcept { - return std::hash>()(queue_to_vector(q)); + return Lincheck::hash>()(queue_to_vector(q)); } }; @@ -79,13 +80,13 @@ bool operator==(LibcdsQueue &a, LibcdsQueue &b) { void myAttach() { //std::string val = "attached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; - //std::cerr << val; + //std::cout << val; cds::threading::Manager::attachThread(); } void myDetach() { //std::string val = "detached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; - //std::cerr << val; + //std::cout << val; cds::threading::Manager::detachThread(); } @@ -94,7 +95,7 @@ TEST(LibcdsMSQueueTest, QueueTest) { { cds::gc::DHP dhpGC( - 16 //dhp_init_guard_count + 160 //dhp_init_guard_count ); myAttach(); diff --git a/cpp/lincheck.h b/cpp/lincheck.h index dd3faadcf..d25cfad43 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -7,58 +7,17 @@ extern libnative_ExportedSymbols *libnative_symbols(void); namespace Lincheck { - // Setup generic toString https://www.fluentcpp.com/2017/06/06/using-tostring-custom-types-cpp/ -// 1- detecting if std::to_string is valid on T - - template - using std_to_string_expression = decltype(std::to_string(std::declval())); - - template - constexpr bool has_std_to_string = std::experimental::is_detected(); - - -// 2- detecting if to_string is valid on T - - template - using to_string_expression = decltype(to_string(std::declval())); - - template - constexpr bool has_to_string = std::experimental::is_detected(); - - -// 3- detecting if T can be sent to an ostringstream - - template - using ostringstream_expression = decltype(std::declval() << std::declval()); - - template - constexpr bool has_ostringstream = std::experimental::is_detected(); - -// 1- std::to_string is valid on T - template, int>::type = 0> - std::string toString(T const &t) { - return std::to_string(t); - } - -// 2- std::to_string is not valid on T, but to_string is - template && has_to_string, int>::type = 0> - std::string toString(T const &t) { - return to_string(t); - } - -// 3- neither std::string nor to_string work on T, let's stream it then - template && !has_to_string && has_ostringstream, int>::type = 0> - std::string toString(T const &t) { - std::ostringstream oss; - oss << t; - return oss.str(); - } + template + struct to_string { + std::string operator()(const type &val) const noexcept { + return std::to_string(val); + } + }; template struct hash { std::size_t operator()(const type &val) const noexcept { - return std::hash()(val) % 200 - 100; + return std::hash()(val); } }; @@ -67,7 +26,7 @@ namespace Lincheck { std::mt19937 rnd; public: type generate() { - return rnd(); + return rnd() % 200 - 100; } }; @@ -163,7 +122,7 @@ namespace Lincheck { return Lincheck::hash()(*(Ret *) ret); }, (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString - strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); + strncpy(dest, Lincheck::to_string()(*(Ret *) ret).c_str(), destSize); }, operationName, useOnce @@ -184,7 +143,7 @@ namespace Lincheck { }, (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg1_toString - strncpy(dest, toString(*(Arg1 *) arg).c_str(), destSize); + strncpy(dest, Lincheck::to_string()(*(Arg1 *) arg).c_str(), destSize); }, (void *) (void *(*)(void *, void *)) [](void *instance, void *arg1) -> void * { // operation auto *obj = (TestClass *) instance; // add type to void* @@ -212,7 +171,7 @@ namespace Lincheck { return Lincheck::hash()(*(Ret *) ret); }, (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString - strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); + strncpy(dest, Lincheck::to_string()(*(Ret *) ret).c_str(), destSize); }, operationName, useOnce @@ -236,7 +195,7 @@ namespace Lincheck { }, (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg1_toString - strncpy(dest, toString(*(Arg1 *) arg).c_str(), destSize); + strncpy(dest, Lincheck::to_string()(*(Arg1 *) arg).c_str(), destSize); }, (void *) (void *(*)()) []() -> void * { // arg2_gen_initial_state return new ParameterGenerator(); @@ -248,7 +207,7 @@ namespace Lincheck { }, (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg2_toString - strncpy(dest, toString(*(Arg2 *) arg).c_str(), destSize); + strncpy(dest, Lincheck::to_string()(*(Arg2 *) arg).c_str(), destSize); }, (void *) (void *(*)(void *, void *, void *)) [](void *instance, void *arg1, void *arg2) -> void * { // operation @@ -280,7 +239,7 @@ namespace Lincheck { return Lincheck::hash()(*(Ret *) ret); }, (void *) (void (*)(void *, char *, int)) [](void *ret, char *dest, int destSize) { // Ret toString - strncpy(dest, toString(*(Ret *) ret).c_str(), destSize); + strncpy(dest, Lincheck::to_string()(*(Ret *) ret).c_str(), destSize); }, operationName, useOnce diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 8e4332f88..bcd39c1f7 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -133,46 +133,4 @@ class FirstTest { operation(TestClass::complexOperation, "complexOperation") }.runTest() } - -/* - @Test - fun test() { - val testClass = TestClass("FirstTest") { FirstTest() } - - val actorGenerator1 = ActorGenerator( - function = { instance, arguments -> - (instance as TestClass).state.a() - }, - parameterGenerators = listOf() - ) - val actorGenerator2 = ActorGenerator( - function = { instance, arguments -> - (instance as TestClass).state.b() - }, - parameterGenerators = listOf() - ) - val actorGenerators: List = listOf(actorGenerator1, actorGenerator2) - val operationGroups: List = listOf() - val validationFunctions: List = listOf() - val stateRepresentation: StateRepresentationFunction? = null - val testStructure = CTestStructure( - actorGenerators = actorGenerators, - operationGroups = operationGroups, - validationFunctions = validationFunctions, - stateRepresentation = stateRepresentation - ) - - val options = StressOptions().run { - iterations(1) - invocationsPerIteration(50000) - actorsBefore(2) - threads(3) - actorsPerThread(2) - actorsAfter(2) - minimizeFailedScenario(false) - } - - LinChecker.check(testClass = testClass, testStructure = testStructure, options = options) - } -*/ } \ No newline at end of file From ba008199cdb7928ac4bebcd76e297df715ffe96c Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 28 Apr 2021 05:28:32 +0300 Subject: [PATCH 47/72] Add junction and torf cpp example --- cpp/CMakeLists.txt | 38 +++---------- cpp/junction/junction | 1 + cpp/junction/turf | 1 + cpp/junction_test.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++ cpp/lincheck.h | 29 +++++++--- 5 files changed, 158 insertions(+), 37 deletions(-) create mode 160000 cpp/junction/junction create mode 160000 cpp/junction/turf create mode 100644 cpp/junction_test.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e6b7e85a7..74fba0b94 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -31,35 +31,7 @@ if(NOT libcds_POPULATED) add_subdirectory(${libcds_SOURCE_DIR} ${libcds_BINARY_DIR}) endif() -# add turf for junction -FetchContent_Declare( - turf - GIT_REPOSITORY https://github.com/preshing/turf - GIT_TAG master - GIT_SHALLOW TRUE -) - -FetchContent_GetProperties(turf) -if(NOT turf_POPULATED) - FetchContent_Populate(turf) - add_subdirectory(${turf_SOURCE_DIR} ${turf_BINARY_DIR}) -endif() - -set(TURF_ROOT "${turf_SOURCE_DIR}" CACHE STRING "Path to Turf") - -# add junction -FetchContent_Declare( - junction - GIT_REPOSITORY https://github.com/Krock21rus/junction - GIT_TAG master - GIT_SHALLOW TRUE -) - -FetchContent_GetProperties(junction) -if(NOT junction_POPULATED) - FetchContent_Populate(junction) - add_subdirectory(${junction_SOURCE_DIR} ${junction_BINARY_DIR}) -endif() +add_subdirectory("junction/junction" junction) enable_testing() @@ -67,6 +39,7 @@ add_executable( tests counter_test.cpp libcds_msqueue_dhp_test.cpp + junction_test.cpp ) target_link_libraries( tests @@ -84,6 +57,13 @@ target_link_libraries( tests cds ) +target_link_libraries( + tests + junction +) + +include_directories(${JUNCTION_ALL_INCLUDE_DIRS}) +target_link_libraries(tests ${JUNCTION_ALL_LIBRARIES}) include(GoogleTest) gtest_discover_tests(tests) diff --git a/cpp/junction/junction b/cpp/junction/junction new file mode 160000 index 000000000..5ad3be7ce --- /dev/null +++ b/cpp/junction/junction @@ -0,0 +1 @@ +Subproject commit 5ad3be7ce1d3f16b9f7ed6065bbfeacd2d629a08 diff --git a/cpp/junction/turf b/cpp/junction/turf new file mode 160000 index 000000000..9ae0d4b98 --- /dev/null +++ b/cpp/junction/turf @@ -0,0 +1 @@ +Subproject commit 9ae0d4b984fa95ed5f823274b39c87ee742f6650 diff --git a/cpp/junction_test.cpp b/cpp/junction_test.cpp new file mode 100644 index 000000000..43cc09221 --- /dev/null +++ b/cpp/junction_test.cpp @@ -0,0 +1,126 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "lincheck.h" +#include + +class SequentialMap { +public: + std::unordered_map map; + + int assign(int key, int value) { + return map[key] = value; + } + + int get(int key) { + auto it = map.find(key); + if(it != map.end()) { + return it->second; + } + return 0; + } + + int exchange(int key, int value) { + auto it = map.find(key); + if(it != map.end()) { + auto ans = it->second; + it->second = value; + return ans; + } + map[key] = value; + return 0; + } + + int erase(int key) { + auto it = map.find(key); + if(it != map.end()) { + auto ans = it->second; + map.erase(key); + return ans; + } + map.erase(key); + return 0; + } +}; + +class ConcurrentMap { +public: + junction::ConcurrentMap_Grampa map; + + int assign(int key, int value) { + std::cerr << "assign" << key << ", " << value << "\n"; + auto ans = map.assign(key, value); + std::cerr << "assign2" << key << ", " << value << "\n"; + return ans; + } + + int get(int key) { + std::cerr << "get\n"; + auto mut = map.find(key); + std::cerr << "get2\n"; + auto ans = map.get(key); + std::cerr << "get3\n"; + return ans; + } + + int exchange(int key, int value) { + std::cerr << "exchange\n"; + auto ans = map.exchange(key, value); + std::cerr << "exchange2\n"; + return ans; + } + + int erase(int key) { + std::cerr << "erase\n"; + auto ans = map.erase(key); + std::cerr << "erase2\n"; + return ans; + } +}; + +template<> +struct Lincheck::hash> { + std::size_t operator()(const std::vector &v) const noexcept { + std::string s; + for(auto elem : v) { + s += std::to_string(elem) + ","; + } + return std::hash()(s); + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialMap const &s) const noexcept { + std::vector vec; + for(auto it : s.map) { + vec.push_back(it.first); + vec.push_back(it.second); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialMap &a, const SequentialMap &b) { + return a.map == b.map; +} + +using namespace Lincheck; + +void myAttach2() { + std::cerr << "myattach2\n"; + junction::DefaultQSBR.createContext(); +} + +TEST(JunctionTest, FirstTest) { + LincheckConfiguration conf; + conf.iterations(1); + conf.invocationsPerIteration(500); + conf.minimizeFailedScenario(false); + conf.initThreadFunction(); + //Not working right now, because of produce-consume(locks and waits until consumed previous value) + //conf.operation("assign"); + //conf.operation("get"); + //conf.operation("exchange"); + //conf.operation("erase"); + ASSERT_EQ(conf.runTest(false), ""); +} \ No newline at end of file diff --git a/cpp/lincheck.h b/cpp/lincheck.h index d25cfad43..cce51bc13 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -26,7 +26,7 @@ namespace Lincheck { std::mt19937 rnd; public: type generate() { - return rnd() % 200 - 100; + return rnd() % 14 - 7; } }; @@ -45,21 +45,34 @@ namespace Lincheck { public: LincheckConfiguration() { + + constructor_pointer *instance_constructor = new constructor_pointer(); + *instance_constructor = []() -> void * { return new TestClass(); }; + + destructor_pointer *instance_destructor = new destructor_pointer(); + *instance_destructor = [](void *p) { delete (TestClass *) p; }; + + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialState( + configuration, + (void *) *instance_constructor, // constructor + (void *) *instance_destructor // destructor + ); + constructor_pointer *constructor = new constructor_pointer(); - *constructor = []() -> void * { return new TestClass(); }; + *constructor = []() -> void * { return new SequentialSpecification(); }; destructor_pointer *destructor = new destructor_pointer(); - *destructor = [](void *p) { delete (TestClass *) p; }; + *destructor = [](void *p) { delete (SequentialSpecification *) p; }; equals_pointer *equals = new equals_pointer(); - *equals = [](void *a, void *b) -> bool { return *(TestClass *) a == *(TestClass *) b; }; + *equals = [](void *a, void *b) -> bool { return *(SequentialSpecification *) a == *(SequentialSpecification *) b; }; hashCode_pointer *hashCode = new hashCode_pointer(); *hashCode = [](void *instance) -> int { return Lincheck::hash()(*(SequentialSpecification *) instance); }; - lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialStateAndSequentialSpecification( + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupSequentialSpecification( configuration, (void *) *constructor, // constructor (void *) *destructor, // destructor @@ -95,7 +108,7 @@ namespace Lincheck { configuration, (void *) (void (*)()) []() {f();}); } - template + template void operation(const char *operationName, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation1( configuration, @@ -129,7 +142,7 @@ namespace Lincheck { ); } - template + template void operation(const char *operationName, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation2( configuration, @@ -180,7 +193,7 @@ namespace Lincheck { template void operation(const char *operationName, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation3( From fd32a2b51875f491c80b71c19ef496e0e7921745 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 28 Apr 2021 06:52:02 +0300 Subject: [PATCH 48/72] Add libcuckoo test, some fixes in lincheck --- cpp/CMakeLists.txt | 6 + cpp/libcuckoo_test.cpp | 113 ++++++++++++++++++ cpp/lincheck.h | 3 +- .../runner/FixedActiveThreadsExecutor.kt | 5 +- .../lincheck/runner/TestThreadExecution.kt | 1 - src/native/test/UnexpectedExceptionTest.kt | 3 + 6 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 cpp/libcuckoo_test.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 74fba0b94..632335530 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -32,6 +32,7 @@ if(NOT libcds_POPULATED) endif() add_subdirectory("junction/junction" junction) +add_subdirectory("libcuckoo/libcuckoo" libcuckoo) enable_testing() @@ -40,6 +41,7 @@ add_executable( counter_test.cpp libcds_msqueue_dhp_test.cpp junction_test.cpp + libcuckoo_test.cpp ) target_link_libraries( tests @@ -61,6 +63,10 @@ target_link_libraries( tests junction ) +target_link_libraries( + tests + libcuckoo +) include_directories(${JUNCTION_ALL_INCLUDE_DIRS}) target_link_libraries(tests ${JUNCTION_ALL_LIBRARIES}) diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp new file mode 100644 index 000000000..5a3b5f249 --- /dev/null +++ b/cpp/libcuckoo_test.cpp @@ -0,0 +1,113 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "lincheck.h" + +#include + +class SequentialMapCuckoo { +public: + std::unordered_map map; + + bool assign(int key, int value) { + //std::cerr << "seqassign " << key << ", " << value << "\n"; + auto it = map.find(key); + if (it != map.end()) { + map[key] = value; + return false; + } + map[key] = value; + return true; + } + + int get(int key) { + //std::cerr << "seqget " << key << "\n"; + auto it = map.find(key); + if (it != map.end()) { + return it->second; + } + return -239; + } + + bool erase(int key) { + //std::cerr << "seqerase " << key << "\n"; + auto it = map.find(key); + if (it != map.end()) { + map.erase(key); + return true; + } + map.erase(key); + return false; + } +}; + +class ConcurrentMapCuckoo { +public: + libcuckoo::cuckoohash_map map; + + bool assign(int key, int value) { + //std::cerr << "assign " << key << ", " << value << "\n"; + auto ans = map.insert_or_assign(key, value); + //std::cerr << "assign2 " << key << ", " << value << "\n"; + return ans; + } + + int get(int key) { + //std::cerr << "get " << key << "\n"; + try { + auto ans = map.find(key); + //std::cerr << "get2 " << key << "\n"; + return ans; + } catch (std::out_of_range &e) { + //std::cerr << "get3 " << key << "\n"; + return -239; + } + } + + bool erase(int key) { + //std::cerr << "erase " << key << "\n"; + auto ans = map.erase(key); + //std::cerr << "erase2 " << key << "\n"; + return ans; + } +}; + +template<> +struct Lincheck::hash> { + std::size_t operator()(const std::vector &v) const noexcept { + std::string s; + for (auto elem : v) { + s += std::to_string(elem) + ","; + } + return std::hash()(s); + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialMapCuckoo const &s) const noexcept { + std::vector vec; + for (auto it : s.map) { + vec.push_back(it.first); + vec.push_back(it.second); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialMapCuckoo &a, const SequentialMapCuckoo &b) { + return a.map == b.map; +} + +using namespace Lincheck; + +TEST(libcuckooTest, FirstTest) { + LincheckConfiguration conf; + conf.iterations(1); + conf.invocationsPerIteration(500); + conf.minimizeFailedScenario(false); + + conf.operation("assign"); + conf.operation("get"); + conf.operation("erase"); + ASSERT_EQ(conf.runTest(false), ""); +} \ No newline at end of file diff --git a/cpp/lincheck.h b/cpp/lincheck.h index cce51bc13..fc7fb5cdf 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "../build/bin/native/debugShared/libnative_api.h" @@ -23,7 +24,7 @@ namespace Lincheck { template class ParameterGenerator { - std::mt19937 rnd; + std::mt19937 rnd = std::mt19937(rand()); public: type generate() { return rnd() % 14 - 7; diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index e2c80b62d..b00559133 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -28,10 +28,7 @@ import kotlin.native.ThreadLocal import kotlin.native.concurrent.* import kotlin.system.* -@ThreadLocal -val currentThreadId = Any() - -val results = mutableSetOf() +val currentThreadId = Worker.current.id internal actual class TestThread actual constructor(val iThread: Int, val runnerHash: Int, val r: Runnable) { val worker: Worker = Worker.start(true, "Worker $iThread $runnerHash") diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt index 982fbe0de..bf466118f 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt @@ -52,7 +52,6 @@ class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List actors.forEachIndexed { index, actor -> //printErr("RUN $iThread #2 $index") readClocks(index) - // TODO add try-catch runner.onActorStart(iThread) // Load arguments for operation val result: Result = try { diff --git a/src/native/test/UnexpectedExceptionTest.kt b/src/native/test/UnexpectedExceptionTest.kt index b213271cf..b5f4ff75b 100644 --- a/src/native/test/UnexpectedExceptionTest.kt +++ b/src/native/test/UnexpectedExceptionTest.kt @@ -38,6 +38,9 @@ class UnexpectedExceptionTest : AbstractLincheckStressTest> T.customize() { + iterations(100) + invocationsPerIteration(500) + initialState { UnexpectedExceptionTest() } operation(UnexpectedExceptionTest::operation1) From 53edf81e9246a1efd5f692ac41e1a173a9dc0f44 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 28 Apr 2021 08:30:04 +0300 Subject: [PATCH 49/72] Add folly ConcurrentHashMap test --- .gitmodules | 3 ++ cpp/CMakeLists.txt | 39 ++++++++++++----- cpp/folly_hashmap_test.cpp | 89 ++++++++++++++++++++++++++++++++++++++ cpp/libcuckoo/libcuckoo | 1 + 4 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 .gitmodules create mode 100644 cpp/folly_hashmap_test.cpp create mode 160000 cpp/libcuckoo/libcuckoo diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c361f379a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cpp/libcuckoo/libcuckoo"] + path = cpp/libcuckoo/libcuckoo + url = https://github.com/efficient/libcuckoo diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 632335530..a1d4d16c3 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(lincheck_test) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 17) # add lincheck shared library add_library(lincheck_library SHARED IMPORTED GLOBAL) @@ -31,8 +31,22 @@ if(NOT libcds_POPULATED) add_subdirectory(${libcds_SOURCE_DIR} ${libcds_BINARY_DIR}) endif() -add_subdirectory("junction/junction" junction) -add_subdirectory("libcuckoo/libcuckoo" libcuckoo) +# add folly +FetchContent_Declare( + folly + GIT_REPOSITORY https://github.com/facebook/folly + GIT_TAG master + GIT_SHALLOW TRUE +) + +FetchContent_GetProperties(folly) +if(NOT folly_POPULATED) + FetchContent_Populate(folly) + add_subdirectory(${folly_SOURCE_DIR} ${folly_BINARY_DIR}) +endif() + +#add_subdirectory("junction/junction" junction) +#add_subdirectory("libcuckoo/libcuckoo" libcuckoo) enable_testing() @@ -40,8 +54,9 @@ add_executable( tests counter_test.cpp libcds_msqueue_dhp_test.cpp - junction_test.cpp - libcuckoo_test.cpp + #junction_test.cpp + #libcuckoo_test.cpp + folly_hashmap_test.cpp ) target_link_libraries( tests @@ -59,13 +74,17 @@ target_link_libraries( tests cds ) +#target_link_libraries( +# tests +# junction +#) +#target_link_libraries( +# tests +# libcuckoo +#) target_link_libraries( tests - junction -) -target_link_libraries( - tests - libcuckoo + folly ) include_directories(${JUNCTION_ALL_INCLUDE_DIRS}) diff --git a/cpp/folly_hashmap_test.cpp b/cpp/folly_hashmap_test.cpp new file mode 100644 index 000000000..7a5cf7e00 --- /dev/null +++ b/cpp/folly_hashmap_test.cpp @@ -0,0 +1,89 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "lincheck.h" + +#include "folly/concurrency/ConcurrentHashMap.h" + +class SequentialMapFolly { +public: + std::unordered_map map; + + bool assign(int key, int value) { + map.insert_or_assign(key, value); + return true; // concurrent version always returns true + } + + int get(int key) { + auto it = map.find(key); + if (it != map.end()) { + return it->second; + } + return -239; + } + + int erase(int key) { + return map.erase(key); + } +}; + +class ConcurrentMapFolly { +public: + folly::ConcurrentHashMap map; + + bool assign(int key, int value) { + return map.insert_or_assign(key, value).second; + } + + int get(int key) { + auto it = map.find(key); + if (it != map.end()) { + return it->second; + } + return -239; + } + + int erase(int key) { + return map.erase(key); + } +}; + +template<> +struct Lincheck::hash> { + std::size_t operator()(const std::vector &v) const noexcept { + std::string s; + for (auto elem : v) { + s += std::to_string(elem) + ","; + } + return std::hash()(s); + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialMapFolly const &s) const noexcept { + std::vector vec; + for (auto it : s.map) { + vec.push_back(it.first); + vec.push_back(it.second); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialMapFolly &a, const SequentialMapFolly &b) { + return a.map == b.map; +} + +using namespace Lincheck; + +TEST(follyHashMapTest, FirstTest) { + LincheckConfiguration conf; + conf.iterations(10); + conf.invocationsPerIteration(500); + conf.minimizeFailedScenario(false); + + conf.operation("assign"); + conf.operation("get"); + conf.operation("erase"); + ASSERT_EQ(conf.runTest(false), ""); +} \ No newline at end of file diff --git a/cpp/libcuckoo/libcuckoo b/cpp/libcuckoo/libcuckoo new file mode 160000 index 000000000..878577389 --- /dev/null +++ b/cpp/libcuckoo/libcuckoo @@ -0,0 +1 @@ +Subproject commit 8785773896d74f72b6224e59d37f5f8c3c1e022a From a96b940c9def20700b6d740a01560ea3bdfaa98b Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Fri, 30 Apr 2021 01:16:04 +0300 Subject: [PATCH 50/72] Add boost::lockfree::queue test, fix bugs in Lincheck --- cpp/CMakeLists.txt | 59 ++--- cpp/boost_lockfree_queue_test.cpp | 76 ++++++ cpp/counter_test.cpp | 14 +- cpp/folly_hashmap_test.cpp | 27 +- cpp/junction_test.cpp | 12 +- cpp/libcds_msqueue_dhp_test.cpp | 27 +- cpp/libcuckoo_test.cpp | 12 +- cpp/lincheck.h | 33 ++- cpp/lincheck_functions.h | 25 ++ .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 28 +- .../kotlinx/lincheck/execution/HBClock.kt | 18 +- .../runner/FixedActiveThreadsExecutor.kt | 250 ------------------ .../quiescent/QuiescentConsistencyVerifier.kt | 3 +- .../jetbrains/kotlinx/lincheck/Reporter.kt | 2 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 6 +- .../runner/FixedActiveThreadsExecutor.kt | 216 +++++++++++++-- .../lincheck/runner/ParallelThreadsRunner.kt | 21 +- .../strategy/managed/ManagedStrategy.kt | 2 +- .../runner/FixedActiveThreadsExecutorTest.kt | 2 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 30 ++- .../jetbrains/kotlinx/lincheck/NativeUtils.kt | 13 + .../runner/FixedActiveThreadsExecutor.kt | 119 +++++++-- .../lincheck/runner/ParallelThreadsRunner.kt | 50 ++-- .../lincheck/runner/TestThreadExecution.kt | 42 +-- src/native/test/FirstTest.kt | 19 +- 25 files changed, 628 insertions(+), 478 deletions(-) create mode 100644 cpp/boost_lockfree_queue_test.cpp create mode 100644 cpp/lincheck_functions.h diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index a1d4d16c3..2d1b7964e 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -26,10 +26,10 @@ FetchContent_Declare( ) FetchContent_GetProperties(libcds) -if(NOT libcds_POPULATED) +if (NOT libcds_POPULATED) FetchContent_Populate(libcds) add_subdirectory(${libcds_SOURCE_DIR} ${libcds_BINARY_DIR}) -endif() +endif () # add folly FetchContent_Declare( @@ -40,16 +40,20 @@ FetchContent_Declare( ) FetchContent_GetProperties(folly) -if(NOT folly_POPULATED) +if (NOT folly_POPULATED) FetchContent_Populate(folly) add_subdirectory(${folly_SOURCE_DIR} ${folly_BINARY_DIR}) -endif() +endif () + +# add boost +set(Boost_USE_STATIC_LIBS OFF) +set(Boost_USE_MULTITHREADED ON) +set(Boost_USE_STATIC_RUNTIME OFF) +find_package(Boost REQUIRED COMPONENTS thread program_options) #add_subdirectory("junction/junction" junction) #add_subdirectory("libcuckoo/libcuckoo" libcuckoo) -enable_testing() - add_executable( tests counter_test.cpp @@ -57,38 +61,25 @@ add_executable( #junction_test.cpp #libcuckoo_test.cpp folly_hashmap_test.cpp + boost_lockfree_queue_test.cpp ) -target_link_libraries( - tests - gtest_main -) -target_link_libraries( - tests - gmock_main -) -target_link_libraries( - tests - lincheck_library -) -target_link_libraries( - tests - cds -) -#target_link_libraries( -# tests -# junction -#) -#target_link_libraries( -# tests -# libcuckoo -#) -target_link_libraries( - tests - folly -) +enable_testing() + +target_link_libraries(tests gtest_main) +target_link_libraries(tests gmock_main) +target_link_libraries(tests lincheck_library) +target_link_libraries(tests cds) +#target_link_libraries(tests junction) +#target_link_libraries(tests libcuckoo) +target_link_libraries(tests folly) include_directories(${JUNCTION_ALL_INCLUDE_DIRS}) target_link_libraries(tests ${JUNCTION_ALL_LIBRARIES}) +set(Boost_USE_STATIC_LIBS OFF) +set(Boost_USE_MULTITHREADED ON) +set(Boost_USE_STATIC_RUNTIME OFF) +include_directories(${Boost_INCLUDE_DIR} ${BOOST_LOCKFREE_DIR}) +target_link_libraries(tests ${Boost_LIBRARIES}) include(GoogleTest) gtest_discover_tests(tests) diff --git a/cpp/boost_lockfree_queue_test.cpp b/cpp/boost_lockfree_queue_test.cpp new file mode 100644 index 000000000..5d748f4d5 --- /dev/null +++ b/cpp/boost_lockfree_queue_test.cpp @@ -0,0 +1,76 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "lincheck.h" +#include "lincheck_functions.h" +#include + +#include + +class SequentialQueueBoost { +public: + std::queue q; + + bool push(int value) { + q.push(value); + return true; + } + + std::pair pop() { + int val = 0; + if(!q.empty()) { + val = q.front(); + q.pop(); + return {true, val}; + } else { + return {false, val}; + } + } +}; + +class ConcurrentQueueBoost { +public: + boost::lockfree::queue q = boost::lockfree::queue(100); + + bool push(int value) { + return q.push(value); + } + + std::pair pop() { + int val = 0; + bool success = q.pop(val); + return {success, success ? val : 0}; + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialQueueBoost &s) const noexcept { + std::vector vec; + while(!s.q.empty()) { + vec.push_back(s.q.front()); + s.q.pop(); + } + for (auto it : vec) { + s.q.push(it); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialQueueBoost &a, const SequentialQueueBoost &b) { + return a.q == b.q; +} + +using namespace Lincheck; + +TEST(BoostLockfreeQueueTest, QueueTest) { + LincheckConfiguration conf; + conf.iterations(1000); + conf.invocationsPerIteration(1000); + conf.minimizeFailedScenario(false); + conf.threads(3); + + conf.operation("push"); + conf.operation, &ConcurrentQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); +} \ No newline at end of file diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index 93b89f869..391e5db7a 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -64,13 +64,6 @@ class Counter { //std::cout << "atomic_add " << value << " finished" << std::endl; return ans; } - - Counter() { - } - - ~Counter() { - //std::cout << "SharedInstance has destructed" << std::endl; // To ensure that destructor is never called - } }; template<> @@ -90,6 +83,7 @@ using ::testing::HasSubstr; TEST(CounterTest, BadInc) { LincheckConfiguration conf; conf.minimizeFailedScenario(false); + conf.threads(3); conf.operation("inc"); ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); } @@ -97,6 +91,7 @@ TEST(CounterTest, BadInc) { TEST(CounterTest, BadDec) { LincheckConfiguration conf; conf.minimizeFailedScenario(false); + conf.threads(3); conf.operation("dec"); ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); } @@ -104,30 +99,35 @@ TEST(CounterTest, BadDec) { TEST(CounterTest, BadAdd) { LincheckConfiguration conf; conf.minimizeFailedScenario(false); + conf.threads(3); conf.operation("add"); ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); } TEST(CounterTest, GoodDoubleOp) { LincheckConfiguration conf; + conf.threads(3); conf.operation("double_op"); ASSERT_EQ(conf.runTest(false), ""); } TEST(CounterTest, GoodAtomicInc) { LincheckConfiguration conf; + conf.threads(3); conf.operation("inc"); ASSERT_EQ(conf.runTest(false), ""); } TEST(CounterTest, GoodAtomicDec) { LincheckConfiguration conf; + conf.threads(3); conf.operation("dec"); ASSERT_EQ(conf.runTest(false), ""); } TEST(CounterTest, GoodAtomicAdd) { LincheckConfiguration conf; + conf.threads(3); conf.operation("add"); ASSERT_EQ(conf.runTest(false), ""); } diff --git a/cpp/folly_hashmap_test.cpp b/cpp/folly_hashmap_test.cpp index 7a5cf7e00..f62691ca4 100644 --- a/cpp/folly_hashmap_test.cpp +++ b/cpp/folly_hashmap_test.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" #include "lincheck.h" +#include "lincheck_functions.h" #include "folly/concurrency/ConcurrentHashMap.h" @@ -13,12 +14,12 @@ class SequentialMapFolly { return true; // concurrent version always returns true } - int get(int key) { + std::pair get(int key) { auto it = map.find(key); if (it != map.end()) { - return it->second; + return {true, it->second}; } - return -239; + return {false, 0}; } int erase(int key) { @@ -34,12 +35,12 @@ class ConcurrentMapFolly { return map.insert_or_assign(key, value).second; } - int get(int key) { + std::pair get(int key) { auto it = map.find(key); if (it != map.end()) { - return it->second; + return {true, it->second}; } - return -239; + return {false, 0}; } int erase(int key) { @@ -47,17 +48,6 @@ class ConcurrentMapFolly { } }; -template<> -struct Lincheck::hash> { - std::size_t operator()(const std::vector &v) const noexcept { - std::string s; - for (auto elem : v) { - s += std::to_string(elem) + ","; - } - return std::hash()(s); - } -}; - template<> struct Lincheck::hash { std::size_t operator()(SequentialMapFolly const &s) const noexcept { @@ -81,9 +71,10 @@ TEST(follyHashMapTest, FirstTest) { conf.iterations(10); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); + conf.threads(3); conf.operation("assign"); - conf.operation("get"); + conf.operation, int, &ConcurrentMapFolly::get, &SequentialMapFolly::get>("get"); conf.operation("erase"); ASSERT_EQ(conf.runTest(false), ""); } \ No newline at end of file diff --git a/cpp/junction_test.cpp b/cpp/junction_test.cpp index 43cc09221..47a26b168 100644 --- a/cpp/junction_test.cpp +++ b/cpp/junction_test.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" #include "lincheck.h" +#include "lincheck_functions.h" #include class SequentialMap { @@ -77,17 +78,6 @@ class ConcurrentMap { } }; -template<> -struct Lincheck::hash> { - std::size_t operator()(const std::vector &v) const noexcept { - std::string s; - for(auto elem : v) { - s += std::to_string(elem) + ","; - } - return std::hash()(s); - } -}; - template<> struct Lincheck::hash { std::size_t operator()(SequentialMap const &s) const noexcept { diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp index 3045a6ac4..6c7068821 100644 --- a/cpp/libcds_msqueue_dhp_test.cpp +++ b/cpp/libcds_msqueue_dhp_test.cpp @@ -5,6 +5,7 @@ #include #include #include "lincheck.h" +#include "lincheck_functions.h" using namespace Lincheck; using ::testing::HasSubstr; @@ -20,24 +21,6 @@ std::vector queue_to_vector(queue_type &queue) { return ans; } -template<> -struct Lincheck::hash> { - std::size_t operator()(const std::vector &v) const noexcept { - std::string s; - for(auto elem : v) { - s += std::to_string(elem) + ","; - } - return std::hash()(s); - } -}; - -template<> -struct Lincheck::to_string> { - std::string operator()(const std::pair &ret) const noexcept { - return ret.first ? "{true, " + std::to_string(ret.second) + "}" : "{false, " + std::to_string(ret.second) + "}"; - } -}; - template<> struct Lincheck::hash { std::size_t operator()(queue_type &q) const noexcept { @@ -60,13 +43,6 @@ class LibcdsQueue { } }; -template<> -struct Lincheck::hash> { - std::size_t operator()(std::pair &p) const noexcept { - return p.first ? Lincheck::hash()(p.second) : -1 * Lincheck::hash()(p.second); - } -}; - template<> struct Lincheck::hash { std::size_t operator()(LibcdsQueue &q) const noexcept { @@ -104,6 +80,7 @@ TEST(LibcdsMSQueueTest, QueueTest) { conf.iterations(10); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); + conf.threads(3); conf.initThreadFunction(); conf.finishThreadFunction(); diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp index 5a3b5f249..dbcb69828 100644 --- a/cpp/libcuckoo_test.cpp +++ b/cpp/libcuckoo_test.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" #include "lincheck.h" +#include "lincheck_functions.h" #include @@ -71,17 +72,6 @@ class ConcurrentMapCuckoo { } }; -template<> -struct Lincheck::hash> { - std::size_t operator()(const std::vector &v) const noexcept { - std::string s; - for (auto elem : v) { - s += std::to_string(elem) + ","; - } - return std::hash()(s); - } -}; - template<> struct Lincheck::hash { std::size_t operator()(SequentialMapCuckoo const &s) const noexcept { diff --git a/cpp/lincheck.h b/cpp/lincheck.h index fc7fb5cdf..2e0be11b7 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -27,7 +27,7 @@ namespace Lincheck { std::mt19937 rnd = std::mt19937(rand()); public: type generate() { - return rnd() % 14 - 7; + return rnd() % 16 - 5; } }; @@ -66,7 +66,9 @@ namespace Lincheck { *destructor = [](void *p) { delete (SequentialSpecification *) p; }; equals_pointer *equals = new equals_pointer(); - *equals = [](void *a, void *b) -> bool { return *(SequentialSpecification *) a == *(SequentialSpecification *) b; }; + *equals = [](void *a, void *b) -> bool { + return *(SequentialSpecification *) a == *(SequentialSpecification *) b; + }; hashCode_pointer *hashCode = new hashCode_pointer(); *hashCode = [](void *instance) -> int { @@ -97,16 +99,36 @@ namespace Lincheck { configuration, minimizeFailedScenario); } + void threads(int count) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupThreads( + configuration, count); + } + + void actorsPerThread(int count) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupActorsPerThread( + configuration, count); + } + + void actorsBefore(int count) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupActorsBefore( + configuration, count); + } + + void actorsAfter(int count) { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupActorsAfter( + configuration, count); + } + template void initThreadFunction() { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitThreadFunction( - configuration, (void *) (void (*)()) []() {f();}); + configuration, (void *) (void (*)()) []() { f(); }); } template void finishThreadFunction() { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupFinishThreadFunction( - configuration, (void *) (void (*)()) []() {f();}); + configuration, (void *) (void (*)()) []() { f(); }); } template @@ -143,7 +165,8 @@ namespace Lincheck { ); } - template + template void operation(const char *operationName, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation2( configuration, diff --git a/cpp/lincheck_functions.h b/cpp/lincheck_functions.h new file mode 100644 index 000000000..daeac9e60 --- /dev/null +++ b/cpp/lincheck_functions.h @@ -0,0 +1,25 @@ +#include "lincheck.h" +template<> +struct Lincheck::hash> { + std::size_t operator()(const std::vector &v) const noexcept { + std::string s; + for(auto elem : v) { + s += std::to_string(elem) + ","; + } + return std::hash()(s); + } +}; + +template<> +struct Lincheck::to_string> { + std::string operator()(const std::pair &ret) const noexcept { + return ret.first ? "{true, " + std::to_string(ret.second) + "}" : "{false, " + std::to_string(ret.second) + "}"; + } +}; + +template<> +struct Lincheck::hash> { + std::size_t operator()(std::pair &p) const noexcept { + return p.first ? Lincheck::hash()(p.second) : -1 * Lincheck::hash()(p.second); + } +}; \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index ab3df8d22..c56c8db25 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -21,6 +21,7 @@ */ package org.jetbrains.kotlinx.lincheck +import kotlinx.atomicfu.* import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.runner.* @@ -150,4 +151,29 @@ internal val String.internalClassName get() = this.replace('.', '/') * Collects the current thread dump and keeps only those * threads that are related to the specified [runner]. */ -internal expect fun collectThreadDump(runner: Runner): ThreadDump \ No newline at end of file +internal expect fun collectThreadDump(runner: Runner): ThreadDump + +internal expect fun nativeFreeze(any: Any) + +class LincheckAtomicArray(size: Int) { + val array = atomicArrayOfNulls(size) + init { + nativeFreeze(this) + } +} + +class LincheckAtomicIntArray(size: Int) { + val array = AtomicIntArray(size) + init { + nativeFreeze(this) + } +} + +fun LincheckAtomicIntArray.toArray(): IntArray = IntArray(this.array.size) { this.array[it].value } +fun IntArray.toLincheckAtomicIntArray(): LincheckAtomicIntArray { + val ans = LincheckAtomicIntArray(this.size) + for (i in this.indices) { + ans.array[i].value = this[i] + } + return ans +} \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt index 8a4a0366c..7e75a6b00 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt @@ -20,14 +20,14 @@ package org.jetbrains.kotlinx.lincheck.execution -import org.jetbrains.kotlinx.lincheck.Result +import org.jetbrains.kotlinx.lincheck.* -data class HBClock(val clock: IntArray) { - val threads: Int get() = clock.size - val empty: Boolean get() = clock.all { it == 0 } - operator fun get(i: Int) = clock[i] +data class HBClock(val clock: LincheckAtomicIntArray) { + val threads: Int get() = clock.array.size + val empty: Boolean get() = clock.toArray().all { it == 0 } + operator fun get(i: Int) = clock.array[i].value - override fun toString() = clock.joinToString(prefix = "[", separator = ",", postfix = "]") { + override fun toString() = clock.toArray().joinToString(prefix = "[", separator = ",", postfix = "]") { if (it == 0) "-" else "$it" } @@ -35,14 +35,14 @@ data class HBClock(val clock: IntArray) { if (this === other) return true if (other != null && this::class != other::class) return false other as HBClock - return clock.contentEquals(other.clock) + return clock.toArray().contentEquals(other.clock.toArray()) } - override fun hashCode() = clock.contentHashCode() + override fun hashCode() = clock.toArray().contentHashCode() } fun emptyClock(size: Int) = HBClock(emptyClockArray(size)) -fun emptyClockArray(size: Int) = IntArray(size) { 0 } +fun emptyClockArray(size: Int) = LincheckAtomicIntArray(size) data class ResultWithClock(val result: Result, val clockOnStart: HBClock) diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 5ce3a1975..e69de29bb 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -1,250 +0,0 @@ -/*- - * #%L - * Lincheck - * %% - * Copyright (C) 2019 - 2020 JetBrains s.r.o. - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ -package org.jetbrains.kotlinx.lincheck.runner - -import kotlinx.atomicfu.* -import kotlinx.coroutines.* -import org.jetbrains.kotlinx.lincheck.* -import org.jetbrains.kotlinx.lincheck.execution.* -import kotlin.jvm.* -import kotlin.math.* - -internal expect class TestThread(iThread: Int, runnerHash: Int, r: Runnable) { - - fun execute() - fun terminate() - - companion object { - fun currentThread(): Any? - } -} - -internal expect class LockSupport { - companion object { - fun park() - fun unpark(thread: Any?) - fun parkNanos(nanos: Long) - } -} - -internal expect fun currentTimeMillis(): Long - -/** - * This executor maintains the specified number of threads and is used by - * [ParallelThreadsRunner] to execute [ExecutionScenario]-s. The main feature - * is that this executor keeps the re-using threads "hot" (active) as long as - * possible, so that they should not be parked and unparked between invocations. - */ -internal class FixedActiveThreadsExecutor(private val nThreads: Int, - runnerHash: Int, - private val initThreadFunction: (() -> Unit)? = null, - private val finishThreadFunction: (() -> Unit)? = null) { - // Threads used in this runner. - private val threads: List - /** - * null, waiting TestThread, Runnable task, or SHUTDOWN - */ - private val tasks = atomicArrayOfNulls(nThreads) - /** - * null, waiting in [submitAndAwait] thread, DONE, or exception - */ - private val results = atomicArrayOfNulls(nThreads) - /** - * Specifies the number of loop cycles for threads - * active waiting, after that they should be parked - */ - private var spinCount = 40000 - /** - * An adaptive active waiting strategy is used for the case when - * the number of threads is greater than the number of cores. - * This flag is set to `true` when any of the threads was parked - * during the previous [submitAndAwait] call. - */ - @Volatile - private var wasParked: Boolean = false - /** - * This balance is either increased or decreased by 1 at the - * end of each invocation when [wasParked] is `true` or `false` - * correspondingly. When the balance achieves [WAS_PARK_BALANCE_THRESHOLD], - * [spinCount] is doubled, and when the balance achieves - * -[WAS_PARK_BALANCE_THRESHOLD], [spinCount] is halved. - */ - private var wasParkedBalance: Int = 0 - - init { - threads = (0 until nThreads).map { iThread -> - TestThread(iThread, runnerHash, testThreadRunnable(iThread)).also { it.execute() } - } - } - - /** - * Submits the specified set of [tasks] to this executor - * and waits until all of them are completed. - * The number of tasks should be equal to [nThreads]. - * - * @throws LincheckTimeoutException if more than [timeoutMs] is passed. - * @throws LincheckExecutionException if an unexpected exception is thrown during the execution. - */ - fun submitAndAwait(tasks: Array, timeoutMs: Long) { - require(tasks.size == nThreads) - submitTasks(tasks) - await(timeoutMs) - updateAdaptiveSpinCount() - } - - private fun submitTasks(tasks: Array) { - for (i in 0 until nThreads) { - results[i].value = null - submitTask(i, tasks[i]) - } - } - - private fun updateAdaptiveSpinCount() { - if (wasParked) { - wasParked = false - wasParkedBalance++ - if (wasParkedBalance >= WAS_PARK_BALANCE_THRESHOLD) { - spinCount /= 2 - wasParkedBalance = 0 - } - } else { - wasParkedBalance-- - if (wasParkedBalance <= -WAS_PARK_BALANCE_THRESHOLD) { - spinCount = min(spinCount * 2, MAX_SPIN_COUNT) - wasParkedBalance = 0 - } - } - } - - private fun submitTask(iThread: Int, task: Any) { - if (tasks[iThread].compareAndSet(null, task)) return - // CAS failed => a test thread is parked. - // Submit the task and unpark the waiting thread. - val thread = tasks[iThread].value - tasks[iThread].value = task - LockSupport.unpark(thread) - } - - private fun await(timeoutMs: Long) { - val deadline = currentTimeMillis() + timeoutMs - for (iThread in 0 until nThreads) - awaitTask(iThread, deadline) - } - - private fun awaitTask(iThread: Int, deadline: Long) { - val result = getResult(iThread, deadline) - // Check whether there was an exception during the execution. - if (result != DONE) throw LincheckExecutionException(result as Throwable) - } - - private fun getResult(iThread: Int, deadline: Long): Any { - // Active wait for a result during the limited number of loop cycles. - spinWait { results[iThread].value }?.let { - return it - } - // Park with timeout until the result is set or the timeout is passed. - val currentThread = TestThread.currentThread() - if (results[iThread].compareAndSet(null, TestThread.currentThread())) { - while (results[iThread].value === currentThread) { - val timeLeft = deadline - currentTimeMillis() - if (timeLeft <= 0) throw LincheckTimeoutException() - LockSupport.parkNanos(timeLeft * 1_000_000) - } - } - return results[iThread].value!! - } - - private fun testThreadRunnable(iThread: Int) = Runnable { - initThreadFunction?.invoke() - loop@while (true) { - val task = getTask(iThread) - if (task == SHUTDOWN) { - finishThreadFunction?.invoke() - return@Runnable - } - tasks[iThread].value = null // reset task - val runnable = task as Runnable - try { - runnable.run() - } catch(e: Throwable) { - setResult(iThread, wrapInvalidAccessFromUnnamedModuleExceptionWithDescription(e)) - continue@loop - } - setResult(iThread, DONE) - } - } - - private fun getTask(iThread: Int): Any { - // Active wait for a task for the limited number of loop cycles. - spinWait { tasks[iThread].value }?.let { - return it - } - // Park until a task is stored into `tasks[iThread]`. - val currentThread = TestThread.currentThread() - if (tasks[iThread].compareAndSet(null, TestThread.currentThread())) { - while (tasks[iThread].value === currentThread) { - LockSupport.park() - } - } - return tasks[iThread].value!! - } - - private fun setResult(iThread: Int, any: Any) { - if (results[iThread].compareAndSet(null, any)) return - // CAS failed => a test thread is parked. - // Set the result and unpark the waiting thread. - val thread = results[iThread].value - results[iThread].value = any - LockSupport.unpark(thread) - } - - private inline fun spinWait(getter: () -> Any?): Any? { - repeat(spinCount) { - getter()?.let { - return it - } - } - wasParked = true - return null - } - - fun close() { - // submit the shutdown task. - submitTasks(Array(nThreads) { SHUTDOWN }) - for (t in threads) t.terminate() - } - - inline fun use(block: (FixedActiveThreadsExecutor) -> R): R { - try { - return block(this) - } finally { - close() - } - } - - companion object { - private val SHUTDOWN = Any() - private val DONE = Any() - private const val MAX_SPIN_COUNT = 1_000_000 - private const val WAS_PARK_BALANCE_THRESHOLD = 20 - } -} diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt index 93a601564..5e534c2c4 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.jetbrains.kotlinx.lincheck.verifier.linearizability.* import kotlin.collections.ArrayList -import kotlin.reflect.* /** * This verifier tests for quiescent consistency. @@ -89,7 +88,7 @@ class QuiescentConsistencyVerifier(sequentialSpecification: SequentialSpecificat clockMapping[t].add(clockMapping[t][i] + 1) val c = IntArray(newThreads) { 0 } clocks[t].add(c) - parallelResults[t].add(ResultWithClock(r.result, HBClock(c))) + parallelResults[t].add(ResultWithClock(r.result, HBClock(c.toLincheckAtomicIntArray()))) } } } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt index 5affcdf8a..aec0b7a27 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Reporter.kt @@ -51,7 +51,7 @@ internal actual fun StringBuilder.appendDeadlockWithDumpFailure(failure: Deadloc appendExecutionScenario(failure.scenario) appendLine() for ((t, stackTrace) in failure.threadDump.dump) { - val threadNumber = if (t is TestThread) t.iThread.toString() else "?" + val threadNumber = if (t is FixedActiveThreadsExecutor.TestThread) t.iThread.toString() else "?" appendLine("Thread-$threadNumber:") stackTrace.map { StackTraceElement(it.className.removePrefix(TransformationClassLoader.REMAPPED_PACKAGE_CANONICAL_NAME), it.methodName, it.fileName, it.lineNumber) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 3a2a776cb..fbae80e18 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -161,7 +161,7 @@ private val cancelCompletedResultMethod = DispatchedTask::class.declaredFunction actual fun storeCancellableContinuation(cont: CancellableContinuation<*>) { val t = Thread.currentThread() - if (t is TestThread) { + if (t is FixedActiveThreadsExecutor.TestThread) { t.cont = cont } else { storedLastCancellableCont = cont @@ -241,7 +241,7 @@ private class CustomObjectInputStream(val loader: ClassLoader, inputStream: Inpu * threads that are related to the specified [runner]. */ internal actual fun collectThreadDump(runner: Runner) = ThreadDump(Thread.getAllStackTraces().filter { (t, _) -> - t is TestThread && t.runnerHash == runner.hashCode() + t is FixedActiveThreadsExecutor.TestThread && t.runnerHash == runner.hashCode() }) /** @@ -253,3 +253,5 @@ internal fun getRemapperByTransformers(classTransformers: List): R classTransformers.any { it is ManagedStrategyTransformer } -> JavaUtilRemapper() else -> null } + +internal actual fun nativeFreeze(any: Any) {} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index ee3d4df06..beb7a79f7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -1,8 +1,9 @@ -/* +/*- + * #%L * Lincheck - * - * Copyright (C) 2019 - 2021 JetBrains s.r.o. - * + * %% + * Copyright (C) 2019 - 2020 JetBrains s.r.o. + * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -15,32 +16,207 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * + * . + * #L% */ package org.jetbrains.kotlinx.lincheck.runner +import kotlinx.atomicfu.* import kotlinx.coroutines.CancellableContinuation +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.execution.* +import java.io.* import java.lang.* +import java.util.concurrent.* +import java.util.concurrent.locks.* +import kotlin.math.* -internal actual class TestThread actual constructor(val iThread: Int, val runnerHash: Int, r: Runnable) : Thread(r, "FixedActiveThreadsExecutor@$runnerHash-$iThread") { - var cont: CancellableContinuation<*>? = null +/** + * This executor maintains the specified number of threads and is used by + * [ParallelThreadsRunner] to execute [ExecutionScenario]-s. The main feature + * is that this executor keeps the re-using threads "hot" (active) as long as + * possible, so that they should not be parked and unparked between invocations. + */ +internal class FixedActiveThreadsExecutor(private val nThreads: Int, runnerHash: Int) : Closeable { + // Threads used in this runner. + private val threads: List + /** + * null, waiting TestThread, Runnable task, or SHUTDOWN + */ + private val tasks = atomicArrayOfNulls(nThreads) + /** + * null, waiting in [submitAndAwait] thread, DONE, or exception + */ + private val results = atomicArrayOfNulls(nThreads) + /** + * Specifies the number of loop cycles for threads + * active waiting, after that they should be parked + */ + private var spinCount = 40000 + /** + * An adaptive active waiting strategy is used for the case when + * the number of threads is greater than the number of cores. + * This flag is set to `true` when any of the threads was parked + * during the previous [submitAndAwait] call. + */ + @Volatile + private var wasParked: Boolean = false + /** + * This balance is either increased or decreased by 1 at the + * end of each invocation when [wasParked] is `true` or `false` + * correspondingly. When the balance achieves [WAS_PARK_BALANCE_THRESHOLD], + * [spinCount] is doubled, and when the balance achieves + * -[WAS_PARK_BALANCE_THRESHOLD], [spinCount] is halved. + */ + private var wasParkedBalance: Int = 0 - actual companion object { - actual fun currentThread(): Any? = Thread.currentThread() // For storing identifier and then call unpark() + init { + threads = (0 until nThreads).map { iThread -> + TestThread(iThread, runnerHash, testThreadRunnable(iThread)).also { it.start() } + } } - actual fun execute() = start() - actual fun terminate() { - stop() + /** + * Submits the specified set of [tasks] to this executor + * and waits until all of them are completed. + * The number of tasks should be equal to [nThreads]. + * + * @throws LincheckTimeoutException if more than [timeoutMs] is passed. + * @throws LincheckExecutionException if an unexpected exception is thrown during the execution. + */ + fun submitAndAwait(tasks: Array, timeoutMs: Long) { + require(tasks.size == nThreads) + submitTasks(tasks) + await(timeoutMs) + updateAdaptiveSpinCount() } -} -internal actual class LockSupport { - actual companion object { - actual fun park() = java.util.concurrent.locks.LockSupport.park() - actual fun unpark(thread: Any?) = java.util.concurrent.locks.LockSupport.unpark(thread as Thread) - actual fun parkNanos(nanos: Long) = java.util.concurrent.locks.LockSupport.parkNanos(nanos) + private fun submitTasks(tasks: Array) { + for (i in 0 until nThreads) { + results[i].value = null + submitTask(i, tasks[i]) + } + } + + private fun updateAdaptiveSpinCount() { + if (wasParked) { + wasParked = false + wasParkedBalance++ + if (wasParkedBalance >= WAS_PARK_BALANCE_THRESHOLD) { + spinCount /= 2 + wasParkedBalance = 0 + } + } else { + wasParkedBalance-- + if (wasParkedBalance <= -WAS_PARK_BALANCE_THRESHOLD) { + spinCount = min(spinCount * 2, MAX_SPIN_COUNT) + wasParkedBalance = 0 + } + } + } + + private fun submitTask(iThread: Int, task: Any) { + if (tasks[iThread].compareAndSet(null, task)) return + // CAS failed => a test thread is parked. + // Submit the task and unpark the waiting thread. + val thread = tasks[iThread].value as TestThread + tasks[iThread].value = task + LockSupport.unpark(thread) + } + + private fun await(timeoutMs: Long) { + val deadline = System.currentTimeMillis() + timeoutMs + for (iThread in 0 until nThreads) + awaitTask(iThread, deadline) + } + + private fun awaitTask(iThread: Int, deadline: Long) { + val result = getResult(iThread, deadline) + // Check whether there was an exception during the execution. + if (result != DONE) throw LincheckExecutionException(result as Throwable) + } + + private fun getResult(iThread: Int, deadline: Long): Any { + // Active wait for a result during the limited number of loop cycles. + spinWait { results[iThread].value }?.let { + return it + } + // Park with timeout until the result is set or the timeout is passed. + val currentThread = Thread.currentThread() + if (results[iThread].compareAndSet(null, Thread.currentThread())) { + while (results[iThread].value === currentThread) { + val timeLeft = deadline - System.currentTimeMillis() + if (timeLeft <= 0) throw LincheckTimeoutException() + LockSupport.parkNanos(timeLeft * 1_000_000) + } + } + return results[iThread].value!! } -} -internal actual fun currentTimeMillis() = System.currentTimeMillis() \ No newline at end of file + private fun testThreadRunnable(iThread: Int) = Runnable { + loop@while (true) { + val task = getTask(iThread) + if (task == SHUTDOWN) return@Runnable + tasks[iThread].value = null // reset task + val runnable = task as Runnable + try { + runnable.run() + } catch(e: Throwable) { + setResult(iThread, wrapInvalidAccessFromUnnamedModuleExceptionWithDescription(e)) + continue@loop + } + setResult(iThread, DONE) + } + } + + private fun getTask(iThread: Int): Any { + // Active wait for a task for the limited number of loop cycles. + spinWait { tasks[iThread].value }?.let { + return it + } + // Park until a task is stored into `tasks[iThread]`. + val currentThread = Thread.currentThread() + if (tasks[iThread].compareAndSet(null, Thread.currentThread())) { + while (tasks[iThread].value === currentThread) { + LockSupport.park() + } + } + return tasks[iThread].value!! + } + + private fun setResult(iThread: Int, any: Any) { + if (results[iThread].compareAndSet(null, any)) return + // CAS failed => a test thread is parked. + // Set the result and unpark the waiting thread. + val thread = results[iThread].value as Thread + results[iThread].value = any + LockSupport.unpark(thread) + } + + private inline fun spinWait(getter: () -> Any?): Any? { + repeat(spinCount) { + getter()?.let { + return it + } + } + wasParked = true + return null + } + + override fun close() { + // submit the shutdown task. + submitTasks(Array(nThreads) { SHUTDOWN }) + for (t in threads) t.stop() + } + + class TestThread(val iThread: Int, val runnerHash: Int, r: Runnable) : Thread(r, "FixedActiveThreadsExecutor@$runnerHash-$iThread") { + var cont: CancellableContinuation<*>? = null + } + + companion object { + private val SHUTDOWN = Any() + private val DONE = Any() + private const val MAX_SPIN_COUNT = 1_000_000 + private const val WAS_PARK_BALANCE_THRESHOLD = 20 + } +} diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 32d16167a..3b068eaf6 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -1,8 +1,9 @@ -/* +/*- + * #%L * Lincheck - * - * Copyright (C) 2019 - 2021 JetBrains s.r.o. - * + * %% + * Copyright (C) 2019 JetBrains s.r.o. + * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the @@ -15,18 +16,20 @@ * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see - * + * . + * #L% */ - package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.CancellationResult.* import org.jetbrains.kotlinx.lincheck.execution.* +import org.jetbrains.kotlinx.lincheck.runner.FixedActiveThreadsExecutor.TestThread import org.jetbrains.kotlinx.lincheck.runner.UseClocks.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.objectweb.asm.* +import java.lang.reflect.* import java.util.concurrent.* import java.util.concurrent.atomic.* import kotlin.coroutines.* @@ -52,7 +55,7 @@ internal actual open class ParallelThreadsRunner actual constructor( finishThreadFunction: (() -> Unit)? ) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others - private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash, initThreadFunction, finishThreadFunction) // should be closed in `close()` + private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash) // should be closed in `close()` private lateinit var testInstance: Any private lateinit var testThreadExecutions: Array @@ -149,7 +152,7 @@ internal actual open class ParallelThreadsRunner actual constructor( val actors = scenario.parallelExecution[t].size ex.useClocks = if (useClocks == ALWAYS) true else Random.nextBoolean() ex.curClock = 0 - ex.clocks = Array(actors) { emptyClockArray(threads) } + ex.clocks = Array(actors) { emptyClockArray(threads).toArray() } ex.results = arrayOfNulls(actors) } suspensionPointResults.forEach { it.fill(NoResult) } @@ -267,7 +270,7 @@ internal actual open class ParallelThreadsRunner actual constructor( return UnexpectedExceptionInvocationResult(e.cause!!) } val parallelResultsWithClock = testThreadExecutions.map { ex -> - ex.results.zip(ex.clocks).map { ResultWithClock(it.first, HBClock(it.second)) } + ex.results.zip(ex.clocks).map { ResultWithClock(it.first, HBClock(it.second.toLincheckAtomicIntArray())) } } executeValidationFunctions(testInstance, validationFunctions) { functionName, exception -> val s = ExecutionScenario( diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index b283278aa..e04915ae5 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -681,7 +681,7 @@ abstract class ManagedStrategy( */ fun currentThreadNumber(): Int { val t = Thread.currentThread() - return if (t is TestThread) { + return if (t is FixedActiveThreadsExecutor.TestThread) { t.iThread } else { nThreads diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt index c5ceda126..c078cbdc9 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck/test/runner/FixedActiveThreadsExecutorTest.kt @@ -81,7 +81,7 @@ class FixedActiveThreadsExecutorTest { FixedActiveThreadsExecutor(2, uniqueRunnerHash).close() while (true) { // check that all test threads are finished - if (Thread.getAllStackTraces().keys.all { it !is TestThread || it.runnerHash != uniqueRunnerHash }) + if (Thread.getAllStackTraces().keys.all { it !is FixedActiveThreadsExecutor.TestThread || it.runnerHash != uniqueRunnerHash }) return } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index d2e5e722c..af276ef0e 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -119,16 +119,38 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { init { // default configuration - iterations(5) + iterations(10) invocationsPerIteration(500) } + fun setupInvocationsPerIteration(count: Int) { + invocationsPerIteration(count) + } + fun setupIterations(count: Int) { iterations(count) } - fun setupInvocationsPerIteration(count: Int) { - invocationsPerIteration(count) + fun setupThreads(count: Int) { + threads(count) + } + + fun setupActorsPerThread(count: Int) { + actorsPerThread(count) + } + + fun setupActorsBefore(count: Int) { + actorsBefore(count) + } + + fun setupActorsAfter(count: Int) { + actorsAfter(count) + } + + // executionGenerator + // verifier + fun setupRequireStateEquivalenceImplCheck(require: Boolean) { + requireStateEquivalenceImplCheck(require) } fun setupMinimizeFailedScenario(minimizeFailedScenario: Boolean) { @@ -355,7 +377,6 @@ open class LincheckStressConfiguration(protected val testName: String verifier(verifier: (sequentialSpecification: SequentialSpecification<*>) -> Verifier) requireStateEquivalenceImplCheck minimizeFailedScenario - createTestConfigurations logLevel(logLevel: LoggingLevel) sequentialSpecification(clazz: SequentialSpecification<*>?) */ @@ -561,6 +582,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> + println(i) val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(i + 1, iterations, scenario) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt index b309b33b3..af430ded8 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* import platform.posix.* import kotlin.coroutines.* +import kotlin.native.concurrent.* import kotlin.reflect.* actual class TestClass( @@ -125,4 +126,16 @@ val STDERR = platform.posix.fdopen(2, "w") fun printErr(message: String) { fprintf(STDERR, message + "\n") fflush(STDERR) +} + +internal actual fun nativeFreeze(any: Any): Unit { + any.freeze() +} +inline fun LincheckAtomicArray.toArray(): Array = Array(this.array.size) { this.array[it].value!! } +fun Array.toLincheckAtomicArray(): LincheckAtomicArray { + val ans = LincheckAtomicArray(this.size) + for (i in this.indices) { + ans.array[i].value = this[i] + } + return ans } \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index b00559133..97a4b52da 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -24,25 +24,33 @@ import kotlin.native.concurrent.* import kotlinx.cinterop.* import platform.posix.* import kotlinx.coroutines.* +import org.jetbrains.kotlinx.lincheck.* +import kotlin.math.* import kotlin.native.ThreadLocal -import kotlin.native.concurrent.* +import kotlin.native.internal.test.* import kotlin.system.* -val currentThreadId = Worker.current.id +@ThreadLocal +internal val currentThreadId = Any() + +internal class TestThread constructor(val iThread: Int, runnerHash: Int) { + val worker = AtomicReference(Worker.start(true, "Worker $iThread $runnerHash")) + val runnableFuture = AtomicReference?>(null) -internal actual class TestThread actual constructor(val iThread: Int, val runnerHash: Int, val r: Runnable) { - val worker: Worker = Worker.start(true, "Worker $iThread $runnerHash") - var runnableFuture: Future? = null + fun executeTask(r: () -> Any) { + runnableFuture.value = worker.value.execute(TransferMode.UNSAFE, { r }, { it.invoke() }) + } - actual fun execute() { - //printErr("start() $iThread called") - runnableFuture = worker.execute(TransferMode.UNSAFE, { r }, { r -> - r.run() - r - }) + fun awaitLastTask(deadline: Long): Any { + while(deadline - currentTimeMillis() > 0) { + if (runnableFuture.value!!.state.value == FutureState.COMPUTED.value) { + return runnableFuture.value!!.result + } + } + return FixedActiveThreadsExecutor.TIMEOUT } - actual fun terminate() { + fun terminate() { // Don't want to terminate threads because of GC. Don't want to wait for result because of hanging //printErr("stop() $iThread called") @@ -55,26 +63,87 @@ internal actual class TestThread actual constructor(val iThread: Int, val runner //return res //printErr("stop() $iThread finished") } +} + +internal fun currentTimeMillis() = getTimeMillis() + +/** + * This executor maintains the specified number of threads and is used by + * [ParallelThreadsRunner] to execute [ExecutionScenario]-s. The main feature + * is that this executor keeps the re-using threads "hot" (active) as long as + * possible, so that they should not be parked and unparked between invocations. + */ +internal class FixedActiveThreadsExecutor(private val nThreads: Int, + runnerHash: Int, + private val initThreadFunction: (() -> Unit)? = null, + private val finishThreadFunction: (() -> Unit)? = null) { + // Threads used in this runner. + private val threads: LincheckAtomicArray = LincheckAtomicArray(nThreads) + + init { + (0 until nThreads).forEach { iThread -> + threads.array[iThread].value = TestThread(iThread, runnerHash).also { it.executeTask { initThreadFunction?.invoke(); Any() } } + } + } - actual companion object { - actual fun currentThread(): Any? = currentThreadId + /** + * Submits the specified set of [tasks] to this executor + * and waits until all of them are completed. + * The number of tasks should be equal to [nThreads]. + * + * @throws LincheckTimeoutException if more than [timeoutMs] is passed. + * @throws LincheckExecutionException if an unexpected exception is thrown during the execution. + */ + fun submitAndAwait(tasks: Array, timeoutMs: Long) { + require(tasks.size == nThreads) + submitTasks(tasks) + await(timeoutMs) } -} -internal actual class LockSupport { - actual companion object { - actual fun park() { - //usleep(1000.toUInt()) // 1ms + private fun submitTasks(tasks: Array) { + for (i in 0 until nThreads) { + submitTask(i, tasks[i]) } + } + + private fun submitTask(iThread: Int, task: Any) { + threads.array[iThread].value!!.executeTask { testThreadRunnable(iThread, task as Runnable) } + } - actual fun unpark(thread: Any?) { + private fun await(timeoutMs: Long) { + val deadline = currentTimeMillis() + timeoutMs + for (iThread in 0 until nThreads) + awaitTask(iThread, deadline) + } + + private fun awaitTask(iThread: Int, deadline: Long) { + val result = threads.array[iThread].value!!.awaitLastTask(deadline) + if (result == TIMEOUT) throw LincheckTimeoutException() + // Check whether there was an exception during the execution. + if (result != DONE) throw LincheckExecutionException(result as Throwable) + } + + private fun testThreadRunnable(iThread: Int, task: Runnable): Any { + initThreadFunction?.invoke() + val runnable = task + try { + runnable.run() + } catch (e: Throwable) { + return wrapInvalidAccessFromUnnamedModuleExceptionWithDescription(e) } + return DONE + } - actual fun parkNanos(nanos: Long) { - //usleep(1000.toUInt()) // 1ms - //platform.posix.sleep((nanos / 1_000_000_000).toUInt()) + fun close() { + // submit the shutdown task. + for (t in threads.toArray()) { + t.executeTask { finishThreadFunction?.invoke(); Any() } + t.terminate() } } -} -internal actual fun currentTimeMillis() = getTimeMillis() \ No newline at end of file + companion object { + internal val TIMEOUT = Any().freeze() + private val DONE = Any() + } +} diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 189c5d030..12ba7991c 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -20,10 +20,12 @@ package org.jetbrains.kotlinx.lincheck.runner +import kotlinx.cinterop.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* import kotlin.coroutines.* +import kotlin.native.concurrent.* import kotlin.random.* /** @@ -43,38 +45,46 @@ internal actual open class ParallelThreadsRunner actual constructor( finishThreadFunction: (() -> Unit)?) : Runner(strategy, testClass, validationFunctions, stateRepresentationFunction) { private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others - private lateinit var testInstance: Any - private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash, initThreadFunction, finishThreadFunction) // should be closed in `close()` + private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash, initThreadFunction, finishThreadFunction).freeze() // should be closed in `close()` - private lateinit var testThreadExecutions: Array + private val testThreadExecutions = AtomicReference>(LincheckAtomicArray(0)) + + init { + this.ensureNeverFrozen() + } override fun initialize() { super.initialize() - testThreadExecutions = Array(scenario.threads) { t -> - TestThreadExecution(this, t, scenario.parallelExecution[t]) + val arr = Array(scenario.threads) { t -> + TestThreadExecution(t, scenario.parallelExecution[t]).freeze() } - testThreadExecutions.forEach { it.allThreadExecutions = testThreadExecutions } + testThreadExecutions.value = arr.toLincheckAtomicArray() + testThreadExecutions.value.toArray().forEach { it.allThreadExecutions.value = testThreadExecutions.value } } private fun reset() { - testInstance = testClass.createInstance() - testThreadExecutions.forEachIndexed { t, ex -> - ex.testInstance = testInstance + testThreadExecutions.value.toArray().forEachIndexed { t, ex -> val threads = scenario.threads val actors = scenario.parallelExecution[t].size - ex.useClocks = if (useClocks == UseClocks.ALWAYS) true else Random.nextBoolean() - ex.curClock = 0 - ex.clocks = Array(actors) { emptyClockArray(threads) } - ex.results = arrayOfNulls(actors) + ex.useClocks.value = if (useClocks == UseClocks.ALWAYS) 1 else (if (Random.nextBoolean()) 1 else 0) + ex.curClock.value = 0 + ex.clocks.value = Array(actors) { emptyClockArray(threads) }.toLincheckAtomicArray() + ex.results.value = LincheckAtomicArray(actors) } completedOrSuspendedThreads.set(0) } - override fun constructStateRepresentation() = + override fun constructStateRepresentation(): String? { + throw RuntimeException("should not be called") + } + + fun nativeConstructStateRepresentation(testInstance: Any) = stateRepresentationFunction?.function?.invoke(testInstance) as String? override fun run(): InvocationResult { reset() + val testInstance = testClass.createInstance() + testInstance.ensureNeverFrozen() val initResults = scenario.initExecution.mapIndexed { i, initActor -> executeActor(testInstance, initActor).also { executeValidationFunctions(testInstance, validationFunctions) { functionName, exception -> @@ -87,17 +97,17 @@ internal actual open class ParallelThreadsRunner actual constructor( } } } - val afterInitStateRepresentation = constructStateRepresentation() + val afterInitStateRepresentation = nativeConstructStateRepresentation(testInstance) try { - executor.submitAndAwait(testThreadExecutions, timeoutMs) + executor.submitAndAwait(testThreadExecutions.value.toArray().map { NativeTestThreadExecution(testInstance, it) }.toTypedArray(), timeoutMs) } catch (e: LincheckTimeoutException) { val threadDump = collectThreadDump(this) return DeadlockInvocationResult(threadDump) } catch (e: LincheckExecutionException) { return UnexpectedExceptionInvocationResult(e.cause!!) } - val parallelResultsWithClock = testThreadExecutions.map { ex -> - ex.results.zip(ex.clocks).map { ResultWithClock(it.first!!, HBClock(it.second)) } + val parallelResultsWithClock = testThreadExecutions.value.toArray().map { ex -> + ex.results.value.toArray().zip(ex.clocks.value.toArray()).map { ResultWithClock(it.first!!, HBClock(it.second)) } } executeValidationFunctions(testInstance, validationFunctions) { functionName, exception -> val s = ExecutionScenario( @@ -107,7 +117,7 @@ internal actual open class ParallelThreadsRunner actual constructor( ) return ValidationFailureInvocationResult(s, functionName, exception) } - val afterParallelStateRepresentation = constructStateRepresentation() + val afterParallelStateRepresentation = nativeConstructStateRepresentation(testInstance) val dummyCompletion = Continuation(EmptyCoroutineContext) {} var postPartSuspended = false val postResults = scenario.postExecution.mapIndexed { i, postActor -> @@ -130,7 +140,7 @@ internal actual open class ParallelThreadsRunner actual constructor( } result } - val afterPostStateRepresentation = constructStateRepresentation() + val afterPostStateRepresentation = nativeConstructStateRepresentation(testInstance) val results = ExecutionResult( initResults, afterInitStateRepresentation, parallelResultsWithClock, afterParallelStateRepresentation, diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt index bf466118f..0e518c616 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt @@ -22,37 +22,43 @@ package org.jetbrains.kotlinx.lincheck.runner import kotlinx.coroutines.* import org.jetbrains.kotlinx.lincheck.* +import kotlin.native.concurrent.* +import kotlin.native.concurrent.AtomicInt import kotlin.reflect.* -class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List) : Runnable { - lateinit var testInstance: Any - lateinit var objArgs: Array - lateinit var allThreadExecutions: Array +class NativeTestThreadExecution(val testInstance: Any, val threadExecution: TestThreadExecution) : Runnable { + override fun run() { + threadExecution.run(testInstance) + } +} - lateinit var results: Array // for ExecutionResult - lateinit var clocks: Array // for HBClock +class TestThreadExecution(val iThread: Int, val actors: List) { + val allThreadExecutions = AtomicReference>(LincheckAtomicArray(0)) - // TODO should be volatilie - var curClock = 0 - var useClocks = false + val results = AtomicReference>(LincheckAtomicArray(0)) // for ExecutionResult + val clocks = AtomicReference>(LincheckAtomicArray(0)) // for HBClock + + var curClock = AtomicInt(0) + var useClocks = AtomicInt(0) // 0 -- false, 1 -- true fun readClocks(currentActor: Int) { - for (i in allThreadExecutions.indices) { - clocks[currentActor][i] = allThreadExecutions[i].curClock + val arr = allThreadExecutions.value.toArray() + for (i in arr.indices) { + clocks.value.array[currentActor].value!!.array[i].value = arr[i].curClock.value } } fun incClock() { - curClock++ + curClock.increment() } - override fun run() { + fun run(testInstance: Any) { //printErr("RUN $iThread #1") - runner.onStart(iThread) + //runner.onStart(iThread) actors.forEachIndexed { index, actor -> //printErr("RUN $iThread #2 $index") readClocks(index) - runner.onActorStart(iThread) + //runner.onActorStart(iThread) // Load arguments for operation val result: Result = try { val r = actor.function(testInstance, actor.arguments) @@ -64,14 +70,14 @@ class TestThreadExecution(val runner: Runner, val iThread: Int, val actors: List ExceptionResult(e::class, false) } else { //printErr("FailureResult with $e") - runner.onFailure(iThread, e) + //runner.onFailure(iThread, e) throw e } } - results[index] = result + results.value.array[index].value = result incClock() } //printErr("RUN $iThread #finish ") - runner.onFinish(iThread) + //runner.onFinish(iThread) } } diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index bcd39c1f7..77f0c9bb2 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -117,15 +117,26 @@ class FirstTest { }.runTest() } + @Test + fun test_many_threads() { + LincheckStressConfiguration("FirstTest_3").apply { + iterations(2) + invocationsPerIteration(500) + threads(8) + minimizeFailedScenario(false) + + initialState { TestClass() } + + operation(TestClass::atomicIncrement, "atomicIncrement") + }.runTest() + } + @Test fun test_complex() { LincheckStressConfiguration("FirstTest_3").apply { iterations(10) invocationsPerIteration(500) - actorsBefore(2) - threads(3) - actorsPerThread(5) - actorsAfter(2) + threads(4) minimizeFailedScenario(false) initialState { TestClass() } From c5163ab0072b50ec2bcb1b15c4bfad7c8983dd47 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Fri, 30 Apr 2021 02:19:09 +0300 Subject: [PATCH 51/72] enable libcuckoo_test --- cpp/CMakeLists.txt | 6 +++--- cpp/boost_lockfree_queue_test.cpp | 7 ++++--- cpp/counter_test.cpp | 3 +++ cpp/folly_hashmap_test.cpp | 3 ++- cpp/libcds_msqueue_dhp_test.cpp | 3 ++- cpp/libcuckoo_test.cpp | 6 ++++-- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 2d1b7964e..7e51bc926 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -52,14 +52,14 @@ set(Boost_USE_STATIC_RUNTIME OFF) find_package(Boost REQUIRED COMPONENTS thread program_options) #add_subdirectory("junction/junction" junction) -#add_subdirectory("libcuckoo/libcuckoo" libcuckoo) +add_subdirectory("libcuckoo/libcuckoo" libcuckoo) add_executable( tests counter_test.cpp libcds_msqueue_dhp_test.cpp #junction_test.cpp - #libcuckoo_test.cpp + libcuckoo_test.cpp folly_hashmap_test.cpp boost_lockfree_queue_test.cpp ) @@ -71,7 +71,7 @@ target_link_libraries(tests gmock_main) target_link_libraries(tests lincheck_library) target_link_libraries(tests cds) #target_link_libraries(tests junction) -#target_link_libraries(tests libcuckoo) +target_link_libraries(tests libcuckoo) target_link_libraries(tests folly) include_directories(${JUNCTION_ALL_INCLUDE_DIRS}) target_link_libraries(tests ${JUNCTION_ALL_LIBRARIES}) diff --git a/cpp/boost_lockfree_queue_test.cpp b/cpp/boost_lockfree_queue_test.cpp index 5d748f4d5..6c069649d 100644 --- a/cpp/boost_lockfree_queue_test.cpp +++ b/cpp/boost_lockfree_queue_test.cpp @@ -65,10 +65,11 @@ using namespace Lincheck; TEST(BoostLockfreeQueueTest, QueueTest) { LincheckConfiguration conf; - conf.iterations(1000); - conf.invocationsPerIteration(1000); + conf.iterations(10000); + conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(3); + conf.threads(4); + conf.actorsPerThread(7); conf.operation("push"); conf.operation, &ConcurrentQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index 391e5db7a..e260ccfbc 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -84,6 +84,9 @@ TEST(CounterTest, BadInc) { LincheckConfiguration conf; conf.minimizeFailedScenario(false); conf.threads(3); + conf.iterations(100); + conf.invocationsPerIteration(500); + conf.actorsPerThread(8); conf.operation("inc"); ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); } diff --git a/cpp/folly_hashmap_test.cpp b/cpp/folly_hashmap_test.cpp index f62691ca4..0469e20a8 100644 --- a/cpp/folly_hashmap_test.cpp +++ b/cpp/folly_hashmap_test.cpp @@ -71,7 +71,8 @@ TEST(follyHashMapTest, FirstTest) { conf.iterations(10); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(3); + conf.threads(4); + conf.actorsPerThread(7); conf.operation("assign"); conf.operation, int, &ConcurrentMapFolly::get, &SequentialMapFolly::get>("get"); diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp index 6c7068821..a35eb4d9a 100644 --- a/cpp/libcds_msqueue_dhp_test.cpp +++ b/cpp/libcds_msqueue_dhp_test.cpp @@ -80,7 +80,8 @@ TEST(LibcdsMSQueueTest, QueueTest) { conf.iterations(10); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(3); + conf.threads(4); + conf.actorsPerThread(7); conf.initThreadFunction(); conf.finishThreadFunction(); diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp index dbcb69828..172d304da 100644 --- a/cpp/libcuckoo_test.cpp +++ b/cpp/libcuckoo_test.cpp @@ -92,9 +92,11 @@ using namespace Lincheck; TEST(libcuckooTest, FirstTest) { LincheckConfiguration conf; - conf.iterations(1); + conf.iterations(2); conf.invocationsPerIteration(500); - conf.minimizeFailedScenario(false); + conf.minimizeFailedScenario(false); + conf.threads(2); + conf.actorsPerThread(3); conf.operation("assign"); conf.operation("get"); From 962fb29e558aa7e5cd20e3b9a357f5077a3d0b31 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Fri, 30 Apr 2021 23:00:32 +0300 Subject: [PATCH 52/72] Fix memory issue --- cpp/boost_lockfree_queue_test.cpp | 4 +- cpp/folly_hashmap_test.cpp | 6 +-- cpp/libcds_msqueue_dhp_test.cpp | 6 +-- cpp/libcuckoo_test.cpp | 6 +-- cpp/lincheck.h | 54 ++++++++----------- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 2 + .../jetbrains/kotlinx/lincheck/CommonUtils.kt | 4 ++ .../lincheck/execution/ExecutionResult.kt | 8 ++- .../lincheck/execution/ExecutionScenario.kt | 51 ++++++++++-------- .../kotlinx/lincheck/execution/HBClock.kt | 6 ++- .../lincheck/runner/InvocationResult.kt | 8 ++- .../kotlinx/lincheck/runner/Runner.kt | 6 ++- .../org/jetbrains/kotlinx/lincheck/Actor.kt | 4 ++ .../org/jetbrains/kotlinx/lincheck/Actor.kt | 4 ++ .../jetbrains/kotlinx/lincheck/LinChecker.kt | 32 +++++++---- .../jetbrains/kotlinx/lincheck/ValueResult.kt | 8 ++- .../lincheck/runner/ParallelThreadsRunner.kt | 3 ++ .../strategy/stress/StressStrategy.kt | 5 +- 18 files changed, 135 insertions(+), 82 deletions(-) diff --git a/cpp/boost_lockfree_queue_test.cpp b/cpp/boost_lockfree_queue_test.cpp index 6c069649d..a36b40f6e 100644 --- a/cpp/boost_lockfree_queue_test.cpp +++ b/cpp/boost_lockfree_queue_test.cpp @@ -65,11 +65,11 @@ using namespace Lincheck; TEST(BoostLockfreeQueueTest, QueueTest) { LincheckConfiguration conf; - conf.iterations(10000); + conf.iterations(100); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); conf.threads(4); - conf.actorsPerThread(7); + conf.actorsPerThread(5); conf.operation("push"); conf.operation, &ConcurrentQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); diff --git a/cpp/folly_hashmap_test.cpp b/cpp/folly_hashmap_test.cpp index 0469e20a8..ddd7107fd 100644 --- a/cpp/folly_hashmap_test.cpp +++ b/cpp/folly_hashmap_test.cpp @@ -68,11 +68,11 @@ using namespace Lincheck; TEST(follyHashMapTest, FirstTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(3); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(4); - conf.actorsPerThread(7); + conf.threads(3); + conf.actorsPerThread(5); conf.operation("assign"); conf.operation, int, &ConcurrentMapFolly::get, &SequentialMapFolly::get>("get"); diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp index a35eb4d9a..2cbf2fe66 100644 --- a/cpp/libcds_msqueue_dhp_test.cpp +++ b/cpp/libcds_msqueue_dhp_test.cpp @@ -77,11 +77,11 @@ TEST(LibcdsMSQueueTest, QueueTest) { myAttach(); LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(2); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(4); - conf.actorsPerThread(7); + conf.threads(3); + conf.actorsPerThread(5); conf.initThreadFunction(); conf.finishThreadFunction(); diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp index 172d304da..7dee6f9b2 100644 --- a/cpp/libcuckoo_test.cpp +++ b/cpp/libcuckoo_test.cpp @@ -92,11 +92,11 @@ using namespace Lincheck; TEST(libcuckooTest, FirstTest) { LincheckConfiguration conf; - conf.iterations(2); + conf.iterations(40); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(2); - conf.actorsPerThread(3); + conf.threads(4); + conf.actorsPerThread(5); conf.operation("assign"); conf.operation("get"); diff --git a/cpp/lincheck.h b/cpp/lincheck.h index 2e0be11b7..60d279a05 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -46,41 +46,22 @@ namespace Lincheck { public: LincheckConfiguration() { - - constructor_pointer *instance_constructor = new constructor_pointer(); - *instance_constructor = []() -> void * { return new TestClass(); }; - - destructor_pointer *instance_destructor = new destructor_pointer(); - *instance_destructor = [](void *p) { delete (TestClass *) p; }; - lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupInitialState( configuration, - (void *) *instance_constructor, // constructor - (void *) *instance_destructor // destructor + (void *) (constructor_pointer) []() -> void * { return new TestClass(); }, // constructor + (void *) (destructor_pointer) [](void *p) { delete (TestClass *) p; } // destructor ); - constructor_pointer *constructor = new constructor_pointer(); - *constructor = []() -> void * { return new SequentialSpecification(); }; - - destructor_pointer *destructor = new destructor_pointer(); - *destructor = [](void *p) { delete (SequentialSpecification *) p; }; - - equals_pointer *equals = new equals_pointer(); - *equals = [](void *a, void *b) -> bool { - return *(SequentialSpecification *) a == *(SequentialSpecification *) b; - }; - - hashCode_pointer *hashCode = new hashCode_pointer(); - *hashCode = [](void *instance) -> int { - return Lincheck::hash()(*(SequentialSpecification *) instance); - }; - lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupSequentialSpecification( configuration, - (void *) *constructor, // constructor - (void *) *destructor, // destructor - (void *) *equals, // equals - (void *) *hashCode // hashCode + (void *) (constructor_pointer) []() -> void * { return new SequentialSpecification(); }, // constructor + (void *) (destructor_pointer) [](void *p) { delete (SequentialSpecification *) p; }, // destructor + (void *) (equals_pointer) [](void *a, void *b) -> bool { + return *(SequentialSpecification *) a == *(SequentialSpecification *) b; + }, // equals + (void *) (hashCode_pointer) [](void *instance) -> int { + return Lincheck::hash()(*(SequentialSpecification *) instance); + } // hashCode ); } @@ -178,10 +159,13 @@ namespace Lincheck { Arg1 arg = obj->generate(); // invoke generate method return new Arg1(arg); // copy from stack to heap and return }, - (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg1_toString strncpy(dest, Lincheck::to_string()(*(Arg1 *) arg).c_str(), destSize); }, + (void *) (void (*)(void *)) [](void *arg1) { // arg1_destructor + Arg1 *obj = (Arg1 *) arg1; // add type to void* + delete obj; // destructor + }, (void *) (void *(*)(void *, void *)) [](void *instance, void *arg1) -> void * { // operation auto *obj = (TestClass *) instance; // add type to void* auto *a1 = (Arg1 *) arg1; @@ -230,10 +214,13 @@ namespace Lincheck { Arg1 arg = obj->generate(); // invoke generate method return new Arg1(arg); // copy from stack to heap and return }, - (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg1_toString strncpy(dest, Lincheck::to_string()(*(Arg1 *) arg).c_str(), destSize); }, + (void *) (void (*)(void *)) [](void *arg1) { // arg1_destructor + Arg1 *obj = (Arg1 *) arg1; // add type to void* + delete obj; // destructor + }, (void *) (void *(*)()) []() -> void * { // arg2_gen_initial_state return new ParameterGenerator(); }, @@ -242,10 +229,13 @@ namespace Lincheck { Arg2 arg = obj->generate(); // invoke generate method return new Arg2(arg); // copy from stack to heap and return }, - (void *) (void (*)(void *, char *, int)) [](void *arg, char *dest, int destSize) { // arg2_toString strncpy(dest, Lincheck::to_string()(*(Arg2 *) arg).c_str(), destSize); }, + (void *) (void (*)(void *)) [](void *arg2) { // arg2_destructor + Arg2 *obj = (Arg2 *) arg2; // add type to void* + delete obj; // destructor + }, (void *) (void *(*)(void *, void *, void *)) [](void *instance, void *arg1, void *arg2) -> void * { // operation auto *obj = (TestClass *) instance; // add type to void* diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 9764b91e7..637347043 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -37,6 +37,8 @@ expect class Actor { val handledExceptions: List> override fun toString(): String + + fun finalize() } expect val Actor.isQuiescentConsistent: Boolean \ No newline at end of file diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt index c56c8db25..ea4609487 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/CommonUtils.kt @@ -147,6 +147,10 @@ private val ADD_OPENS_MESSAGE = "It seems that you use Java 9+ and the code uses internal val String.canonicalClassName get() = this.replace('/', '.') internal val String.internalClassName get() = this.replace('.', '/') +internal interface Finalizable { + fun finalize() +} + /** * Collects the current thread dump and keeps only those * threads that are related to the specified [runner]. diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt index bafc5f432..d593f2ebd 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt @@ -58,9 +58,15 @@ data class ExecutionResult( * State representation at the end of the scenario. */ val afterPostStateRepresentation: String? -) { +) : Finalizable { constructor(initResults: List, parallelResultsWithClock: List>, postResults: List) : this(initResults, null, parallelResultsWithClock, null, postResults, null) + + override fun finalize() { + initResults.forEach { if(it is Finalizable) it.finalize() } + parallelResultsWithClock.forEach { l -> l.forEach { if(it is Finalizable) it.finalize() } } + postResults.forEach { if(it is Finalizable) it.finalize() } + } } val ExecutionResult.withEmptyClocks: ExecutionResult get() = ExecutionResult( diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt index dd9b7da7d..c8256c724 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.kt @@ -18,6 +18,7 @@ * */ @file:JvmName("ExecutionScenarioKtCommon") + package org.jetbrains.kotlinx.lincheck.execution import org.jetbrains.kotlinx.lincheck.Actor @@ -29,34 +30,40 @@ import kotlin.jvm.* * used by a [Strategy] which produces an [ExecutionResult]. */ class ExecutionScenario( - /** - * The initial sequential part of the execution. - * It helps to produce different initial states - * before the parallel part. - * - * The initial execution part should contain only non-suspendable actors; - * otherwise, the single initial execution thread will suspend with no chance to be resumed. - */ - val initExecution: List, - /** - * The parallel part of the execution, which is used - * to find an interleaving with incorrect behaviour. - */ - val parallelExecution: List>, - /** - * The last sequential part is used to test that - * the data structure is in some correct state. - * - * If this execution scenario contains suspendable actors, the post part should be empty; - * if not, an actor could resume a previously suspended one from the parallel execution part. - */ - val postExecution: List + /** + * The initial sequential part of the execution. + * It helps to produce different initial states + * before the parallel part. + * + * The initial execution part should contain only non-suspendable actors; + * otherwise, the single initial execution thread will suspend with no chance to be resumed. + */ + val initExecution: List, + /** + * The parallel part of the execution, which is used + * to find an interleaving with incorrect behaviour. + */ + val parallelExecution: List>, + /** + * The last sequential part is used to test that + * the data structure is in some correct state. + * + * If this execution scenario contains suspendable actors, the post part should be empty; + * if not, an actor could resume a previously suspended one from the parallel execution part. + */ + val postExecution: List ) { override fun toString(): String { val sb = StringBuilder() sb.appendExecutionScenario(this) return sb.toString() } + + fun finalize() { + initExecution.forEach { it.finalize() } + parallelExecution.forEach { l -> l.forEach { it.finalize() } } + postExecution.forEach { it.finalize() } + } } /** diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt index 7e75a6b00..b90b80794 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/HBClock.kt @@ -44,7 +44,11 @@ data class HBClock(val clock: LincheckAtomicIntArray) { fun emptyClock(size: Int) = HBClock(emptyClockArray(size)) fun emptyClockArray(size: Int) = LincheckAtomicIntArray(size) -data class ResultWithClock(val result: Result, val clockOnStart: HBClock) +data class ResultWithClock(val result: Result, val clockOnStart: HBClock) : Finalizable { + override fun finalize() { + if(result is Finalizable) result.finalize() + } +} fun Result.withEmptyClock(threads: Int) = ResultWithClock(this, emptyClock(threads)) fun List.withEmptyClock(threads: Int): List = map { it.withEmptyClock(threads) } diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt index 32e5c5d75..2fb7adccd 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt @@ -20,6 +20,8 @@ package org.jetbrains.kotlinx.lincheck.runner +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.Finalizable import org.jetbrains.kotlinx.lincheck.execution.* expect class ThreadDump @@ -34,7 +36,11 @@ open class InvocationResult */ class CompletedInvocationResult( val results: ExecutionResult -) : InvocationResult() +) : InvocationResult(), Finalizable { + override fun finalize() { + results.finalize() + } +} /** * The invocation has completed with an unexpected exception. diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt index 06aeeb83e..dfdb01888 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/runner/Runner.kt @@ -46,7 +46,7 @@ abstract class Runner protected constructor( private val _testClass: TestClass, // will be transformed later protected val validationFunctions: List, protected val stateRepresentationFunction: StateRepresentationFunction? -) { +) : Finalizable { protected var scenario = strategy.scenario // `strategy.scenario` will be transformed in `initialize` protected lateinit var testClass: TestClass // not available before `initialize` call @Suppress("LeakingThis") @@ -160,4 +160,8 @@ abstract class Runner protected constructor( */ val isParallelExecutionCompleted: Boolean get() = completedOrSuspendedThreads.get() == scenario.threads + + override fun finalize() { + scenario.finalize() + } } \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 8c2de7337..5abf2b0cf 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -59,6 +59,10 @@ actual data class Actor @JvmOverloads constructor( (if (promptCancellation) "prompt_" else "") + (if (cancelOnSuspension) "cancel" else "") + actual fun finalize() { + // do nothing + } + val handlesExceptions = handledExceptions.isNotEmpty() } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt index 696ea8582..da6b36cd1 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/Actor.kt @@ -45,6 +45,10 @@ actual class Actor( (if (cancelOnSuspension) " + " else "") + (if (promptCancellation) "prompt_" else "") + (if (cancelOnSuspension) "cancel" else "") + + actual fun finalize() { + arguments.forEach { if(it is Finalizable) it.finalize() } + } } actual val Actor.isQuiescentConsistent: Boolean diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index af276ef0e..922726760 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -47,7 +47,7 @@ internal class ObjectWithDestructorAndEqualsAndHashcodeAndToString(val obj: CPoi val destructor: CDestructor, val equals: EqualsCFunction, val hashCode: HashCodeCFunction, - val toString: ToStringCFunction) { + val toString: ToStringCFunction) : Finalizable { override fun equals(other: Any?): Boolean { return if (other is ObjectWithDestructorAndEqualsAndHashcodeAndToString) { @@ -65,22 +65,28 @@ internal class ObjectWithDestructorAndEqualsAndHashcodeAndToString(val obj: CPoi return applyToStringFunction(obj, toString) } - protected fun finalize() { + override fun finalize() { // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 destructor.invoke(obj) } } internal class ParameterGeneratorArgument(val arg: CPointer<*>, - val toString: ToStringCFunction) { + val destructor: CDestructor, + val toString: ToStringCFunction) : Finalizable { override fun toString(): String { return applyToStringFunction(arg, toString) } + + override fun finalize() { + // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 + destructor.invoke(arg) + } } -internal class ConcurrentInstance(val obj: CPointer<*>, val destructor: CDestructor) { +internal class ConcurrentInstance(val obj: CPointer<*>, val destructor: CDestructor): Finalizable { - protected fun finalize() { + override fun finalize() { // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 destructor.invoke(obj) } @@ -89,7 +95,7 @@ internal class ConcurrentInstance(val obj: CPointer<*>, val destructor: CDestruc internal class SequentialSpecificationInstance(val obj: CPointer<*>, val destructor: CDestructor, val equalsFunction: EqualsCFunction, - val hashCodeFunction: HashCodeCFunction) { + val hashCodeFunction: HashCodeCFunction) : Finalizable { override fun equals(other: Any?): Boolean { return if (other is SequentialSpecificationInstance) { equalsFunction.invoke(obj, other.obj) @@ -102,7 +108,7 @@ internal class SequentialSpecificationInstance(val obj: CPointer<*>, return hashCodeFunction.invoke(obj) } - protected fun finalize() { + override fun finalize() { // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 destructor.invoke(obj) } @@ -179,7 +185,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { if (initialStateCreator != null) { // different initialState and sequentialSpecification initialState { - ConcurrentInstance(initialStateCreator!!.invoke(), sequentialSpecificationDestructor!!) + ConcurrentInstance(initialStateCreator!!.invoke(), initialStateDestructor!!) } } else { initialState { @@ -274,6 +280,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { arg1_gen_initial_state: CCreator, arg1_gen_generate: CPointer) -> CPointer<*>>>, arg1_toString: ToStringCFunction, + arg1_destructor: CDestructor, op: CPointer, CPointer<*>) -> CPointer<*>>>, seq_spec: CPointer, CPointer<*>) -> CPointer<*>>>, result_destructor: CDestructor, @@ -286,7 +293,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { val arg1_paramgen = object : ParameterGenerator { val state = arg1_gen_initial_state.invoke() override fun generate(): ParameterGeneratorArgument { - return ParameterGeneratorArgument(arg1_gen_generate.invoke(state), arg1_toString) + return ParameterGeneratorArgument(arg1_gen_generate.invoke(state), arg1_destructor, arg1_toString) } } val actorGenerator = ActorGenerator( @@ -316,9 +323,11 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { arg1_gen_initial_state: CCreator, arg1_gen_generate: CPointer) -> CPointer<*>>>, arg1_toString: ToStringCFunction, + arg1_destructor: CDestructor, arg2_gen_initial_state: CCreator, arg2_gen_generate: CPointer) -> CPointer<*>>>, arg2_toString: ToStringCFunction, + arg2_destructor: CDestructor, op: CPointer, CPointer<*>, CPointer<*>) -> CPointer<*>>>, seq_spec: CPointer, CPointer<*>, CPointer<*>) -> CPointer<*>>>, result_destructor: CDestructor, @@ -331,13 +340,13 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { val arg1Paramgen = object : ParameterGenerator { val state = arg1_gen_initial_state.invoke() override fun generate(): ParameterGeneratorArgument { - return ParameterGeneratorArgument(arg1_gen_generate.invoke(state), arg1_toString) + return ParameterGeneratorArgument(arg1_gen_generate.invoke(state), arg1_destructor, arg1_toString) } } val arg2Paramgen = object : ParameterGenerator { val state = arg2_gen_initial_state.invoke() override fun generate(): ParameterGeneratorArgument { - return ParameterGeneratorArgument(arg2_gen_generate.invoke(state), arg2_toString) + return ParameterGeneratorArgument(arg2_gen_generate.invoke(state), arg2_destructor, arg2_toString) } } val actorGenerator = ActorGenerator( @@ -593,6 +602,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT reporter.logFailedIteration(minimizedFailedIteration) return minimizedFailedIteration } + //scenario.finalize() } return null } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt index edfaf3c34..b79f06707 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/ValueResult.kt @@ -23,10 +23,16 @@ package org.jetbrains.kotlinx.lincheck /** * Type of result used if the actor invocation returns any value. */ -actual class ValueResult(actual val value: Any?, override val wasSuspended: Boolean = false) : Result() { +actual class ValueResult(actual val value: Any?, override val wasSuspended: Boolean = false) : Result(), Finalizable { actual override fun equals(other: Any?): Boolean = if (other !is ValueResult) false else other.wasSuspended == wasSuspended && other.value == value actual override fun hashCode(): Int = if (wasSuspended) 0 else 1 // We can't use value here override fun toString() = wasSuspendedPrefix + "$value" + + override fun finalize() { + if(value is Finalizable) { + value.finalize() + } + } } \ No newline at end of file diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 12ba7991c..4db83f8bf 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -141,6 +141,9 @@ internal actual open class ParallelThreadsRunner actual constructor( result } val afterPostStateRepresentation = nativeConstructStateRepresentation(testInstance) + if (testInstance is Finalizable) { + testInstance.finalize() + } val results = ExecutionResult( initResults, afterInitStateRepresentation, parallelResultsWithClock, afterParallelStateRepresentation, diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index ddf50cb61..c42df9759 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -66,8 +66,11 @@ actual class StressStrategy actual constructor( runner.also { when (val ir = runner.run()) { is CompletedInvocationResult -> { - if (!verifier.verifyResults(scenario, ir.results)) + if (!verifier.verifyResults(scenario, ir.results)) { return IncorrectResultsFailure(scenario, ir.results) + } else { + ir.finalize() + } } else -> return ir.toLincheckFailure(scenario) } From 027abc33a75c43ffc956ba93e9b8e61df09e20dc Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 3 May 2021 10:22:33 +0300 Subject: [PATCH 53/72] Fix git submodules, add progress debug output --- cpp/CMakeLists.txt | 22 ++-- cpp/boost_lockfree_queue_test.cpp | 2 +- cpp/counter_test.cpp | 2 +- cpp/folly_hashmap_test.cpp | 2 +- cpp/junction/junction | 1 - cpp/junction/turf | 1 - cpp/junction_test.cpp | 116 ------------------ cpp/libcds_msqueue_dhp_test.cpp | 2 +- cpp/libcuckoo/libcuckoo | 1 - cpp/libcuckoo_test.cpp | 7 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 13 +- .../runner/FixedActiveThreadsExecutor.kt | 2 +- .../strategy/stress/StressStrategy.kt | 9 +- src/native/test/AbstractLincheckTest.kt | 2 +- src/native/test/FirstTest.kt | 9 +- 15 files changed, 47 insertions(+), 144 deletions(-) delete mode 160000 cpp/junction/junction delete mode 160000 cpp/junction/turf delete mode 100644 cpp/junction_test.cpp delete mode 160000 cpp/libcuckoo/libcuckoo diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 7e51bc926..1025714ff 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -45,20 +45,31 @@ if (NOT folly_POPULATED) add_subdirectory(${folly_SOURCE_DIR} ${folly_BINARY_DIR}) endif () +# add libcuckoo +FetchContent_Declare( + libcuckoo + GIT_REPOSITORY https://github.com/efficient/libcuckoo + GIT_TAG master + GIT_SHALLOW TRUE +) + +FetchContent_GetProperties(libcuckoo) +if (NOT libcuckoo_POPULATED) + FetchContent_Populate(libcuckoo) + add_subdirectory(${libcuckoo_SOURCE_DIR} ${libcuckoo_BINARY_DIR}) + include_directories(${libcuckoo_SOURCE_DIR}) +endif () + # add boost set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) find_package(Boost REQUIRED COMPONENTS thread program_options) -#add_subdirectory("junction/junction" junction) -add_subdirectory("libcuckoo/libcuckoo" libcuckoo) - add_executable( tests counter_test.cpp libcds_msqueue_dhp_test.cpp - #junction_test.cpp libcuckoo_test.cpp folly_hashmap_test.cpp boost_lockfree_queue_test.cpp @@ -70,11 +81,8 @@ target_link_libraries(tests gtest_main) target_link_libraries(tests gmock_main) target_link_libraries(tests lincheck_library) target_link_libraries(tests cds) -#target_link_libraries(tests junction) target_link_libraries(tests libcuckoo) target_link_libraries(tests folly) -include_directories(${JUNCTION_ALL_INCLUDE_DIRS}) -target_link_libraries(tests ${JUNCTION_ALL_LIBRARIES}) set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) diff --git a/cpp/boost_lockfree_queue_test.cpp b/cpp/boost_lockfree_queue_test.cpp index a36b40f6e..f31aea96f 100644 --- a/cpp/boost_lockfree_queue_test.cpp +++ b/cpp/boost_lockfree_queue_test.cpp @@ -68,7 +68,7 @@ TEST(BoostLockfreeQueueTest, QueueTest) { conf.iterations(100); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(4); + conf.threads(3); conf.actorsPerThread(5); conf.operation("push"); diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index e260ccfbc..6e5d34edb 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -86,7 +86,7 @@ TEST(CounterTest, BadInc) { conf.threads(3); conf.iterations(100); conf.invocationsPerIteration(500); - conf.actorsPerThread(8); + conf.actorsPerThread(5); conf.operation("inc"); ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); } diff --git a/cpp/folly_hashmap_test.cpp b/cpp/folly_hashmap_test.cpp index ddd7107fd..bf62ed22b 100644 --- a/cpp/folly_hashmap_test.cpp +++ b/cpp/folly_hashmap_test.cpp @@ -68,7 +68,7 @@ using namespace Lincheck; TEST(follyHashMapTest, FirstTest) { LincheckConfiguration conf; - conf.iterations(3); + conf.iterations(100); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); conf.threads(3); diff --git a/cpp/junction/junction b/cpp/junction/junction deleted file mode 160000 index 5ad3be7ce..000000000 --- a/cpp/junction/junction +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ad3be7ce1d3f16b9f7ed6065bbfeacd2d629a08 diff --git a/cpp/junction/turf b/cpp/junction/turf deleted file mode 160000 index 9ae0d4b98..000000000 --- a/cpp/junction/turf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9ae0d4b984fa95ed5f823274b39c87ee742f6650 diff --git a/cpp/junction_test.cpp b/cpp/junction_test.cpp deleted file mode 100644 index 47a26b168..000000000 --- a/cpp/junction_test.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "lincheck.h" -#include "lincheck_functions.h" -#include - -class SequentialMap { -public: - std::unordered_map map; - - int assign(int key, int value) { - return map[key] = value; - } - - int get(int key) { - auto it = map.find(key); - if(it != map.end()) { - return it->second; - } - return 0; - } - - int exchange(int key, int value) { - auto it = map.find(key); - if(it != map.end()) { - auto ans = it->second; - it->second = value; - return ans; - } - map[key] = value; - return 0; - } - - int erase(int key) { - auto it = map.find(key); - if(it != map.end()) { - auto ans = it->second; - map.erase(key); - return ans; - } - map.erase(key); - return 0; - } -}; - -class ConcurrentMap { -public: - junction::ConcurrentMap_Grampa map; - - int assign(int key, int value) { - std::cerr << "assign" << key << ", " << value << "\n"; - auto ans = map.assign(key, value); - std::cerr << "assign2" << key << ", " << value << "\n"; - return ans; - } - - int get(int key) { - std::cerr << "get\n"; - auto mut = map.find(key); - std::cerr << "get2\n"; - auto ans = map.get(key); - std::cerr << "get3\n"; - return ans; - } - - int exchange(int key, int value) { - std::cerr << "exchange\n"; - auto ans = map.exchange(key, value); - std::cerr << "exchange2\n"; - return ans; - } - - int erase(int key) { - std::cerr << "erase\n"; - auto ans = map.erase(key); - std::cerr << "erase2\n"; - return ans; - } -}; - -template<> -struct Lincheck::hash { - std::size_t operator()(SequentialMap const &s) const noexcept { - std::vector vec; - for(auto it : s.map) { - vec.push_back(it.first); - vec.push_back(it.second); - } - return Lincheck::hash>()(vec); - } -}; - -bool operator==(const SequentialMap &a, const SequentialMap &b) { - return a.map == b.map; -} - -using namespace Lincheck; - -void myAttach2() { - std::cerr << "myattach2\n"; - junction::DefaultQSBR.createContext(); -} - -TEST(JunctionTest, FirstTest) { - LincheckConfiguration conf; - conf.iterations(1); - conf.invocationsPerIteration(500); - conf.minimizeFailedScenario(false); - conf.initThreadFunction(); - //Not working right now, because of produce-consume(locks and waits until consumed previous value) - //conf.operation("assign"); - //conf.operation("get"); - //conf.operation("exchange"); - //conf.operation("erase"); - ASSERT_EQ(conf.runTest(false), ""); -} \ No newline at end of file diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp index 2cbf2fe66..64d7608dd 100644 --- a/cpp/libcds_msqueue_dhp_test.cpp +++ b/cpp/libcds_msqueue_dhp_test.cpp @@ -77,7 +77,7 @@ TEST(LibcdsMSQueueTest, QueueTest) { myAttach(); LincheckConfiguration conf; - conf.iterations(2); + conf.iterations(100); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); conf.threads(3); diff --git a/cpp/libcuckoo/libcuckoo b/cpp/libcuckoo/libcuckoo deleted file mode 160000 index 878577389..000000000 --- a/cpp/libcuckoo/libcuckoo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8785773896d74f72b6224e59d37f5f8c3c1e022a diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp index 7dee6f9b2..042786957 100644 --- a/cpp/libcuckoo_test.cpp +++ b/cpp/libcuckoo_test.cpp @@ -3,7 +3,7 @@ #include "lincheck.h" #include "lincheck_functions.h" -#include +#include "libcuckoo/cuckoohash_map.hh" class SequentialMapCuckoo { public: @@ -92,11 +92,10 @@ using namespace Lincheck; TEST(libcuckooTest, FirstTest) { LincheckConfiguration conf; - conf.iterations(40); + conf.iterations(100); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); - conf.threads(4); - conf.actorsPerThread(5); + conf.threads(3); conf.operation("assign"); conf.operation("get"); diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 922726760..2b5e99aa2 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -558,8 +558,6 @@ open class LincheckStressConfiguration(protected val testName: String class LinChecker(private val testClass: TestClass, private val testStructure: CTestStructure, options: Options<*, *>) { private val testConfigurations: List private val reporter: Reporter - var initThreadFunction: CPointer Unit>>? = null - var finishThreadFunction: CPointer Unit>>? = null init { val logLevel = options?.logLevel ?: DEFAULT_LOG_LEVEL @@ -591,7 +589,14 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> - println(i) + val curPercent = i.toDouble() / iterations.toDouble() + val nextPercent = (i + 1).toDouble() / iterations.toDouble() + val STEP = 0.2 + val curShare = (curPercent / STEP).toInt() + val nextShare = (nextPercent / STEP).toInt() + if(curShare != nextShare) { + println("${(nextShare * STEP * 100).toInt()}%") + } val scenario = exGen.nextExecution() scenario.validate() reporter.logIteration(i + 1, iterations, scenario) @@ -602,7 +607,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT reporter.logFailedIteration(minimizedFailedIteration) return minimizedFailedIteration } - //scenario.finalize() + //scenario.finalize() leads to fail ?? } return null } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 97a4b52da..0b053ba7b 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -59,7 +59,7 @@ internal class TestThread constructor(val iThread: Int, runnerHash: Int) { //val result = runnableFuture!!.result //worker.execute(TransferMode.UNSAFE, { }, { sleep(1000000000) }) //println("terminate $iThread end") - //worker.requestTermination(false).result + //worker.value.requestTermination(false).result //return res //printErr("stop() $iThread finished") } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index c42df9759..56fcb1dbe 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -64,7 +64,12 @@ actual class StressStrategy actual constructor( try { for (invocation in 0 until invocations) { runner.also { - when (val ir = runner.run()) { + //println("invocation $invocation has started") + //val t1 = currentTimeMillis() + val ir = runner.run() + //val t2 = currentTimeMillis() + //println("invocation $invocation has invocated, took ${t2 - t1}ms to run") + when (ir) { is CompletedInvocationResult -> { if (!verifier.verifyResults(scenario, ir.results)) { return IncorrectResultsFailure(scenario, ir.results) @@ -74,6 +79,8 @@ actual class StressStrategy actual constructor( } else -> return ir.toLincheckFailure(scenario) } + //val t3 = currentTimeMillis() + //println("invocation $invocation has verified, took ${t3 - t2}ms to verify") } } } finally { diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index a6504cab5..41bf743cd 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -64,7 +64,7 @@ abstract class AbstractLincheckStressTest( invocationsPerIteration(500) actorsBefore(2) threads(3) - actorsPerThread(2) + actorsPerThread(4) actorsAfter(2) minimizeFailedScenario(false) customize() diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 77f0c9bb2..8137407a8 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -120,14 +120,17 @@ class FirstTest { @Test fun test_many_threads() { LincheckStressConfiguration("FirstTest_3").apply { - iterations(2) - invocationsPerIteration(500) - threads(8) + iterations(100) + invocationsPerIteration(20) + threads(7) + actorsPerThread(2) minimizeFailedScenario(false) initialState { TestClass() } operation(TestClass::atomicIncrement, "atomicIncrement") + operation(TestClass::atomicDecrement, "atomicDecrement") + operation(TestClass::complexOperation, "complexOperation") }.runTest() } From a50b8f73a2f22a5aaef64da46fa816878fa74982 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 3 May 2021 10:31:55 +0300 Subject: [PATCH 54/72] switch from debugShared to releaseShared --- cpp/CMakeLists.txt | 2 +- cpp/lincheck.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1025714ff..550d1c2ae 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 17) # add lincheck shared library add_library(lincheck_library SHARED IMPORTED GLOBAL) -set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/debugShared/libnative.so") +set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/releaseShared/libnative.so") # add gtest include(FetchContent) diff --git a/cpp/lincheck.h b/cpp/lincheck.h index 60d279a05..c0cc1a152 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -3,7 +3,7 @@ #include #include #include -#include "../build/bin/native/debugShared/libnative_api.h" +#include "../build/bin/native/releaseShared/libnative_api.h" extern libnative_ExportedSymbols *libnative_symbols(void); @@ -278,4 +278,4 @@ namespace Lincheck { configuration, printErrorToStderr); } }; -} \ No newline at end of file +} From 74d164ecf154df9635695201dac9bcf7a7d6ea78 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 3 May 2021 10:35:51 +0300 Subject: [PATCH 55/72] fix test_failing test --- src/native/test/FirstTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/test/FirstTest.kt b/src/native/test/FirstTest.kt index 8137407a8..c09173d04 100644 --- a/src/native/test/FirstTest.kt +++ b/src/native/test/FirstTest.kt @@ -76,10 +76,10 @@ class FirstTest { fun test_failing() { val f = LincheckStressConfiguration("FirstTest_1").apply { iterations(300) - invocationsPerIteration(50) + invocationsPerIteration(500) actorsBefore(2) threads(3) - actorsPerThread(2) + actorsPerThread(4) actorsAfter(2) minimizeFailedScenario(false) @@ -147,4 +147,4 @@ class FirstTest { operation(TestClass::complexOperation, "complexOperation") }.runTest() } -} \ No newline at end of file +} From 949eb7f4065d0bfbda0bdb6125e8b2d0a42d1521 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 3 May 2021 10:47:48 +0300 Subject: [PATCH 56/72] increase iterations count in AbstractLincheckStressTest --- src/native/test/AbstractLincheckTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index 41bf743cd..eb99be9fb 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -60,7 +60,7 @@ abstract class AbstractLincheckStressTest( } */ private fun > T.commonConfiguration(): Unit = run { - iterations(10) + iterations(20) invocationsPerIteration(500) actorsBefore(2) threads(3) @@ -76,4 +76,4 @@ fun checkTraceHasNoLincheckEvents(trace: String) { val testPackageOccurrences = trace.split("org.jetbrains.kotlinx.lincheck.test.").size - 1 val lincheckPackageOccurrences = trace.split("org.jetbrains.kotlinx.lincheck.").size - 1 check(testPackageOccurrences == lincheckPackageOccurrences) { "Internal Lincheck events were found in the trace" } -} \ No newline at end of file +} From e3350e96c46d0aa56909ebc7b50a85e562a8501d Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Mon, 3 May 2021 11:17:57 +0300 Subject: [PATCH 57/72] set up parameters for tests --- cpp/boost_lockfree_queue_test.cpp | 6 +++--- cpp/libcds_msqueue_dhp_test.cpp | 2 +- cpp/libcuckoo_test.cpp | 1 + src/native/test/AbstractLincheckTest.kt | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cpp/boost_lockfree_queue_test.cpp b/cpp/boost_lockfree_queue_test.cpp index f31aea96f..67b997a72 100644 --- a/cpp/boost_lockfree_queue_test.cpp +++ b/cpp/boost_lockfree_queue_test.cpp @@ -65,11 +65,11 @@ using namespace Lincheck; TEST(BoostLockfreeQueueTest, QueueTest) { LincheckConfiguration conf; - conf.iterations(100); + conf.iterations(1000); conf.invocationsPerIteration(500); - conf.minimizeFailedScenario(false); + conf.minimizeFailedScenario(true); conf.threads(3); - conf.actorsPerThread(5); + conf.actorsPerThread(3); conf.operation("push"); conf.operation, &ConcurrentQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp index 64d7608dd..7c7a38dfa 100644 --- a/cpp/libcds_msqueue_dhp_test.cpp +++ b/cpp/libcds_msqueue_dhp_test.cpp @@ -81,7 +81,7 @@ TEST(LibcdsMSQueueTest, QueueTest) { conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); conf.threads(3); - conf.actorsPerThread(5); + conf.actorsPerThread(4); conf.initThreadFunction(); conf.finishThreadFunction(); diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp index 042786957..0ba78326b 100644 --- a/cpp/libcuckoo_test.cpp +++ b/cpp/libcuckoo_test.cpp @@ -96,6 +96,7 @@ TEST(libcuckooTest, FirstTest) { conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); conf.threads(3); + conf.actorsPerThread(4); conf.operation("assign"); conf.operation("get"); diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index eb99be9fb..2ad06f17f 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -60,12 +60,12 @@ abstract class AbstractLincheckStressTest( } */ private fun > T.commonConfiguration(): Unit = run { - iterations(20) + iterations(100) invocationsPerIteration(500) - actorsBefore(2) + actorsBefore(3) threads(3) actorsPerThread(4) - actorsAfter(2) + actorsAfter(3) minimizeFailedScenario(false) customize() } From a90e7e81eb5ba25c3ad755b8ee9ea05d4ba56a82 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 5 May 2021 14:18:44 +0300 Subject: [PATCH 58/72] Add more c++ tests --- cpp/CMakeLists.txt | 6 +- cpp/boost_lockfree_queue_test.cpp | 77 ----- cpp/boost_lockfree_test.cpp | 184 ++++++++++ cpp/counter_test.cpp | 4 +- cpp/folly_hashmap_test.cpp | 81 ----- cpp/folly_test.cpp | 212 ++++++++++++ cpp/libcds_msqueue_dhp_test.cpp | 97 ------ cpp/libcds_test.cpp | 318 ++++++++++++++++++ cpp/libcuckoo_test.cpp | 24 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 12 +- .../runner/FixedActiveThreadsExecutor.kt | 7 +- .../lincheck/runner/TestThreadExecution.kt | 2 +- 12 files changed, 752 insertions(+), 272 deletions(-) delete mode 100644 cpp/boost_lockfree_queue_test.cpp create mode 100644 cpp/boost_lockfree_test.cpp delete mode 100644 cpp/folly_hashmap_test.cpp create mode 100644 cpp/folly_test.cpp delete mode 100644 cpp/libcds_msqueue_dhp_test.cpp create mode 100644 cpp/libcds_test.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 550d1c2ae..954559f1f 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -69,10 +69,10 @@ find_package(Boost REQUIRED COMPONENTS thread program_options) add_executable( tests counter_test.cpp - libcds_msqueue_dhp_test.cpp + libcds_test.cpp libcuckoo_test.cpp - folly_hashmap_test.cpp - boost_lockfree_queue_test.cpp + folly_test.cpp + boost_lockfree_test.cpp ) enable_testing() diff --git a/cpp/boost_lockfree_queue_test.cpp b/cpp/boost_lockfree_queue_test.cpp deleted file mode 100644 index 67b997a72..000000000 --- a/cpp/boost_lockfree_queue_test.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "lincheck.h" -#include "lincheck_functions.h" -#include - -#include - -class SequentialQueueBoost { -public: - std::queue q; - - bool push(int value) { - q.push(value); - return true; - } - - std::pair pop() { - int val = 0; - if(!q.empty()) { - val = q.front(); - q.pop(); - return {true, val}; - } else { - return {false, val}; - } - } -}; - -class ConcurrentQueueBoost { -public: - boost::lockfree::queue q = boost::lockfree::queue(100); - - bool push(int value) { - return q.push(value); - } - - std::pair pop() { - int val = 0; - bool success = q.pop(val); - return {success, success ? val : 0}; - } -}; - -template<> -struct Lincheck::hash { - std::size_t operator()(SequentialQueueBoost &s) const noexcept { - std::vector vec; - while(!s.q.empty()) { - vec.push_back(s.q.front()); - s.q.pop(); - } - for (auto it : vec) { - s.q.push(it); - } - return Lincheck::hash>()(vec); - } -}; - -bool operator==(const SequentialQueueBoost &a, const SequentialQueueBoost &b) { - return a.q == b.q; -} - -using namespace Lincheck; - -TEST(BoostLockfreeQueueTest, QueueTest) { - LincheckConfiguration conf; - conf.iterations(1000); - conf.invocationsPerIteration(500); - conf.minimizeFailedScenario(true); - conf.threads(3); - conf.actorsPerThread(3); - - conf.operation("push"); - conf.operation, &ConcurrentQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); - ASSERT_EQ(conf.runTest(false), ""); -} \ No newline at end of file diff --git a/cpp/boost_lockfree_test.cpp b/cpp/boost_lockfree_test.cpp new file mode 100644 index 000000000..ad3bd512f --- /dev/null +++ b/cpp/boost_lockfree_test.cpp @@ -0,0 +1,184 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "lincheck.h" +#include "lincheck_functions.h" +#include +#include + +#include +#include + +class SequentialQueueBoost { +public: + std::queue q; + + bool push(int value) { + q.push(value); + return true; + } + + std::pair pop() { + int val = 0; + if(!q.empty()) { + val = q.front(); + q.pop(); + return {true, val}; + } else { + return {false, val}; + } + } +}; + +class ConcurrentQueueBoost { +public: + boost::lockfree::queue q = boost::lockfree::queue(100); + + bool push(int value) { + return q.push(value); + } + + std::pair pop() { + int val = 0; + bool success = q.pop(val); + return {success, success ? val : 0}; + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialQueueBoost &s) const noexcept { + std::vector vec; + while(!s.q.empty()) { + vec.push_back(s.q.front()); + s.q.pop(); + } + for (auto it : vec) { + s.q.push(it); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialQueueBoost &a, const SequentialQueueBoost &b) { + return a.q == b.q; +} + +class SequentialStackBoost { +public: + std::stack s; + + bool push(int value) { + s.push(value); + return true; + } + + std::pair pop() { + int val = 0; + if(!s.empty()) { + val = s.top(); + s.pop(); + return {true, val}; + } else { + return {false, val}; + } + } + + bool empty() { + return s.empty(); + } +}; + +class ConcurrentStackBoost { +public: + boost::lockfree::stack s = boost::lockfree::stack(100); + + bool push(int value) { + return s.push(value); + } + + std::pair pop() { + int val = 0; + bool success = s.pop(val); + return {success, success ? val : 0}; + } + + bool empty() { + return s.empty(); + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialStackBoost &s) const noexcept { + std::vector vec; + while(!s.s.empty()) { + vec.push_back(s.s.top()); + s.s.pop(); + } + reverse(vec.begin(), vec.end()); + for (auto it : vec) { + s.s.push(it); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialStackBoost &a, const SequentialStackBoost &b) { + return a.s == b.s; +} + +using namespace Lincheck; + +TEST(BoostLockfreeTest, BadSequentialQueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(3); + + conf.operation("push"); + conf.operation, &SequentialQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(BoostLockfreeTest, BadSequentialStackTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(3); + + conf.operation("push"); + conf.operation, &SequentialStackBoost::pop, &SequentialStackBoost::pop>("pop"); + conf.operation("empty"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(BoostLockfreeTest, QueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(3); + + conf.operation("push"); + conf.operation, &ConcurrentQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(BoostLockfreeTest, StackTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(3); + + conf.operation("push"); + conf.operation, &ConcurrentStackBoost::pop, &SequentialStackBoost::pop>("pop"); + conf.operation("empty"); + ASSERT_EQ(conf.runTest(false), ""); +} \ No newline at end of file diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index 6e5d34edb..b26e28e88 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -84,8 +84,8 @@ TEST(CounterTest, BadInc) { LincheckConfiguration conf; conf.minimizeFailedScenario(false); conf.threads(3); - conf.iterations(100); - conf.invocationsPerIteration(500); + conf.iterations(10); + conf.actorsPerThread(5); conf.operation("inc"); ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); diff --git a/cpp/folly_hashmap_test.cpp b/cpp/folly_hashmap_test.cpp deleted file mode 100644 index bf62ed22b..000000000 --- a/cpp/folly_hashmap_test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "lincheck.h" -#include "lincheck_functions.h" - -#include "folly/concurrency/ConcurrentHashMap.h" - -class SequentialMapFolly { -public: - std::unordered_map map; - - bool assign(int key, int value) { - map.insert_or_assign(key, value); - return true; // concurrent version always returns true - } - - std::pair get(int key) { - auto it = map.find(key); - if (it != map.end()) { - return {true, it->second}; - } - return {false, 0}; - } - - int erase(int key) { - return map.erase(key); - } -}; - -class ConcurrentMapFolly { -public: - folly::ConcurrentHashMap map; - - bool assign(int key, int value) { - return map.insert_or_assign(key, value).second; - } - - std::pair get(int key) { - auto it = map.find(key); - if (it != map.end()) { - return {true, it->second}; - } - return {false, 0}; - } - - int erase(int key) { - return map.erase(key); - } -}; - -template<> -struct Lincheck::hash { - std::size_t operator()(SequentialMapFolly const &s) const noexcept { - std::vector vec; - for (auto it : s.map) { - vec.push_back(it.first); - vec.push_back(it.second); - } - return Lincheck::hash>()(vec); - } -}; - -bool operator==(const SequentialMapFolly &a, const SequentialMapFolly &b) { - return a.map == b.map; -} - -using namespace Lincheck; - -TEST(follyHashMapTest, FirstTest) { - LincheckConfiguration conf; - conf.iterations(100); - conf.invocationsPerIteration(500); - conf.minimizeFailedScenario(false); - conf.threads(3); - conf.actorsPerThread(5); - - conf.operation("assign"); - conf.operation, int, &ConcurrentMapFolly::get, &SequentialMapFolly::get>("get"); - conf.operation("erase"); - ASSERT_EQ(conf.runTest(false), ""); -} \ No newline at end of file diff --git a/cpp/folly_test.cpp b/cpp/folly_test.cpp new file mode 100644 index 000000000..be3db5a6a --- /dev/null +++ b/cpp/folly_test.cpp @@ -0,0 +1,212 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "lincheck.h" +#include "lincheck_functions.h" + +#include "folly/concurrency/ConcurrentHashMap.h" +#include "folly/concurrency/DynamicBoundedQueue.h" +#include "folly/concurrency/UnboundedQueue.h" + +class SequentialMapFolly { +public: + std::unordered_map map; + + SequentialMapFolly() { + map.reserve(100); // to prevent from rehashing and crashes with SIGSEGV, SIGABRT, or new/delete issues + } + + bool assign(int key, int value) { + map.insert_or_assign(key, value); + return true; // concurrent version always returns true + } + + std::pair get(int key) { + auto it = map.find(key); + if (it != map.end()) { + return {true, it->second}; + } + return {false, 0}; + } + + int erase(int key) { + return map.erase(key); + } +}; + +class ConcurrentMapFolly { +public: + folly::ConcurrentHashMap map; + + bool assign(int key, int value) { + return map.insert_or_assign(key, value).second; + } + + std::pair get(int key) { + auto it = map.find(key); + if (it != map.end()) { + return {true, it->second}; + } + return {false, 0}; + } + + int erase(int key) { + return map.erase(key); + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialMapFolly const &s) const noexcept { + std::vector vec; + for (auto it : s.map) { + vec.push_back(it.first); + vec.push_back(it.second); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialMapFolly &a, const SequentialMapFolly &b) { + return a.map == b.map; +} + +class SequentialQueueFolly { +public: + std::queue q; + + bool push(int value) { + q.push(value); + return true; + } + + std::pair pop() { + int val = 0; + if(!q.empty()) { + val = q.front(); + q.pop(); + return {true, val}; + } else { + return {false, val}; + } + } +}; + +class ConcurrentDynamicBoundedQueueFolly { +public: + folly::DMPMCQueue queue = folly::DMPMCQueue(100); + + bool push(int val) { + return queue.try_enqueue(val); + } + + std::pair pop() { + int ans = 0; + bool success = queue.try_dequeue(ans); + return {success, ans}; + } +}; + +class ConcurrentUnboundedQueueFolly { +public: + folly::UMPMCQueue queue; + + bool push(int val) { + queue.enqueue(val); + return true; + } + + std::pair pop() { + int ans = 0; + bool success = queue.try_dequeue(ans); + return {success, ans}; + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialQueueFolly &s) const noexcept { + std::vector vec; + while(!s.q.empty()) { + vec.push_back(s.q.front()); + s.q.pop(); + } + for (auto it : vec) { + s.q.push(it); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialQueueFolly &a, const SequentialQueueFolly &b) { + return a.q == b.q; +} + +using namespace Lincheck; + +TEST(FollyTest, BadSequentialMapTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("assign"); + conf.operation, int, &SequentialMapFolly::get, &SequentialMapFolly::get>("get"); + conf.operation("erase"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(FollyTest, BadSequentialQueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &SequentialQueueFolly::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(FollyTest, HashMapTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("assign"); + conf.operation, int, &ConcurrentMapFolly::get, &SequentialMapFolly::get>("get"); + conf.operation("erase"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(FollyTest, DynamicBoundedQueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &ConcurrentDynamicBoundedQueueFolly::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); +} + + +TEST(FollyTest, UnboundedQueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &ConcurrentUnboundedQueueFolly::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); +} \ No newline at end of file diff --git a/cpp/libcds_msqueue_dhp_test.cpp b/cpp/libcds_msqueue_dhp_test.cpp deleted file mode 100644 index 7c7a38dfa..000000000 --- a/cpp/libcds_msqueue_dhp_test.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "lincheck.h" -#include "lincheck_functions.h" - -using namespace Lincheck; -using ::testing::HasSubstr; - -using queue_type = cds::container::MSQueue; - -std::vector queue_to_vector(queue_type &queue) { - std::vector ans; - while (queue.dequeue_with([&ans](int &src) { ans.push_back(src); })) {} - for(auto elem : ans) { - queue.enqueue(elem); - } - return ans; -} - -template<> -struct Lincheck::hash { - std::size_t operator()(queue_type &q) const noexcept { - return Lincheck::hash>()(queue_to_vector(q)); - } -}; - -class LibcdsQueue { -public: - queue_type queue; - - bool push(queue_type::value_type val) { - return queue.enqueue(val); - } - - std::pair pop() { - queue_type::value_type ans = 0; - bool success = queue.dequeue(ans); - return {success, ans}; - } -}; - -template<> -struct Lincheck::hash { - std::size_t operator()(LibcdsQueue &q) const noexcept { - return Lincheck::hash>()(queue_to_vector(q.queue)); - } -}; - -bool operator==(LibcdsQueue &a, LibcdsQueue &b) { - return queue_to_vector(a.queue) == queue_to_vector(b.queue); -} - -void myAttach() { - //std::string val = "attached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; - //std::cout << val; - cds::threading::Manager::attachThread(); -} - -void myDetach() { - //std::string val = "detached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; - //std::cout << val; - cds::threading::Manager::detachThread(); -} - -TEST(LibcdsMSQueueTest, QueueTest) { - cds::Initialize(); - - { - cds::gc::DHP dhpGC( - 160 //dhp_init_guard_count - ); - - myAttach(); - - LincheckConfiguration conf; - conf.iterations(100); - conf.invocationsPerIteration(500); - conf.minimizeFailedScenario(false); - conf.threads(3); - conf.actorsPerThread(4); - - conf.initThreadFunction(); - conf.finishThreadFunction(); - - conf.operation("push"); - conf.operation, &LibcdsQueue::pop, &LibcdsQueue::pop>("pop"); - ASSERT_EQ(conf.runTest(false), ""); - - myDetach(); - } - - cds::Terminate(); -} diff --git a/cpp/libcds_test.cpp b/cpp/libcds_test.cpp new file mode 100644 index 000000000..f084babfa --- /dev/null +++ b/cpp/libcds_test.cpp @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lincheck.h" +#include "lincheck_functions.h" + +class SequentialQueueLibcds { +public: + std::queue q; + + bool push(int value) { + q.push(value); + return true; + } + + std::pair pop() { + int val = 0; + if(!q.empty()) { + val = q.front(); + q.pop(); + return {true, val}; + } else { + return {false, val}; + } + } +}; + +template +class ConcurrentQueueLibcds { +public: + cds::container::MSQueue queue; + + bool push(int val) { + return queue.enqueue(val); + } + + std::pair pop() { + int ans = 0; + bool success = queue.dequeue(ans); + return {success, ans}; + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialQueueLibcds &s) const noexcept { + std::vector vec; + while(!s.q.empty()) { + vec.push_back(s.q.front()); + s.q.pop(); + } + for (auto it : vec) { + s.q.push(it); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialQueueLibcds &a, const SequentialQueueLibcds &b) { + return a.q == b.q; +} + +class SequentialStackLibcds { +public: + std::stack s; + + bool push(int value) { + s.push(value); + return true; + } + + std::pair pop() { + int val = 0; + if(!s.empty()) { + val = s.top(); + s.pop(); + return {true, val}; + } else { + return {false, val}; + } + } + + bool empty() { + return s.empty(); + } + + bool clear() { + while(!s.empty()) { + s.pop(); + } + return true; + } +}; + +template +class ConcurrentTreiberStackLibcds { +public: + cds::container::TreiberStack s; + + bool push(int value) { + return s.push(value); + } + + std::pair pop() { + int val = 0; + bool success = s.pop(val); + return {success, success ? val : 0}; + } + + bool empty() { + return s.empty(); + } + + bool clear() { + s.clear(); + return true; + } +}; + +class ConcurrentFCStackLibcds { +public: + cds::container::FCStack s; + + bool push(int value) { + return s.push(value); + } + + std::pair pop() { + int val = 0; + bool success = s.pop(val); + return {success, success ? val : 0}; + } + + bool empty() { + return s.empty(); + } + + bool clear() { + s.clear(); + return true; + } +}; + +template<> +struct Lincheck::hash { + std::size_t operator()(SequentialStackLibcds &s) const noexcept { + std::vector vec; + while(!s.s.empty()) { + vec.push_back(s.s.top()); + s.s.pop(); + } + reverse(vec.begin(), vec.end()); + for (auto it : vec) { + s.s.push(it); + } + return Lincheck::hash>()(vec); + } +}; + +bool operator==(const SequentialStackLibcds &a, const SequentialStackLibcds &b) { + return a.s == b.s; +} + +void myAttach() { + //std::string val = "attached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; + //std::cout << val; + cds::threading::Manager::attachThread(); +} + +void myDetach() { + //std::string val = "detached " + std::to_string(cds::OS::get_current_thread_id()) + "\n"; + //std::cout << val; + cds::threading::Manager::detachThread(); +} + +using namespace Lincheck; + + +TEST(LibcdsTest, BadSequentialQueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &SequentialQueueLibcds::pop, &SequentialQueueLibcds::pop>("pop"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(LibcdsTest, BadSequentialStackTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &SequentialStackLibcds::pop, &SequentialStackLibcds::pop>("pop"); + conf.operation("clear"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(LibcdsTest, ConcurrentQueueHPTest) { + cds::Initialize(); + { + cds::gc::HP dhpGC( + 16 + ); + myAttach(); + LincheckConfiguration, SequentialQueueLibcds> conf; + conf.iterations(10); + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + conf.initThreadFunction(); + conf.finishThreadFunction(); + conf.operation::push, &SequentialQueueLibcds::push>("push"); + conf.operation, &ConcurrentQueueLibcds::pop, &SequentialQueueLibcds::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); + myDetach(); + } + cds::Terminate(); +} + +TEST(LibcdsTest, ConcurrentQueueDHPTest) { + cds::Initialize(); + { + cds::gc::DHP dhpGC( + 160 //dhp_init_guard_count + ); + myAttach(); + LincheckConfiguration, SequentialQueueLibcds> conf; + conf.iterations(10); + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + conf.initThreadFunction(); + conf.finishThreadFunction(); + conf.operation::push, &SequentialQueueLibcds::push>("push"); + conf.operation, &ConcurrentQueueLibcds::pop, &SequentialQueueLibcds::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); + myDetach(); + } + cds::Terminate(); +} + +TEST(LibcdsTest, ConcurrentTreiberStackHPTest) { + cds::Initialize(); + { + cds::gc::HP dhpGC( + 16 + ); + myAttach(); + LincheckConfiguration, SequentialStackLibcds> conf; + conf.iterations(10); + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + conf.initThreadFunction(); + conf.finishThreadFunction(); + conf.operation::push, &SequentialStackLibcds::push>("push"); + conf.operation, &ConcurrentTreiberStackLibcds::pop, &SequentialStackLibcds::pop>("pop"); + conf.operation::clear, &SequentialStackLibcds::clear>("clear"); + ASSERT_EQ(conf.runTest(false), ""); + myDetach(); + } + cds::Terminate(); +} + +TEST(LibcdsTest, ConcurrentTreiberStackDHPTest) { + cds::Initialize(); + { + cds::gc::DHP dhpGC( + 160 + ); + myAttach(); + LincheckConfiguration, SequentialStackLibcds> conf; + conf.iterations(10); + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + conf.initThreadFunction(); + conf.finishThreadFunction(); + conf.operation::push, &SequentialStackLibcds::push>("push"); + conf.operation, &ConcurrentTreiberStackLibcds::pop, &SequentialStackLibcds::pop>("pop"); + conf.operation::clear, &SequentialStackLibcds::clear>("clear"); + ASSERT_EQ(conf.runTest(false), ""); + myDetach(); + } + cds::Terminate(); +} + +/* +TEST(LibcdsTest, ConcurrentFCStackTest) { + cds::Initialize(); + { + LincheckConfiguration conf; + conf.iterations(1); + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + conf.operation("push"); + conf.operation, &ConcurrentFCStackLibcds::pop, &SequentialStackLibcds::pop>("pop"); + conf.operation("clear"); + ASSERT_EQ(conf.runTest(false), ""); + } + cds::Terminate(); +} +*/ \ No newline at end of file diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp index 0ba78326b..e70a9f6f0 100644 --- a/cpp/libcuckoo_test.cpp +++ b/cpp/libcuckoo_test.cpp @@ -9,6 +9,10 @@ class SequentialMapCuckoo { public: std::unordered_map map; + SequentialMapCuckoo() { + map.reserve(100); + } + bool assign(int key, int value) { //std::cerr << "seqassign " << key << ", " << value << "\n"; auto it = map.find(key); @@ -90,10 +94,24 @@ bool operator==(const SequentialMapCuckoo &a, const SequentialMapCuckoo &b) { using namespace Lincheck; -TEST(libcuckooTest, FirstTest) { +TEST(LibcuckooTest, BadSequentialMapTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("assign"); + conf.operation("get"); + conf.operation("erase"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(LibcuckooTest, HashMapTest) { LincheckConfiguration conf; - conf.iterations(100); - conf.invocationsPerIteration(500); + conf.iterations(10); + conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(4); diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 2b5e99aa2..a9f8579e8 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -589,13 +589,13 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> - val curPercent = i.toDouble() / iterations.toDouble() - val nextPercent = (i + 1).toDouble() / iterations.toDouble() - val STEP = 0.2 - val curShare = (curPercent / STEP).toInt() - val nextShare = (nextPercent / STEP).toInt() + val curPercent = (i + 1).toDouble() / iterations.toDouble() + val nextPercent = (i + 2).toDouble() / iterations.toDouble() + val STEP = 0.1 + val curShare = (curPercent / STEP - 0.0001).toInt() + val nextShare = (nextPercent / STEP + 0.0001).toInt() if(curShare != nextShare) { - println("${(nextShare * STEP * 100).toInt()}%") + println("${i + 1}/$iterations") } val scenario = exGen.nextExecution() scenario.validate() diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 0b053ba7b..5f4511809 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -82,7 +82,10 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, init { (0 until nThreads).forEach { iThread -> - threads.array[iThread].value = TestThread(iThread, runnerHash).also { it.executeTask { initThreadFunction?.invoke(); Any() } } + threads.array[iThread].value = TestThread(iThread, runnerHash).also { + it.executeTask { initThreadFunction?.invoke(); Any() } + it.awaitLastTask(currentTimeMillis() + 10000) + } } } @@ -124,7 +127,6 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, } private fun testThreadRunnable(iThread: Int, task: Runnable): Any { - initThreadFunction?.invoke() val runnable = task try { runnable.run() @@ -138,6 +140,7 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, // submit the shutdown task. for (t in threads.toArray()) { t.executeTask { finishThreadFunction?.invoke(); Any() } + t.awaitLastTask(currentTimeMillis() + 10000) t.terminate() } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt index 0e518c616..69b50b157 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecution.kt @@ -59,7 +59,7 @@ class TestThreadExecution(val iThread: Int, val actors: List) { //printErr("RUN $iThread #2 $index") readClocks(index) //runner.onActorStart(iThread) - // Load arguments for operation + //Load arguments for operation val result: Result = try { val r = actor.function(testInstance, actor.arguments) //printErr("ValueResult") From 97650e8cd1345b600e495012536f779a74f34aa2 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 12:02:29 +0300 Subject: [PATCH 59/72] Add operation groups to native, add NonParallelOpGroupTest, add boost:locfree::spsc_queue test. Add folly [SM]P[SM]CQueue tests --- cpp/boost_lockfree_test.cpp | 46 +++++- cpp/folly_test.cpp | 135 ++++++++++++++++-- cpp/lincheck.h | 9 +- .../execution/RandomExecutionGenerator.kt | 2 +- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 30 +++- .../runner/FixedActiveThreadsExecutor.kt | 8 +- src/native/test/ExceptionAsResultTest.kt | 6 +- src/native/test/NonParallelOpGroupTest.kt | 99 +++++++++++++ 8 files changed, 308 insertions(+), 27 deletions(-) create mode 100644 src/native/test/NonParallelOpGroupTest.kt diff --git a/cpp/boost_lockfree_test.cpp b/cpp/boost_lockfree_test.cpp index ad3bd512f..11044801d 100644 --- a/cpp/boost_lockfree_test.cpp +++ b/cpp/boost_lockfree_test.cpp @@ -6,6 +6,7 @@ #include #include +#include #include class SequentialQueueBoost { @@ -31,7 +32,22 @@ class SequentialQueueBoost { class ConcurrentQueueBoost { public: - boost::lockfree::queue q = boost::lockfree::queue(100); + boost::lockfree::queue q = boost::lockfree::queue(1); + + bool push(int value) { + return q.push(value); + } + + std::pair pop() { + int val = 0; + bool success = q.pop(val); + return {success, success ? val : 0}; + } +}; + +class ConcurrentSPSCQueueBoost { +public: + boost::lockfree::spsc_queue q = boost::lockfree::spsc_queue(100); // fixed-size ring buffer bool push(int value) { return q.push(value); @@ -90,7 +106,7 @@ class SequentialStackBoost { class ConcurrentStackBoost { public: - boost::lockfree::stack s = boost::lockfree::stack(100); + boost::lockfree::stack s = boost::lockfree::stack(1); bool push(int value) { return s.push(value); @@ -181,4 +197,30 @@ TEST(BoostLockfreeTest, StackTest) { conf.operation, &ConcurrentStackBoost::pop, &SequentialStackBoost::pop>("pop"); conf.operation("empty"); ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(BoostLockfreeTest, BadSPSCQueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(3); + + conf.operation("push"); + conf.operation, &ConcurrentSPSCQueueBoost::pop, &SequentialQueueBoost::pop>("pop", "popNonParallelGroup"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(BoostLockfreeTest, SPSCQueueTest) { + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(15000); // will be shrinked to 2 + conf.actorsPerThread(3); + + conf.operation("push", "pushNonParallelGroup"); + conf.operation, &ConcurrentSPSCQueueBoost::pop, &SequentialQueueBoost::pop>("pop", "popNonParallelGroup"); + ASSERT_EQ(conf.runTest(false), ""); } \ No newline at end of file diff --git a/cpp/folly_test.cpp b/cpp/folly_test.cpp index be3db5a6a..78a4020a3 100644 --- a/cpp/folly_test.cpp +++ b/cpp/folly_test.cpp @@ -91,9 +91,10 @@ class SequentialQueueFolly { } }; +template class ConcurrentDynamicBoundedQueueFolly { public: - folly::DMPMCQueue queue = folly::DMPMCQueue(100); + DynamicBoundedQueue queue = DynamicBoundedQueue(100); bool push(int val) { return queue.try_enqueue(val); @@ -106,9 +107,10 @@ class ConcurrentDynamicBoundedQueueFolly { } }; +template class ConcurrentUnboundedQueueFolly { public: - folly::UMPMCQueue queue; + UnboundedQueue queue; bool push(int val) { queue.enqueue(val); @@ -184,29 +186,142 @@ TEST(FollyTest, HashMapTest) { ASSERT_EQ(conf.runTest(false), ""); } -TEST(FollyTest, DynamicBoundedQueueTest) { - LincheckConfiguration conf; +TEST(FollyTest, MPMCDynamicBoundedQueueTest) { + using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; + LincheckConfiguration conf; conf.iterations(10); conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(4); - conf.operation("push"); - conf.operation, &ConcurrentDynamicBoundedQueueFolly::pop, &SequentialQueueFolly::pop>("pop"); + conf.operation("push"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop"); ASSERT_EQ(conf.runTest(false), ""); } +TEST(FollyTest, MPSCDynamicBoundedQueueTest) { + using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop", "nonParallelConsumer"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(FollyTest, BadMPSCDynamicBoundedQueueTest) { + using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push", "nonParallelProducer"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(FollyTest, SPMCDynamicBoundedQueueTest) { + using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push", "nonParallelProducer"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(FollyTest, SPSCDynamicBoundedQueueTest) { + using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push", "nonParallelProducer"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop", "nonParallelConsumer"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(FollyTest, BadSPSCDynamicBoundedQueueTest) { + using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push", "nonParallelProducer"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_THAT(conf.runTest(false), ::testing::HasSubstr("Invalid execution results")); +} + +TEST(FollyTest, MPMCUnboundedQueueTest) { + using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(FollyTest, MPSCUnboundedQueueTest) { + using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop", "nonParallelConsumer"); + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(FollyTest, SPMCUnboundedQueueTest) { + using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; + LincheckConfiguration conf; + conf.iterations(10); + + conf.minimizeFailedScenario(false); + conf.threads(3); + conf.actorsPerThread(4); + + conf.operation("push", "nonParallelProducer"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop"); + ASSERT_EQ(conf.runTest(false), ""); +} -TEST(FollyTest, UnboundedQueueTest) { - LincheckConfiguration conf; +TEST(FollyTest, SPSCUnboundedQueueTest) { + using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; + LincheckConfiguration conf; conf.iterations(10); conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(4); - conf.operation("push"); - conf.operation, &ConcurrentUnboundedQueueFolly::pop, &SequentialQueueFolly::pop>("pop"); + conf.operation("push", "nonParallelProducer"); + conf.operation, &ConcurrentQueue::pop, &SequentialQueueFolly::pop>("pop", "nonParallelConsumer"); ASSERT_EQ(conf.runTest(false), ""); } \ No newline at end of file diff --git a/cpp/lincheck.h b/cpp/lincheck.h index c0cc1a152..3ebe9bd86 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -113,7 +113,7 @@ namespace Lincheck { } template - void operation(const char *operationName, bool useOnce = false) { + void operation(const char *operationName, const char *nonParallelGroupName = nullptr, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation1( configuration, (void *) (void *(*)(void *)) [](void *instance) -> void * { // operation @@ -142,13 +142,14 @@ namespace Lincheck { strncpy(dest, Lincheck::to_string()(*(Ret *) ret).c_str(), destSize); }, operationName, + nonParallelGroupName, useOnce ); } template - void operation(const char *operationName, bool useOnce = false) { + void operation(const char *operationName, const char *nonParallelGroupName = nullptr, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation2( configuration, (void *) (void *(*)()) []() -> void * { // arg1_gen_initial_state @@ -195,6 +196,7 @@ namespace Lincheck { strncpy(dest, Lincheck::to_string()(*(Ret *) ret).c_str(), destSize); }, operationName, + nonParallelGroupName, useOnce ); } @@ -203,7 +205,7 @@ namespace Lincheck { template - void operation(const char *operationName, bool useOnce = false) { + void operation(const char *operationName, const char *nonParallelGroupName = nullptr, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation3( configuration, (void *) (void *(*)()) []() -> void * { // arg1_gen_initial_state @@ -269,6 +271,7 @@ namespace Lincheck { strncpy(dest, Lincheck::to_string()(*(Ret *) ret).c_str(), destSize); }, operationName, + nonParallelGroupName, useOnce ); } diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt index d9e2b17db..37cf9d9ed 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.kt @@ -49,7 +49,7 @@ class RandomExecutionGenerator(testConfiguration: CTestConfiguration, testStruct } for (i in nonParallelGroups.indices) { threadGens[i % threadGens.size].nonParallelActorGenerators - .addAll(nonParallelGroups[i]!!.actors) + .addAll(nonParallelGroups[i].actors) } val tgs2: List = ArrayList(threadGens) while (threadGens.isNotEmpty()) { diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index a9f8579e8..0542aef1e 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -84,7 +84,7 @@ internal class ParameterGeneratorArgument(val arg: CPointer<*>, } } -internal class ConcurrentInstance(val obj: CPointer<*>, val destructor: CDestructor): Finalizable { +internal class ConcurrentInstance(val obj: CPointer<*>, val destructor: CDestructor) : Finalizable { override fun finalize() { // there are no destructors in Kotlin/Native :( https://youtrack.jetbrains.com/issue/KT-44191 @@ -251,6 +251,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { result_hashCode: HashCodeCFunction, result_toString: ToStringCFunction, operationName: String, + nonParallelGroupName: String? = null, useOnce: Boolean = false, ) = apply { val actorGenerator = ActorGenerator( @@ -274,6 +275,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { handledExceptions = emptyList() ) actorGenerators.add(actorGenerator) + addToOperationGroups(nonParallelGroupName, actorGenerator) } fun setupOperation2( @@ -288,6 +290,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { result_hashCode: HashCodeCFunction, result_toString: ToStringCFunction, operationName: String, + nonParallelGroupName: String? = null, useOnce: Boolean = false, ) = apply { val arg1_paramgen = object : ParameterGenerator { @@ -317,6 +320,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { handledExceptions = emptyList() ) actorGenerators.add(actorGenerator) + addToOperationGroups(nonParallelGroupName, actorGenerator) } fun setupOperation3( @@ -335,6 +339,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { result_hashCode: HashCodeCFunction, result_toString: ToStringCFunction, operationName: String, + nonParallelGroupName: String? = null, useOnce: Boolean = false, ) = apply { val arg1Paramgen = object : ParameterGenerator { @@ -370,6 +375,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { handledExceptions = emptyList() ) actorGenerators.add(actorGenerator) + addToOperationGroups(nonParallelGroupName, actorGenerator) } } @@ -391,7 +397,7 @@ open class LincheckStressConfiguration(protected val testName: String */ protected var testClass: TestClass? = null protected var actorGenerators = mutableListOf() - protected var operationGroups = mutableListOf() + protected var operationGroups = mutableMapOf() protected var validationFunctions = mutableListOf() protected var stateRepresentationFunction: StateRepresentationFunction? = null @@ -407,7 +413,7 @@ open class LincheckStressConfiguration(protected val testName: String protected fun getTestStructure(): CTestStructure { return CTestStructure( actorGenerators, - operationGroups, + operationGroups.values.toList(), validationFunctions, stateRepresentationFunction ) @@ -440,11 +446,21 @@ open class LincheckStressConfiguration(protected val testName: String // =========================== Operation + protected fun addToOperationGroups(nonParallelGroupName: String?, actorGenerator: ActorGenerator) { + nonParallelGroupName?.let { + if (!operationGroups.containsKey(nonParallelGroupName)) { + operationGroups[nonParallelGroupName] = OperationGroup(nonParallelGroupName, true) + } + operationGroups[nonParallelGroupName]!!.actors.add(actorGenerator) + } + } + fun operation( pGens: List>, op: Instance.(List) -> R, name: String = op.toString(), handleExceptionsAsResult: List> = emptyList(), + nonParallelGroupName: String? = null, useOnce: Boolean = false, isSuspendable: Boolean = false ) = apply { @@ -460,12 +476,14 @@ open class LincheckStressConfiguration(protected val testName: String handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) + addToOperationGroups(nonParallelGroupName, actorGenerator) } fun operation( op: Instance.() -> R, name: String = op.toString(), handleExceptionsAsResult: List> = emptyList(), + nonParallelGroupName: String? = null, useOnce: Boolean = false, isSuspendable: Boolean = false ) = apply { @@ -481,6 +499,7 @@ open class LincheckStressConfiguration(protected val testName: String handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) + addToOperationGroups(nonParallelGroupName, actorGenerator) } fun operation( @@ -488,6 +507,7 @@ open class LincheckStressConfiguration(protected val testName: String op: Instance.(p1: P1) -> R, name: String = op.toString(), handleExceptionsAsResult: List> = emptyList(), + nonParallelGroupName: String? = null, useOnce: Boolean = false, isSuspendable: Boolean = false ) = apply { @@ -503,6 +523,7 @@ open class LincheckStressConfiguration(protected val testName: String handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) + addToOperationGroups(nonParallelGroupName, actorGenerator) } fun operation( @@ -511,6 +532,7 @@ open class LincheckStressConfiguration(protected val testName: String op: Instance.(p1: P1, p2: P2) -> R, name: String = op.toString(), handleExceptionsAsResult: List> = emptyList(), + nonParallelGroupName: String? = null, useOnce: Boolean = false, isSuspendable: Boolean = false ) = apply { @@ -526,6 +548,7 @@ open class LincheckStressConfiguration(protected val testName: String handledExceptions = handleExceptionsAsResult ) actorGenerators.add(actorGenerator) + addToOperationGroups(nonParallelGroupName, actorGenerator) } // ============================= Validation Function @@ -589,6 +612,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val exGen = createExecutionGenerator() val verifier = createVerifier() repeat(iterations) { i -> + // some magic computations for beautiful values val curPercent = (i + 1).toDouble() / iterations.toDouble() val nextPercent = (i + 2).toDouble() / iterations.toDouble() val STEP = 0.1 diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 5f4511809..2c68251bb 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -104,13 +104,11 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, } private fun submitTasks(tasks: Array) { + val testThreads = Array(nThreads){null} for (i in 0 until nThreads) { - submitTask(i, tasks[i]) + testThreads[i] = threads.array[i].value!! // Do atomic loads } - } - - private fun submitTask(iThread: Int, task: Any) { - threads.array[iThread].value!!.executeTask { testThreadRunnable(iThread, task as Runnable) } + testThreads.forEachIndexed{i, t -> t!!.executeTask { testThreadRunnable(i, tasks[i] as Runnable) }} // submit tasks } private fun await(timeoutMs: Long) { diff --git a/src/native/test/ExceptionAsResultTest.kt b/src/native/test/ExceptionAsResultTest.kt index 6fb45783c..f39950c73 100644 --- a/src/native/test/ExceptionAsResultTest.kt +++ b/src/native/test/ExceptionAsResultTest.kt @@ -1,6 +1,3 @@ -import org.jetbrains.kotlinx.lincheck.* -import kotlin.test.* - /* * #%L * Lincheck @@ -23,6 +20,9 @@ import kotlin.test.* * #L% */ +import org.jetbrains.kotlinx.lincheck.* +import kotlin.test.* + class ExceptionAsResultTest { fun npeIsOk() { (null as String?)!![0] diff --git a/src/native/test/NonParallelOpGroupTest.kt b/src/native/test/NonParallelOpGroupTest.kt new file mode 100644 index 000000000..918afbe6a --- /dev/null +++ b/src/native/test/NonParallelOpGroupTest.kt @@ -0,0 +1,99 @@ +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.strategy.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import kotlin.test.* + +/* + * Lincheck + * %% + * Copyright (C) 2015 - 2018 Devexperts, LLC + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +class NonParallelOpGroupTest : VerifierState() { + private var producerCounter = 0 + private var consumerCounter = 0 + + fun produce(count: Int): Int { + producerCounter += count + return producerCounter + } + + fun produce2(count: Int): Int { + producerCounter -= count + return producerCounter + } + + fun consume(): Int { + consumerCounter++ + return consumerCounter + } + + fun consume2(): Int { + consumerCounter-- + return consumerCounter + } + + override fun extractState(): Any { + return Pair(producerCounter, consumerCounter) + } + + @Test + fun test_failing() { + val f = LincheckStressConfiguration("NonParallelOpGroupTest").apply { + iterations(50) + invocationsPerIteration(500) + actorsBefore(2) + threads(2) + actorsPerThread(5) + actorsAfter(2) + minimizeFailedScenario(false) + + initialState { NonParallelOpGroupTest() } + stateRepresentation { this.toString() } + + operation(IntGen(""), NonParallelOpGroupTest::produce, "produce") + operation(IntGen(""), NonParallelOpGroupTest::produce, "produce2") + operation(NonParallelOpGroupTest::consume, "consume") + operation(NonParallelOpGroupTest::consume, "consume2") + }.checkImpl() + assert(f != null && f is IncorrectResultsFailure) { + "This test should fail with a incorrect results error" + } + } + + @Test + fun test_working() { + LincheckStressConfiguration("NonParallelOpGroupTest").apply { + iterations(10) + invocationsPerIteration(500) + actorsBefore(2) + threads(15000) // will be shrinked to 2 + actorsPerThread(5) + actorsAfter(2) + minimizeFailedScenario(false) + + initialState { NonParallelOpGroupTest() } + stateRepresentation { this.toString() } + + operation(IntGen(""), NonParallelOpGroupTest::produce, "produce", nonParallelGroupName = "produce") + operation(IntGen(""), NonParallelOpGroupTest::produce2, "produce2", nonParallelGroupName = "produce") + operation(NonParallelOpGroupTest::consume, "consume", nonParallelGroupName = "consume") + operation(NonParallelOpGroupTest::consume2, "consume2", nonParallelGroupName = "consume") + }.runTest() + } +} \ No newline at end of file From b5c5f460c54444f27b227ed4ba01446860bc1a2a Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 12:12:50 +0300 Subject: [PATCH 60/72] Improve tests stability --- src/native/test/AbstractLincheckTest.kt | 4 ++-- src/native/test/StateEquivalenceImplCheckTest.kt | 1 - src/native/test/UnexpectedExceptionTest.kt | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/native/test/AbstractLincheckTest.kt b/src/native/test/AbstractLincheckTest.kt index 2ad06f17f..34b8a6694 100644 --- a/src/native/test/AbstractLincheckTest.kt +++ b/src/native/test/AbstractLincheckTest.kt @@ -60,8 +60,8 @@ abstract class AbstractLincheckStressTest( } */ private fun > T.commonConfiguration(): Unit = run { - iterations(100) - invocationsPerIteration(500) + iterations(250) + invocationsPerIteration(100) actorsBefore(3) threads(3) actorsPerThread(4) diff --git a/src/native/test/StateEquivalenceImplCheckTest.kt b/src/native/test/StateEquivalenceImplCheckTest.kt index a918c30c8..94a45ba73 100644 --- a/src/native/test/StateEquivalenceImplCheckTest.kt +++ b/src/native/test/StateEquivalenceImplCheckTest.kt @@ -34,7 +34,6 @@ class StateEquivalenceImplCheckTest { try { LincheckStressConfiguration("StateEquivalenceImplCheckTest").apply { initialState { StateEquivalenceImplCheckTest() } - invocationsPerIteration(500) operation(StateEquivalenceImplCheckTest::incAndGet) }.runTest() diff --git a/src/native/test/UnexpectedExceptionTest.kt b/src/native/test/UnexpectedExceptionTest.kt index b5f4ff75b..0f76cc35a 100644 --- a/src/native/test/UnexpectedExceptionTest.kt +++ b/src/native/test/UnexpectedExceptionTest.kt @@ -38,8 +38,8 @@ class UnexpectedExceptionTest : AbstractLincheckStressTest> T.customize() { - iterations(100) - invocationsPerIteration(500) + iterations(250) + invocationsPerIteration(100) initialState { UnexpectedExceptionTest() } From f41271e0de200941c01d3798af24d27d1d6edb50 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 12:15:21 +0300 Subject: [PATCH 61/72] Improve NonParallelOpGroupTest stability --- src/native/test/NonParallelOpGroupTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/test/NonParallelOpGroupTest.kt b/src/native/test/NonParallelOpGroupTest.kt index 918afbe6a..f5e7bd179 100644 --- a/src/native/test/NonParallelOpGroupTest.kt +++ b/src/native/test/NonParallelOpGroupTest.kt @@ -55,8 +55,8 @@ class NonParallelOpGroupTest : VerifierState() { @Test fun test_failing() { val f = LincheckStressConfiguration("NonParallelOpGroupTest").apply { - iterations(50) - invocationsPerIteration(500) + iterations(500) + invocationsPerIteration(100) actorsBefore(2) threads(2) actorsPerThread(5) From 8dc92a56bc49d5dc758b487bd22fcf5ff09b9e53 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 12:34:15 +0300 Subject: [PATCH 62/72] Fix UnexpecedExceptionTest --- src/native/test/UnexpectedExceptionTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/test/UnexpectedExceptionTest.kt b/src/native/test/UnexpectedExceptionTest.kt index 0f76cc35a..b686eacb6 100644 --- a/src/native/test/UnexpectedExceptionTest.kt +++ b/src/native/test/UnexpectedExceptionTest.kt @@ -38,8 +38,8 @@ class UnexpectedExceptionTest : AbstractLincheckStressTest> T.customize() { - iterations(250) - invocationsPerIteration(100) + iterations(500) + invocationsPerIteration(250) initialState { UnexpectedExceptionTest() } From 2029c5f55f25d36e7a4d636b7c86e9908e771a66 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 12:57:26 +0300 Subject: [PATCH 63/72] Fix UnexpecedExceptionTest with atomic --- src/native/test/UnexpectedExceptionTest.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/native/test/UnexpectedExceptionTest.kt b/src/native/test/UnexpectedExceptionTest.kt index b686eacb6..adeb7cb9d 100644 --- a/src/native/test/UnexpectedExceptionTest.kt +++ b/src/native/test/UnexpectedExceptionTest.kt @@ -22,24 +22,26 @@ import org.jetbrains.kotlinx.lincheck.LincheckStressConfiguration import org.jetbrains.kotlinx.lincheck.strategy.* +import kotlin.native.concurrent.* class UnexpectedExceptionTest : AbstractLincheckStressTest(UnexpectedExceptionFailure::class) { - private var canEnterForbiddenSection = false + private var canEnterForbiddenSection: AtomicInt = AtomicInt(0) fun operation1() { - canEnterForbiddenSection = true - canEnterForbiddenSection = false + // atomic because of possible compile-time optimization + canEnterForbiddenSection.value = 1 + canEnterForbiddenSection.value = 0 } fun operation2() { - check(!canEnterForbiddenSection) + check(canEnterForbiddenSection.value == 0) } - override fun extractState(): Any = canEnterForbiddenSection + override fun extractState(): Any = canEnterForbiddenSection.value override fun > T.customize() { - iterations(500) - invocationsPerIteration(250) + iterations(100) + invocationsPerIteration(100) initialState { UnexpectedExceptionTest() } From ce7560b6e39236d9236495707d77689609b122fb Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 13:00:57 +0300 Subject: [PATCH 64/72] Fix HashMapTest possible IllegalStateException --- src/native/test/verifier/linearizability/HashMapTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/test/verifier/linearizability/HashMapTest.kt b/src/native/test/verifier/linearizability/HashMapTest.kt index 67a92c3be..b67c7adb1 100644 --- a/src/native/test/verifier/linearizability/HashMapTest.kt +++ b/src/native/test/verifier/linearizability/HashMapTest.kt @@ -38,8 +38,8 @@ class HashMapTest : AbstractLincheckStressTest(IncorrectResultsFail val keyGen = IntGen("") - operation(IntGen(""), keyGen, HashMapTest::put) - operation(keyGen, HashMapTest::get) + operation(IntGen(""), keyGen, HashMapTest::put, handleExceptionsAsResult = listOf(IllegalStateException::class)) + operation(keyGen, HashMapTest::get, handleExceptionsAsResult = listOf(IllegalStateException::class)) } override fun extractState(): Any = m From db71d4ab7f20227b40c568cc1496ee292eab45b9 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 13:02:57 +0300 Subject: [PATCH 65/72] Fix HashMapTest possible UnexpectedExceptionFailure(IllegalStateException from HashMap) --- src/native/test/verifier/linearizability/HashMapTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/test/verifier/linearizability/HashMapTest.kt b/src/native/test/verifier/linearizability/HashMapTest.kt index b67c7adb1..9dbc861aa 100644 --- a/src/native/test/verifier/linearizability/HashMapTest.kt +++ b/src/native/test/verifier/linearizability/HashMapTest.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.* -class HashMapTest : AbstractLincheckStressTest(IncorrectResultsFailure::class) { +class HashMapTest : AbstractLincheckStressTest(IncorrectResultsFailure::class, UnexpectedExceptionFailure::class) { private val m = HashMap() fun put(key: Int, value: Int): Int? = m.put(key, value) @@ -38,8 +38,8 @@ class HashMapTest : AbstractLincheckStressTest(IncorrectResultsFail val keyGen = IntGen("") - operation(IntGen(""), keyGen, HashMapTest::put, handleExceptionsAsResult = listOf(IllegalStateException::class)) - operation(keyGen, HashMapTest::get, handleExceptionsAsResult = listOf(IllegalStateException::class)) + operation(IntGen(""), keyGen, HashMapTest::put) + operation(keyGen, HashMapTest::get) } override fun extractState(): Any = m From d30852b9f6b7893709e1fc32fe760269e3f39a2f Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Thu, 6 May 2021 14:03:32 +0300 Subject: [PATCH 66/72] Attempt to fix UnexpectedExceptionTest --- src/native/test/UnexpectedExceptionTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/native/test/UnexpectedExceptionTest.kt b/src/native/test/UnexpectedExceptionTest.kt index adeb7cb9d..677918163 100644 --- a/src/native/test/UnexpectedExceptionTest.kt +++ b/src/native/test/UnexpectedExceptionTest.kt @@ -29,8 +29,8 @@ class UnexpectedExceptionTest : AbstractLincheckStressTest> T.customize() { - iterations(100) - invocationsPerIteration(100) + iterations(500) + invocationsPerIteration(200) initialState { UnexpectedExceptionTest() } From e71ad9820815570cc0d572ccfabdb3e70d102556 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 8 May 2021 15:12:17 +0300 Subject: [PATCH 67/72] Add ValidationTest --- cpp/CMakeLists.txt | 6 +++- cpp/counter_test.cpp | 21 ++++++++++++++ cpp/lincheck.h | 18 ++++++++++++ .../jetbrains/kotlinx/lincheck/LinChecker.kt | 29 ++++++++++++++++--- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 954559f1f..f1413531d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -5,7 +5,11 @@ set(CMAKE_CXX_STANDARD 17) # add lincheck shared library add_library(lincheck_library SHARED IMPORTED GLOBAL) -set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/releaseShared/libnative.so") +if(EXISTS "${PROJECT_SOURCE_DIR}/../build/bin/native/releaseShared/libnative.so") #linux .so + set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/releaseShared/libnative.so") +else() # mac .dylib + set_target_properties(lincheck_library PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../build/bin/native/releaseShared/libnative.dylib") +endif() # add gtest include(FetchContent) diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index b26e28e88..6432ecbda 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -30,6 +30,7 @@ class Counter { public: int sharedState = 0; std::atomic_int sharedAtomicState = 0; + int counter_validate_invocations = 0; int inc() { auto ans = ++sharedState; @@ -64,6 +65,15 @@ class Counter { //std::cout << "atomic_add " << value << " finished" << std::endl; return ans; } + + void validate_no_error() {} + + void validate_with_error() { + counter_validate_invocations++; + if (counter_validate_invocations == 5) { + throw std::runtime_error("ValidationRuntimeError"); + } + } }; template<> @@ -134,3 +144,14 @@ TEST(CounterTest, GoodAtomicAdd) { conf.operation("add"); ASSERT_EQ(conf.runTest(false), ""); } + +TEST(CounterTest, TEST_SHOULD_FAIL_ValidateFunctionsTest) { + LincheckConfiguration conf; + conf.iterations(1); + conf.invocationsPerIteration(1); + conf.threads(2); + conf.validationFunction<&Counter::validate_no_error>(); + conf.validationFunction<&Counter::validate_with_error>(); + conf.operation("add"); + ASSERT_EQ(conf.runTest(false), ""); +} diff --git a/cpp/lincheck.h b/cpp/lincheck.h index 3ebe9bd86..db0ea2cb0 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -112,6 +112,24 @@ namespace Lincheck { configuration, (void *) (void (*)()) []() { f(); }); } + template + void validationFunction() { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupValidationFunction( + configuration, + (void *) (void (*)(void *)) [](void *instance) -> void { // validate + auto *obj = (TestClass *) instance; // add type to void* + try { + (obj->*validate)(); // invoke op method + } catch(const std::exception& e) { + std::string message = "Validation error: \"" + std::string(e.what()) + "\""; + libnative_symbols()->kotlin.root.org.jetbrains.kotlinx.lincheck.throwKotlinValidationException(message.c_str()); + } catch(...) { + std::cerr << "Validate function should throw std::exception, but something different was thrown\n"; + } + } + ); + } + template void operation(const char *operationName, const char *nonParallelGroupName = nullptr, bool useOnce = false) { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupOperation1( diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 0542aef1e..3a0c73512 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -114,6 +114,10 @@ internal class SequentialSpecificationInstance(val obj: CPointer<*>, } } +fun throwKotlinValidationException(message: String) { + throw RuntimeException("Validation failure: $message") +} + class NativeAPIStressConfiguration : LincheckStressConfiguration() { private var initialStateCreator: CCreator? = null private var initialStateDestructor: CDestructor? = null @@ -163,6 +167,23 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { minimizeFailedScenario(minimizeFailedScenario) } + fun setupValidationFunction(function: CPointer) -> CPointer<*>>>) { + validationFunction({ + when (this) { + is ConcurrentInstance -> { + try { + function.invoke(this.obj) + } catch(e: RuntimeException) { + throw IllegalStateException(e) + } + } + else -> { + throw RuntimeException("Internal error. ValidationFunction has invoked not on ConcurrentInstance") + } + } + }) + } + fun setupInitThreadFunction(function: CPointer Unit>>) { initThreadFunction { function.invoke() } } @@ -264,7 +285,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { ObjectWithDestructorAndEqualsAndHashcodeAndToString(seq_spec.invoke(instance.obj), result_destructor, result_equals, result_hashCode, result_toString) } else -> { - throw RuntimeException("Internal error. instance has not expected type") + throw RuntimeException("Internal error. Instance has not expected type") } } }, @@ -309,7 +330,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { ObjectWithDestructorAndEqualsAndHashcodeAndToString(seq_spec.invoke(instance.obj, (arguments[0] as ParameterGeneratorArgument).arg), result_destructor, result_equals, result_hashCode, result_toString) } else -> { - throw RuntimeException("Internal error. instance has not expected type") + throw RuntimeException("Internal error. Instance has not expected type") } } }, @@ -364,7 +385,7 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { ObjectWithDestructorAndEqualsAndHashcodeAndToString(seq_spec.invoke(instance.obj, (arguments[0] as ParameterGeneratorArgument).arg, (arguments[1] as ParameterGeneratorArgument).arg), result_destructor, result_equals, result_hashCode, result_toString) } else -> { - throw RuntimeException("Internal error. instance has not expected type") + throw RuntimeException("Internal error. Instance has not expected type") } } }, @@ -618,7 +639,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT val STEP = 0.1 val curShare = (curPercent / STEP - 0.0001).toInt() val nextShare = (nextPercent / STEP + 0.0001).toInt() - if(curShare != nextShare) { + if (curShare != nextShare) { println("${i + 1}/$iterations") } val scenario = exGen.nextExecution() From 4a27867539bb022d16085fc275edc37d0761b473 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sat, 8 May 2021 16:12:06 +0300 Subject: [PATCH 68/72] Add EpsilonVerifierTest --- cpp/counter_test.cpp | 27 +++++++++++++++++++ cpp/lincheck.h | 7 +++++ .../jetbrains/kotlinx/lincheck/LinChecker.kt | 4 +++ 3 files changed, 38 insertions(+) diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index 6432ecbda..771f54a17 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -155,3 +155,30 @@ TEST(CounterTest, TEST_SHOULD_FAIL_ValidateFunctionsTest) { conf.operation("add"); ASSERT_EQ(conf.runTest(false), ""); } + +TEST(ConterTest, EnabledVerifierTest) { + // This test is needed to compare speed with DisabledVerifierTest + LincheckConfiguration conf; + + conf.iterations(25); + conf.invocationsPerIteration(10); + conf.threads(10); + conf.actorsPerThread(10); + conf.operation("add"); + + ASSERT_EQ(conf.runTest(false), ""); +} + +TEST(CounterTest, DisabledVerifierTest) { + // Compare speed with EnabledVerifierTest + LincheckConfiguration conf; + conf.iterations(25); + conf.invocationsPerIteration(10); + // a lot of threads and parallel actors + conf.threads(10); + conf.actorsPerThread(10); + conf.operation("add"); + + conf.disableVerifier(); + ASSERT_EQ(conf.runTest(false), ""); // This should be incredibly fast because of disabled verifier +} \ No newline at end of file diff --git a/cpp/lincheck.h b/cpp/lincheck.h index db0ea2cb0..6ffedc8ab 100644 --- a/cpp/lincheck.h +++ b/cpp/lincheck.h @@ -112,6 +112,13 @@ namespace Lincheck { configuration, (void *) (void (*)()) []() { f(); }); } + /* + * Change verifier to EpsilonVerifier + */ + void disableVerifier() { + lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.disableVerifier(configuration); + } + template void validationFunction() { lib->kotlin.root.org.jetbrains.kotlinx.lincheck.NativeAPIStressConfiguration.setupValidationFunction( diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 3a0c73512..5bcb0a695 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -167,6 +167,10 @@ class NativeAPIStressConfiguration : LincheckStressConfiguration() { minimizeFailedScenario(minimizeFailedScenario) } + fun disableVerifier() { + verifier { sequentialSpecification -> EpsilonVerifier(sequentialSpecification) } + } + fun setupValidationFunction(function: CPointer) -> CPointer<*>>>) { validationFunction({ when (this) { From bba96485e89c96cbee2aeb75459ac05ccde35bc4 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 12 May 2021 18:58:42 +0300 Subject: [PATCH 69/72] add scenario finalization, minor fixes --- cpp/boost_lockfree_test.cpp | 2 ++ .../jetbrains/kotlinx/lincheck/LinChecker.kt | 20 ++++++------------- .../jetbrains/kotlinx/lincheck/NativeUtils.kt | 6 +++--- .../runner/FixedActiveThreadsExecutor.kt | 6 +----- .../strategy/stress/StressStrategy.kt | 1 + 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/cpp/boost_lockfree_test.cpp b/cpp/boost_lockfree_test.cpp index 11044801d..0ecb1c928 100644 --- a/cpp/boost_lockfree_test.cpp +++ b/cpp/boost_lockfree_test.cpp @@ -175,10 +175,12 @@ TEST(BoostLockfreeTest, BadSequentialStackTest) { TEST(BoostLockfreeTest, QueueTest) { LincheckConfiguration conf; conf.iterations(10); + conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(3); + conf.actorsAfter(10); conf.operation("push"); conf.operation, &ConcurrentQueueBoost::pop, &SequentialQueueBoost::pop>("pop"); diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 5bcb0a695..008e78f46 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -635,16 +635,14 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT private fun CTestConfiguration.checkImpl(): LincheckFailure? { val exGen = createExecutionGenerator() - val verifier = createVerifier() + val REPORT_PERIOD_MS = 1000 + var prevReport = currentTimeMillis() - REPORT_PERIOD_MS repeat(iterations) { i -> + val verifier = createVerifier() // Could be created once, but created on every iteration to save memory and finalize scenario // some magic computations for beautiful values - val curPercent = (i + 1).toDouble() / iterations.toDouble() - val nextPercent = (i + 2).toDouble() / iterations.toDouble() - val STEP = 0.1 - val curShare = (curPercent / STEP - 0.0001).toInt() - val nextShare = (nextPercent / STEP + 0.0001).toInt() - if (curShare != nextShare) { + if (currentTimeMillis() - prevReport >= REPORT_PERIOD_MS) { println("${i + 1}/$iterations") + prevReport = currentTimeMillis() } val scenario = exGen.nextExecution() scenario.validate() @@ -656,7 +654,7 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT reporter.logFailedIteration(minimizedFailedIteration) return minimizedFailedIteration } - //scenario.finalize() leads to fail ?? + scenario.finalize() } return null } @@ -723,12 +721,6 @@ class LinChecker(private val testClass: TestClass, private val testStructure: CT verifier = verifier ).run() - private fun ExecutionScenario.copy() = ExecutionScenario( - ArrayList(initExecution), - parallelExecution.map { ArrayList(it) }, - ArrayList(postExecution) - ) - private val ExecutionScenario.isValid: Boolean get() = !isParallelPartEmpty && (!hasSuspendableActors() || (!hasSuspendableActorsInInitPart && !hasPostPartAndSuspendableActors)) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt index af430ded8..5092b68cc 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/NativeUtils.kt @@ -30,9 +30,9 @@ import kotlin.native.concurrent.* import kotlin.reflect.* actual class TestClass( - val function: () -> Any? + val create: () -> Any? ) { - actual fun createInstance(): Any = function() ?: throw IllegalArgumentException("Constructor should not return null") + actual fun createInstance(): Any = create() ?: throw IllegalArgumentException("Constructor should not return null") } actual class SequentialSpecification (val function: () -> Any?) @@ -62,7 +62,7 @@ internal actual fun ExecutionScenario.convertForLoader(loader: Any): ExecutionSc } actual fun chooseSequentialSpecification(sequentialSpecificationByUser: SequentialSpecification<*>?, testClass: TestClass): SequentialSpecification<*> = - sequentialSpecificationByUser ?: SequentialSpecification(testClass.function) + sequentialSpecificationByUser ?: SequentialSpecification(testClass.create) actual fun storeCancellableContinuation(cont: CancellableContinuation<*>) { TODO("Not yet implemented") diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index 2c68251bb..f805178b1 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -30,9 +30,6 @@ import kotlin.native.ThreadLocal import kotlin.native.internal.test.* import kotlin.system.* -@ThreadLocal -internal val currentThreadId = Any() - internal class TestThread constructor(val iThread: Int, runnerHash: Int) { val worker = AtomicReference(Worker.start(true, "Worker $iThread $runnerHash")) val runnableFuture = AtomicReference?>(null) @@ -42,7 +39,7 @@ internal class TestThread constructor(val iThread: Int, runnerHash: Int) { } fun awaitLastTask(deadline: Long): Any { - while(deadline - currentTimeMillis() > 0) { + while(deadline > currentTimeMillis()) { if (runnableFuture.value!!.state.value == FutureState.COMPUTED.value) { return runnableFuture.value!!.result } @@ -138,7 +135,6 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, // submit the shutdown task. for (t in threads.toArray()) { t.executeTask { finishThreadFunction?.invoke(); Any() } - t.awaitLastTask(currentTimeMillis() + 10000) t.terminate() } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 56fcb1dbe..7b3515bb8 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -71,6 +71,7 @@ actual class StressStrategy actual constructor( //println("invocation $invocation has invocated, took ${t2 - t1}ms to run") when (ir) { is CompletedInvocationResult -> { + //printErr("Try Verify") if (!verifier.verifyResults(scenario, ir.results)) { return IncorrectResultsFailure(scenario, ir.results) } else { From d8993d44a9ea51a4221247906ebe2b6cf12ff264 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 12 May 2021 18:59:51 +0300 Subject: [PATCH 70/72] Add import --- src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index 008e78f46..eb0c5f0af 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -23,6 +23,7 @@ package org.jetbrains.kotlinx.lincheck import kotlinx.cinterop.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* From b8bcfee27a1b45448952b0ad0104ee5a0eb33650 Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Wed, 12 May 2021 19:04:26 +0300 Subject: [PATCH 71/72] Fix OpGroupTests --- cpp/boost_lockfree_test.cpp | 2 +- src/native/test/NonParallelOpGroupTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/boost_lockfree_test.cpp b/cpp/boost_lockfree_test.cpp index 0ecb1c928..886184eec 100644 --- a/cpp/boost_lockfree_test.cpp +++ b/cpp/boost_lockfree_test.cpp @@ -219,7 +219,7 @@ TEST(BoostLockfreeTest, SPSCQueueTest) { conf.iterations(10); conf.minimizeFailedScenario(false); - conf.threads(15000); // will be shrinked to 2 + conf.threads(2); conf.actorsPerThread(3); conf.operation("push", "pushNonParallelGroup"); diff --git a/src/native/test/NonParallelOpGroupTest.kt b/src/native/test/NonParallelOpGroupTest.kt index f5e7bd179..1d191cc69 100644 --- a/src/native/test/NonParallelOpGroupTest.kt +++ b/src/native/test/NonParallelOpGroupTest.kt @@ -82,7 +82,7 @@ class NonParallelOpGroupTest : VerifierState() { iterations(10) invocationsPerIteration(500) actorsBefore(2) - threads(15000) // will be shrinked to 2 + threads(2) actorsPerThread(5) actorsAfter(2) minimizeFailedScenario(false) From faed7edb8d1800ce3f8ccdc11f9e0419f9194bcd Mon Sep 17 00:00:00 2001 From: Krock21rus Date: Sun, 30 May 2021 22:37:03 +0300 Subject: [PATCH 72/72] improve cpp tests and fix some bugs --- cpp/boost_lockfree_test.cpp | 12 ++++----- cpp/counter_test.cpp | 4 +-- cpp/folly_test.cpp | 26 +++++++++---------- cpp/libcds_test.cpp | 21 +++++---------- cpp/libcuckoo_test.cpp | 4 +-- .../kotlinx/lincheck/verifier/LTS.kt | 22 +++++++--------- .../jetbrains/kotlinx/lincheck/LinChecker.kt | 8 ++++-- .../runner/FixedActiveThreadsExecutor.kt | 15 ++++++++++- .../strategy/stress/StressStrategy.kt | 2 ++ 9 files changed, 61 insertions(+), 53 deletions(-) diff --git a/cpp/boost_lockfree_test.cpp b/cpp/boost_lockfree_test.cpp index 886184eec..34c1c77a5 100644 --- a/cpp/boost_lockfree_test.cpp +++ b/cpp/boost_lockfree_test.cpp @@ -147,7 +147,7 @@ using namespace Lincheck; TEST(BoostLockfreeTest, BadSequentialQueueTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -160,7 +160,7 @@ TEST(BoostLockfreeTest, BadSequentialQueueTest) { TEST(BoostLockfreeTest, BadSequentialStackTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -174,7 +174,7 @@ TEST(BoostLockfreeTest, BadSequentialStackTest) { TEST(BoostLockfreeTest, QueueTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); @@ -189,7 +189,7 @@ TEST(BoostLockfreeTest, QueueTest) { TEST(BoostLockfreeTest, StackTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -203,7 +203,7 @@ TEST(BoostLockfreeTest, StackTest) { TEST(BoostLockfreeTest, BadSPSCQueueTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -216,7 +216,7 @@ TEST(BoostLockfreeTest, BadSPSCQueueTest) { TEST(BoostLockfreeTest, SPSCQueueTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(2); diff --git a/cpp/counter_test.cpp b/cpp/counter_test.cpp index 771f54a17..4a84d7954 100644 --- a/cpp/counter_test.cpp +++ b/cpp/counter_test.cpp @@ -94,7 +94,7 @@ TEST(CounterTest, BadInc) { LincheckConfiguration conf; conf.minimizeFailedScenario(false); conf.threads(3); - conf.iterations(10); + conf.iterations(100); conf.actorsPerThread(5); conf.operation("inc"); @@ -156,7 +156,7 @@ TEST(CounterTest, TEST_SHOULD_FAIL_ValidateFunctionsTest) { ASSERT_EQ(conf.runTest(false), ""); } -TEST(ConterTest, EnabledVerifierTest) { +TEST(CounterTest, EnabledVerifierTest) { // This test is needed to compare speed with DisabledVerifierTest LincheckConfiguration conf; diff --git a/cpp/folly_test.cpp b/cpp/folly_test.cpp index 78a4020a3..2a9a7146a 100644 --- a/cpp/folly_test.cpp +++ b/cpp/folly_test.cpp @@ -147,7 +147,7 @@ using namespace Lincheck; TEST(FollyTest, BadSequentialMapTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -161,7 +161,7 @@ TEST(FollyTest, BadSequentialMapTest) { TEST(FollyTest, BadSequentialQueueTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -174,7 +174,7 @@ TEST(FollyTest, BadSequentialQueueTest) { TEST(FollyTest, HashMapTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -189,7 +189,7 @@ TEST(FollyTest, HashMapTest) { TEST(FollyTest, MPMCDynamicBoundedQueueTest) { using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -203,7 +203,7 @@ TEST(FollyTest, MPMCDynamicBoundedQueueTest) { TEST(FollyTest, MPSCDynamicBoundedQueueTest) { using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -217,7 +217,7 @@ TEST(FollyTest, MPSCDynamicBoundedQueueTest) { TEST(FollyTest, BadMPSCDynamicBoundedQueueTest) { using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -231,7 +231,7 @@ TEST(FollyTest, BadMPSCDynamicBoundedQueueTest) { TEST(FollyTest, SPMCDynamicBoundedQueueTest) { using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -245,7 +245,7 @@ TEST(FollyTest, SPMCDynamicBoundedQueueTest) { TEST(FollyTest, SPSCDynamicBoundedQueueTest) { using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -259,7 +259,7 @@ TEST(FollyTest, SPSCDynamicBoundedQueueTest) { TEST(FollyTest, BadSPSCDynamicBoundedQueueTest) { using ConcurrentQueue = ConcurrentDynamicBoundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -273,7 +273,7 @@ TEST(FollyTest, BadSPSCDynamicBoundedQueueTest) { TEST(FollyTest, MPMCUnboundedQueueTest) { using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -287,7 +287,7 @@ TEST(FollyTest, MPMCUnboundedQueueTest) { TEST(FollyTest, MPSCUnboundedQueueTest) { using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -301,7 +301,7 @@ TEST(FollyTest, MPSCUnboundedQueueTest) { TEST(FollyTest, SPMCUnboundedQueueTest) { using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -315,7 +315,7 @@ TEST(FollyTest, SPMCUnboundedQueueTest) { TEST(FollyTest, SPSCUnboundedQueueTest) { using ConcurrentQueue = ConcurrentUnboundedQueueFolly>; LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); diff --git a/cpp/libcds_test.cpp b/cpp/libcds_test.cpp index f084babfa..b91ed0585 100644 --- a/cpp/libcds_test.cpp +++ b/cpp/libcds_test.cpp @@ -184,7 +184,7 @@ using namespace Lincheck; TEST(LibcdsTest, BadSequentialQueueTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -197,7 +197,7 @@ TEST(LibcdsTest, BadSequentialQueueTest) { TEST(LibcdsTest, BadSequentialStackTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -215,9 +215,9 @@ TEST(LibcdsTest, ConcurrentQueueHPTest) { cds::gc::HP dhpGC( 16 ); - myAttach(); LincheckConfiguration, SequentialQueueLibcds> conf; - conf.iterations(10); + conf.iterations(100); + conf.invocationsPerIteration(500); conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(4); @@ -226,7 +226,6 @@ TEST(LibcdsTest, ConcurrentQueueHPTest) { conf.operation::push, &SequentialQueueLibcds::push>("push"); conf.operation, &ConcurrentQueueLibcds::pop, &SequentialQueueLibcds::pop>("pop"); ASSERT_EQ(conf.runTest(false), ""); - myDetach(); } cds::Terminate(); } @@ -237,9 +236,8 @@ TEST(LibcdsTest, ConcurrentQueueDHPTest) { cds::gc::DHP dhpGC( 160 //dhp_init_guard_count ); - myAttach(); LincheckConfiguration, SequentialQueueLibcds> conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(4); @@ -248,7 +246,6 @@ TEST(LibcdsTest, ConcurrentQueueDHPTest) { conf.operation::push, &SequentialQueueLibcds::push>("push"); conf.operation, &ConcurrentQueueLibcds::pop, &SequentialQueueLibcds::pop>("pop"); ASSERT_EQ(conf.runTest(false), ""); - myDetach(); } cds::Terminate(); } @@ -259,9 +256,8 @@ TEST(LibcdsTest, ConcurrentTreiberStackHPTest) { cds::gc::HP dhpGC( 16 ); - myAttach(); LincheckConfiguration, SequentialStackLibcds> conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(4); @@ -271,7 +267,6 @@ TEST(LibcdsTest, ConcurrentTreiberStackHPTest) { conf.operation, &ConcurrentTreiberStackLibcds::pop, &SequentialStackLibcds::pop>("pop"); conf.operation::clear, &SequentialStackLibcds::clear>("clear"); ASSERT_EQ(conf.runTest(false), ""); - myDetach(); } cds::Terminate(); } @@ -282,9 +277,8 @@ TEST(LibcdsTest, ConcurrentTreiberStackDHPTest) { cds::gc::DHP dhpGC( 160 ); - myAttach(); LincheckConfiguration, SequentialStackLibcds> conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); conf.actorsPerThread(4); @@ -294,7 +288,6 @@ TEST(LibcdsTest, ConcurrentTreiberStackDHPTest) { conf.operation, &ConcurrentTreiberStackLibcds::pop, &SequentialStackLibcds::pop>("pop"); conf.operation::clear, &SequentialStackLibcds::clear>("clear"); ASSERT_EQ(conf.runTest(false), ""); - myDetach(); } cds::Terminate(); } diff --git a/cpp/libcuckoo_test.cpp b/cpp/libcuckoo_test.cpp index e70a9f6f0..ca00369e7 100644 --- a/cpp/libcuckoo_test.cpp +++ b/cpp/libcuckoo_test.cpp @@ -96,7 +96,7 @@ using namespace Lincheck; TEST(LibcuckooTest, BadSequentialMapTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); @@ -110,7 +110,7 @@ TEST(LibcuckooTest, BadSequentialMapTest) { TEST(LibcuckooTest, HashMapTest) { LincheckConfiguration conf; - conf.iterations(10); + conf.iterations(100); conf.minimizeFailedScenario(false); conf.threads(3); diff --git a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index f26ddba34..771dcc858 100644 --- a/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/common/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -259,8 +259,13 @@ class LTS(sequentialSpecification: SequentialSpecification<*>) { block(old, this.computeRemappingFunction(old)) } else { val newSeqToCreate = if (curOperation != null) this.state.seqToCreate + curOperation else emptyList() - stateInfos[this] = this.also { it.state = State(newSeqToCreate) } - return block(stateInfos[this]!!, null) + val obj = this.also { it.state = State(newSeqToCreate) } + stateInfos[this] = obj + //if(stateInfos[this] != obj) { + // TODO author please check this code. Sometimes it throws + // throw RuntimeException("stateInfos[this] != obj") + //} + return block(obj, null) } } @@ -363,20 +368,11 @@ private class StateInfo( resumedOperations == other.resumedOperations } - private fun hashAll(vararg vals: Any?): Int { - var res = 0 - for (v in vals) { - res += v.hashCode() - res *= 31 - } - return res - } - - override fun hashCode() = hashAll( + override fun hashCode() = arrayOf( instance, suspendedOperations.map { it.actor }, resumedOperations - ) + ).contentHashCode() val maxTicket: Int get() = max( diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index eb0c5f0af..9c3a9fbab 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -51,7 +51,9 @@ internal class ObjectWithDestructorAndEqualsAndHashcodeAndToString(val obj: CPoi val toString: ToStringCFunction) : Finalizable { override fun equals(other: Any?): Boolean { - return if (other is ObjectWithDestructorAndEqualsAndHashcodeAndToString) { + return if(other === this) { + true + } else if (other is ObjectWithDestructorAndEqualsAndHashcodeAndToString) { equals.invoke(obj, other.obj) } else { false @@ -98,7 +100,9 @@ internal class SequentialSpecificationInstance(val obj: CPointer<*>, val equalsFunction: EqualsCFunction, val hashCodeFunction: HashCodeCFunction) : Finalizable { override fun equals(other: Any?): Boolean { - return if (other is SequentialSpecificationInstance) { + return if (other === this) { + true + } else if (other is SequentialSpecificationInstance) { equalsFunction.invoke(obj, other.obj) } else { false diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt index f805178b1..883e807d9 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/runner/FixedActiveThreadsExecutor.kt @@ -76,12 +76,16 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, private val finishThreadFunction: (() -> Unit)? = null) { // Threads used in this runner. private val threads: LincheckAtomicArray = LincheckAtomicArray(nThreads) + private var operationsFinished: AtomicInt = AtomicInt(0) init { (0 until nThreads).forEach { iThread -> threads.array[iThread].value = TestThread(iThread, runnerHash).also { it.executeTask { initThreadFunction?.invoke(); Any() } - it.awaitLastTask(currentTimeMillis() + 10000) + val res = it.awaitLastTask(currentTimeMillis() + 10000) + if(res == FixedActiveThreadsExecutor.TIMEOUT) { + throw RuntimeException("initThreadFunction has hang") + } } } } @@ -96,8 +100,10 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, */ fun submitAndAwait(tasks: Array, timeoutMs: Long) { require(tasks.size == nThreads) + operationsFinished.value = 0 submitTasks(tasks) await(timeoutMs) + operationsFinished.value = 1 } private fun submitTasks(tasks: Array) { @@ -135,6 +141,13 @@ internal class FixedActiveThreadsExecutor(private val nThreads: Int, // submit the shutdown task. for (t in threads.toArray()) { t.executeTask { finishThreadFunction?.invoke(); Any() } + if(operationsFinished.value == 1) { + // Operations executed before deadline, it is safe to wait for finish + val res = t.awaitLastTask(currentTimeMillis() + 10000) + if(res == FixedActiveThreadsExecutor.TIMEOUT) { + throw RuntimeException("finishThreadFunction has hang") + } + } t.terminate() } } diff --git a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index 7b3515bb8..6f4afe51f 100644 --- a/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/native/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -61,6 +61,7 @@ actual class StressStrategy actual constructor( actual override fun run(): LincheckFailure? { // Run invocations + initThreadFunction?.invoke() try { for (invocation in 0 until invocations) { runner.also { @@ -85,6 +86,7 @@ actual class StressStrategy actual constructor( } } } finally { + finishThreadFunction?.invoke() runner.close() } return null