数据结构约束
Some data structures may require a part of operations not to be executed concurrently, such as single-producer single-consumer queues. Lincheck provides out-of-the-box support for such contracts, generating concurrent scenarios according to the restrictions.
Consider the single-consumer queue from the JCTools library. Let’s write a test to check correctness of its poll(), peek(), and offer(x) operations.
In your build.gradle(.kts) file, add the JCTools dependency:
【Kotlin】
dependencies {// jctools dependencytestImplementation("org.jctools:jctools-core:3.3.0")}
【Groovy】
dependencies {// jctools dependencytestImplementation "org.jctools:jctools-core:3.3.0"}
To meet the single-consumer restriction, ensure that all poll() and peek() consuming operations are called from a single thread. For that, we can set the nonParallelGroup parameter of the corresponding @Operation annotations to the same value, e.g. "consumers".
Here is the resulting test:
import org.jctools.queues.atomic.*import org.jetbrains.kotlinx.lincheck.annotations.*import org.jetbrains.kotlinx.lincheck.checkimport org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.*import org.jetbrains.kotlinx.lincheck.strategy.stress.*import org.junit.*class MPSCQueueTest {private val queue = MpscLinkedAtomicQueue<Int>()@Operationfun offer(x: Int) = queue.offer(x)@Operation(nonParallelGroup = "consumers")fun poll(): Int? = queue.poll()@Operation(nonParallelGroup = "consumers")fun peek(): Int? = queue.peek()@Testfun stressTest() = StressOptions().check(this::class)@Testfun modelCheckingTest() = ModelCheckingOptions().check(this::class)}
Here is an example of the scenario generated for this test:
= Iteration 15 / 100 =| --------------------- || Thread 1 | Thread 2 || --------------------- || poll() | || poll() | || peek() | || peek() | || peek() | || --------------------- || offer(-1) | offer(0) || offer(0) | offer(-1) || peek() | offer(-1) || offer(1) | offer(1) || peek() | offer(1) || --------------------- || peek() | || offer(-2) | || offer(-2) | || offer(2) | || offer(-2) | || --------------------- |
Note that all consuming poll() and peek() invocations are performed from a single thread, thus satisfying the “single-consumer” restriction.
Next step
Learn how to check your algorithm for progress guarantees with the model checking strategy.

