1
+ /*
2
+ * Copyright 2016-2017 JetBrains s.r.o.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package kotlinx.coroutines.experimental.internal
18
+
19
+ import org.junit.Assert.assertEquals
20
+ import org.junit.Assert.assertTrue
21
+ import org.junit.Test
22
+ import java.util.*
23
+ import java.util.concurrent.atomic.AtomicInteger
24
+ import kotlin.concurrent.thread
25
+
26
+ /* *
27
+ * This stress test has 6 threads adding randomly first or last item to the list and them immediately undoing
28
+ * this addition by remove, and 4 threads removing first node. The resulting list that is being
29
+ * stressed is very short.
30
+ */
31
+ class LockFreeLinkedListShortStressTest {
32
+ private data class IntNode (val i : Int ) : LockFreeLinkedListNode()
33
+ private val list = LockFreeLinkedListHead ()
34
+
35
+ val threads = mutableListOf<Thread >()
36
+ val nAdderThreads = 6
37
+ val nRemoverThreads = 4
38
+ val timeout = 5000L
39
+ val completedAdder = AtomicInteger ()
40
+ val completedRemover = AtomicInteger ()
41
+
42
+ val undone = AtomicInteger ()
43
+ val missed = AtomicInteger ()
44
+ val removed = AtomicInteger ()
45
+
46
+ @Test
47
+ fun testStress () {
48
+ val deadline = System .currentTimeMillis() + timeout
49
+ repeat(nAdderThreads) { threadId ->
50
+ threads + = thread(start = false , name = " adder-$threadId " ) {
51
+ val rnd = Random ()
52
+ while (System .currentTimeMillis() < deadline) {
53
+ val node = IntNode (threadId)
54
+ when (rnd.nextInt(4 )) {
55
+ 0 -> list.addFirst(node)
56
+ 1 -> list.addLast(node)
57
+ 2 -> list.addFirstIf(node, { true }) // just to test conditional add
58
+ 3 -> list.addLastIf(node, { true })
59
+ }
60
+ if (node.remove())
61
+ undone.incrementAndGet()
62
+ else
63
+ missed.incrementAndGet()
64
+ }
65
+ completedAdder.incrementAndGet()
66
+ }
67
+ }
68
+ repeat(nRemoverThreads) { threadId ->
69
+ threads + = thread(start = false , name = " remover-$threadId " ) {
70
+ while (System .currentTimeMillis() < deadline) {
71
+ val node = list.removeFirstOrNull()
72
+ if (node != null ) removed.incrementAndGet()
73
+
74
+ }
75
+ completedRemover.incrementAndGet()
76
+ }
77
+ }
78
+ threads.forEach { it.start() }
79
+ threads.forEach { it.join() }
80
+ println (" Completed successfully ${completedAdder.get()} adder threads" )
81
+ println (" Completed successfully ${completedRemover.get()} remover threads" )
82
+ println (" Adders undone ${undone.get()} node additions" )
83
+ println (" Adders missed ${missed.get()} nodes" )
84
+ println (" Remover removed ${removed.get()} nodes" )
85
+ assertEquals(nAdderThreads, completedAdder.get())
86
+ assertEquals(nRemoverThreads, completedRemover.get())
87
+ assertEquals(missed.get(), removed.get())
88
+ assertTrue(undone.get() > 0 )
89
+ assertTrue(missed.get() > 0 )
90
+ list.validate()
91
+ }
92
+ }
0 commit comments