Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a mechanism to provide the thread number as an operation parameter #47

Merged
merged 4 commits into from
Sep 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/org/jetbrains/kotlinx/lincheck/Actor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ package org.jetbrains.kotlinx.lincheck

import org.jetbrains.kotlinx.lincheck.annotations.*
import java.lang.reflect.Method
import kotlin.coroutines.Continuation
import kotlin.reflect.jvm.*

/**
* The actor entity describe the operation with its parameters
Expand All @@ -46,4 +46,4 @@ data class Actor @JvmOverloads constructor(
val isSuspendable = method.isSuspendable()
}

fun Method.isSuspendable() = parameterTypes.isNotEmpty() && (kotlin.coroutines.Continuation::class.java).isAssignableFrom(parameterTypes.last())
fun Method.isSuspendable(): Boolean = kotlinFunction?.isSuspend ?: false
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ import java.lang.AssertionError

class LincheckAssertionError(
failure: LincheckFailure
) : AssertionError(failure)
) : AssertionError("\n" + failure)
18 changes: 0 additions & 18 deletions src/main/java/org/jetbrains/kotlinx/lincheck/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,6 @@ fun chooseSequentialSpecification(sequentialSpecificationByUser: Class<*>?, test
if (sequentialSpecificationByUser === DummySequentialSpecification::class.java || sequentialSpecificationByUser == null) testClass
else sequentialSpecificationByUser

/**
* Creates instance of cost counter class using the (int relaxationFactor) constructor.
*/
fun createCostCounterInstance(costCounter: Class<*>, relaxationFactor: Int): Any {
try {
return costCounter.getDeclaredConstructor(Int::class.javaPrimitiveType).newInstance(relaxationFactor)
} catch (e: Exception) {
e.catch(
IllegalStateException::class.java,
IllegalAccessException::class.java,
NoSuchMethodException::class.java,
InvocationTargetException::class.java
) {
throw IllegalStateException("Cost counter class should have '(relaxationFactor: Int)' constructor", e)
}
}
}

internal fun executeActor(testInstance: Any, actor: Actor) = executeActor(testInstance, actor, null)

/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*-
* #%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
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/
package org.jetbrains.kotlinx.lincheck.execution

import org.jetbrains.kotlinx.lincheck.*
import org.jetbrains.kotlinx.lincheck.paramgen.*
import java.lang.reflect.*
import kotlin.random.*

/**
* Implementations of this class generate [actors][Actor]
* using [parameter generators][ParameterGenerator].
*/
class ActorGenerator(
private val method: Method,
private val parameterGenerators: List<ParameterGenerator<*>>,
private val handledExceptions: List<Class<out Throwable?>>,
val useOnce: Boolean,
cancellableOnSuspension: Boolean,
val allowExtraSuspension: Boolean
) {
private val cancellableOnSuspension = cancellableOnSuspension && isSuspendable

fun generate(threadId: Int): Actor {
val parameters = parameterGenerators
.map { it.generate() }
.map { if (it === THREAD_ID_TOKEN) threadId else it }
val cancelOnSuspension = cancellableOnSuspension and DETERMINISTIC_RANDOM.nextBoolean()
return Actor(method, parameters, handledExceptions,
cancelOnSuspension, allowExtraSuspension)
}

val isSuspendable: Boolean get() = method.isSuspendable()
override fun toString() = method.toString()
}

private val DETERMINISTIC_RANDOM = Random(42)
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.util.*;
import java.util.stream.*;

import static org.jetbrains.kotlinx.lincheck.ActorKt.isSuspendable;
import static org.jetbrains.kotlinx.lincheck.ReporterKt.appendExecutionScenario;

/**
Expand Down Expand Up @@ -76,7 +75,7 @@ public int getThreads() {
* 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(actor.getMethod()));
return Stream.concat(parallelExecution.stream().flatMap(Collection::stream), postExecution.stream()).anyMatch(Actor::isSuspendable);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ public RandomExecutionGenerator(CTestConfiguration testConfiguration, CTestStruc
public ExecutionScenario nextExecution() {
// Create init execution part
List<ActorGenerator> validActorGeneratorsForInit = testStructure.actorGenerators.stream()
.filter(ag -> !ag.useOnce() && !ag.isSuspendable()).collect(Collectors.toList());
.filter(ag -> !ag.getUseOnce() && !ag.isSuspendable()).collect(Collectors.toList());
List<Actor> initExecution = new ArrayList<>();
for (int i = 0; i < testConfiguration.actorsBefore && !validActorGeneratorsForInit.isEmpty(); i++) {
ActorGenerator ag = validActorGeneratorsForInit.get(random.nextInt(validActorGeneratorsForInit.size()));
initExecution.add(ag.generate());
initExecution.add(ag.generate(0));
}
// Create parallel execution part
// Construct non-parallel groups and parallel one
Expand All @@ -61,9 +61,9 @@ public ExecutionScenario nextExecution() {

List<List<Actor>> parallelExecution = new ArrayList<>();
List<ThreadGen> threadGens = new ArrayList<>();
for (int i = 0; i < testConfiguration.threads; i++) {
for (int t = 0; t < testConfiguration.threads; t++) {
parallelExecution.add(new ArrayList<>());
threadGens.add(new ThreadGen(i, testConfiguration.actorsPerThread));
threadGens.add(new ThreadGen(t, testConfiguration.actorsPerThread));
}
for (int i = 0; i < nonParallelGroups.size(); i++) {
threadGens.get(i % threadGens.size()).nonParallelActorGenerators
Expand All @@ -86,7 +86,7 @@ public ExecutionScenario nextExecution() {
agen = getActorGenFromGroup(parallelGroup,
aGenIndex - threadGen.nonParallelActorGenerators.size());
}
parallelExecution.get(threadGen.threadNumber).add(agen.generate());
parallelExecution.get(threadGen.iThread).add(agen.generate(threadGen.iThread + 1));
if (--threadGen.left == 0)
it.remove();
}
Expand All @@ -101,7 +101,7 @@ public ExecutionScenario nextExecution() {
leftActorGenerators.addAll(threadGen.nonParallelActorGenerators);
for (int i = 0; i < testConfiguration.actorsAfter && !leftActorGenerators.isEmpty(); i++) {
ActorGenerator agen = getActorGenFromGroup(leftActorGenerators, random.nextInt(leftActorGenerators.size()));
postExecution.add(agen.generate());
postExecution.add(agen.generate(testConfiguration.threads + 1));
}
} else {
postExecution = Collections.emptyList();
Expand All @@ -111,18 +111,18 @@ public ExecutionScenario nextExecution() {

private ActorGenerator getActorGenFromGroup(List<ActorGenerator> aGens, int index) {
ActorGenerator aGen = aGens.get(index);
if (aGen.useOnce())
if (aGen.getUseOnce())
aGens.remove(index);
return aGen;
}

private class ThreadGen {
private static class ThreadGen {
final List<ActorGenerator> nonParallelActorGenerators = new ArrayList<>();
int threadNumber;
int iThread;
int left;

ThreadGen(int threadNumber, int nActors) {
this.threadNumber = threadNumber;
ThreadGen(int iThread, int nActors) {
this.iThread = iThread;
this.left = nActors;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*-
* #%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
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/
package org.jetbrains.kotlinx.lincheck.paramgen

/**
* This generator puts the number of the
* executing thread as the parameter value.
* The `0`-th thread specifies the init part
* of the execution, while the `t+1`-th thread
* references the post part (here we assume that
* the parallel part has `t` threads).
*
* Note, that this API is unstable and is subject to change.
*/
class ThreadIdGen(configuration: String) : ParameterGenerator<Any> {
override fun generate() = THREAD_ID_TOKEN
}

internal val THREAD_ID_TOKEN = Any()
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ open class ParallelThreadsRunner(
private val runnerHash = this.hashCode() // helps to distinguish this runner threads from others
private val executor = FixedActiveThreadsExecutor(scenario.threads, runnerHash)

private val completions = List(scenario.threads) { threadId ->
List(scenario.parallelExecution[threadId].size) { Completion(threadId) }
private val completions = List(scenario.threads) { t ->
List(scenario.parallelExecution[t].size) { Completion(t) }
}

private val testThreadExecutions = Array(scenario.threads) { t ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import java.util.ArrayList;
import java.util.List;

import static org.jetbrains.kotlinx.lincheck.ActorKt.isSuspendable;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Type.*;

Expand Down Expand Up @@ -328,7 +327,7 @@ private static void storeExceptionResultFromThrowable(GeneratorAdapter mv, int r
mv.arrayStore(RESULT_TYPE);
}

private static void storeExceptionResultFromSuspendableThrowable(GeneratorAdapter mv, int resLocal, int iLocal, int threadId, int actorId) {
private static void storeExceptionResultFromSuspendableThrowable(GeneratorAdapter mv, int resLocal, int iLocal, int iThread, int actorId) {
int eLocal = mv.newLocal(THROWABLE_TYPE);
mv.storeLocal(eLocal);
mv.loadLocal(resLocal);
Expand All @@ -339,7 +338,7 @@ private static void storeExceptionResultFromSuspendableThrowable(GeneratorAdapte
mv.checkCast(PARALLEL_THREADS_RUNNER_TYPE);
// Load exception result
mv.loadLocal(eLocal);
mv.push(threadId);
mv.push(iThread);
mv.push(actorId);
// Process result
mv.invokeVirtual(PARALLEL_THREADS_RUNNER_TYPE, PARALLEL_THREADS_RUNNER_PROCESS_INVOCATION_RESULT_METHOD);
Expand All @@ -351,7 +350,7 @@ private static void loadArguments(GeneratorAdapter mv, Actor actor, List<Object>
for (int j = 0; j < nArguments; j++) {
pushArgumentOnStack(mv, objArgs, actor.getArguments().toArray()[j], actor.getMethod().getParameterTypes()[j]);
}
if (isSuspendable(actor.getMethod())) {
if (actor.isSuspendable()) {
pushArgumentOnStack(mv, objArgs, completion, Continuation.class);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import org.jetbrains.kotlinx.lincheck.chooseSequentialSpecification
/**
* Options for [stress][StressStrategy] strategy.
*/
class StressOptions : Options<StressOptions, StressCTestConfiguration>() {
open class StressOptions : Options<StressOptions, StressCTestConfiguration>() {
protected var invocationsPerIteration = StressCTestConfiguration.DEFAULT_INVOCATIONS
protected var addWaits = true
private var addWaits = true

/**
* Run each test scenario `invocations` times.
Expand Down
Loading