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