@@ -6,12 +6,25 @@ package kotlinx.coroutines.internal
6
6
7
7
import kotlinx.atomicfu.*
8
8
import kotlinx.coroutines.*
9
+ import org.junit.runner.*
10
+ import org.junit.runners.*
9
11
import java.util.concurrent.*
10
12
import kotlin.concurrent.*
11
13
import kotlin.test.*
12
14
13
15
// Tests many short queues to stress copy/resize
14
- class LockFreeTaskQueueStressTest : TestBase () {
16
+ @RunWith(Parameterized ::class )
17
+ class LockFreeTaskQueueStressTest (
18
+ private val nConsumers : Int
19
+ ) : TestBase() {
20
+ companion object {
21
+ @Parameterized.Parameters (name = " nConsumers={0}" )
22
+ @JvmStatic
23
+ fun params (): Collection <Int > = listOf (1 , 3 )
24
+ }
25
+
26
+ private val singleConsumer = nConsumers == 1
27
+
15
28
private val nSeconds = 3 * stressTestMultiplier
16
29
private val nProducers = 4
17
30
private val batchSize = 100
@@ -25,7 +38,7 @@ class LockFreeTaskQueueStressTest : TestBase() {
25
38
private val done = atomic(0 )
26
39
private val doneProducers = atomic(0 )
27
40
28
- private val barrier = CyclicBarrier (nProducers + 2 )
41
+ private val barrier = CyclicBarrier (nProducers + nConsumers + 1 )
29
42
30
43
private class Item (val producer : Int , val index : Long )
31
44
@@ -34,7 +47,7 @@ class LockFreeTaskQueueStressTest : TestBase() {
34
47
val threads = mutableListOf<Thread >()
35
48
threads + = thread(name = " Pacer" , start = false ) {
36
49
while (done.value == 0 ) {
37
- queue.value = LockFreeTaskQueue (false )
50
+ queue.value = LockFreeTaskQueue (singleConsumer )
38
51
batch.value = 0
39
52
doneProducers.value = 0
40
53
barrier.await() // start consumers & producers
@@ -44,25 +57,30 @@ class LockFreeTaskQueueStressTest : TestBase() {
44
57
println (" Pacer done" )
45
58
barrier.await() // wakeup the rest
46
59
}
47
- threads + = thread(name = " Consumer" , start = false ) {
48
- while (true ) {
49
- barrier.await()
50
- val queue = queue.value ? : break
60
+ threads + = List (nConsumers) { consumer ->
61
+ thread(name = " Consumer-$consumer " , start = false ) {
51
62
while (true ) {
52
- val item = queue.removeFirstOrNull()
53
- if (item == null ) {
54
- if (doneProducers.value == nProducers && queue.isEmpty) break // that's it
55
- continue // spin to retry
63
+ barrier.await()
64
+ val queue = queue.value ? : break
65
+ while (true ) {
66
+ val item = queue.removeFirstOrNull()
67
+ if (item == null ) {
68
+ if (doneProducers.value == nProducers && queue.isEmpty) break // that's it
69
+ continue // spin to retry
70
+ }
71
+ consumed.incrementAndGet()
72
+ if (singleConsumer) {
73
+ // This check only properly works in single-consumer case
74
+ val eItem = expected[item.producer]++
75
+ if (eItem != item.index) error(" Expected $eItem but got ${item.index} from Producer-${item.producer} " )
76
+ }
56
77
}
57
- consumed.incrementAndGet()
58
- val eItem = expected[item.producer]++
59
- if (eItem != item.index) error(" Expected $eItem but got ${item.index} from Producer-${item.producer} " )
78
+ barrier.await()
60
79
}
61
- barrier.await( )
80
+ println ( " Consumer- $consumer done " )
62
81
}
63
- println (" Consumer done" )
64
82
}
65
- val producers = List (nProducers) { producer ->
83
+ threads + = List (nProducers) { producer ->
66
84
thread(name = " Producer-$producer " , start = false ) {
67
85
var index = 0L
68
86
while (true ) {
@@ -79,7 +97,6 @@ class LockFreeTaskQueueStressTest : TestBase() {
79
97
println (" Producer-$producer done" )
80
98
}
81
99
}
82
- threads + = producers
83
100
threads.forEach {
84
101
it.setUncaughtExceptionHandler { t, e ->
85
102
System .err.println (" Thread $t failed: $e " )
0 commit comments