blob: 3680ab2646ad1c134a8c47083b126deeac96700b [file] [log] [blame]
Roman Elizarovfa612f92017-02-07 12:11:16 +03001/*
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
17package kotlinx.coroutines.experimental.internal
18
Roman Elizarovebe18b42017-02-28 17:50:55 +030019import kotlinx.coroutines.experimental.TestBase
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030020import org.junit.Assert.*
Roman Elizarovfa612f92017-02-07 12:11:16 +030021import org.junit.Test
22import java.util.*
23import java.util.concurrent.atomic.AtomicInteger
24import kotlin.concurrent.thread
25
26/**
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030027 * This stress test has 6 threads adding randomly first to the list and them immediately undoing
Roman Elizarovfa612f92017-02-07 12:11:16 +030028 * this addition by remove, and 4 threads removing first node. The resulting list that is being
29 * stressed is very short.
30 */
Roman Elizarovebe18b42017-02-28 17:50:55 +030031class LockFreeLinkedListShortStressTest : TestBase() {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030032 data class IntNode(val i: Int) : LockFreeLinkedListNode()
33 val list = LockFreeLinkedListHead()
Roman Elizarovfa612f92017-02-07 12:11:16 +030034
Roman Elizarovebe18b42017-02-28 17:50:55 +030035 val TEST_DURATION = 5000L * stressTestMultiplier
36
Roman Elizarovfa612f92017-02-07 12:11:16 +030037 val threads = mutableListOf<Thread>()
38 val nAdderThreads = 6
39 val nRemoverThreads = 4
Roman Elizarovfa612f92017-02-07 12:11:16 +030040 val completedAdder = AtomicInteger()
41 val completedRemover = AtomicInteger()
42
43 val undone = AtomicInteger()
44 val missed = AtomicInteger()
45 val removed = AtomicInteger()
46
47 @Test
48 fun testStress() {
Roman Elizarovd3d335b2017-10-21 17:43:53 +030049 println("--- LockFreeLinkedListShortStressTest")
Roman Elizarovebe18b42017-02-28 17:50:55 +030050 val deadline = System.currentTimeMillis() + TEST_DURATION
Roman Elizarovfa612f92017-02-07 12:11:16 +030051 repeat(nAdderThreads) { threadId ->
52 threads += thread(start = false, name = "adder-$threadId") {
53 val rnd = Random()
54 while (System.currentTimeMillis() < deadline) {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030055 var node: IntNode? = IntNode(threadId)
56 when (rnd.nextInt(3)) {
57 0 -> list.addLast(node!!)
58 1 -> assertTrue(list.addLastIf(node!!, { true })) // just to test conditional add
59 2 -> { // just to test failed conditional add
60 assertFalse(list.addLastIf(node!!, { false }))
61 node = null
62 }
Roman Elizarovfa612f92017-02-07 12:11:16 +030063 }
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030064 if (node != null) {
65 if (node.remove())
66 undone.incrementAndGet()
67 else
68 missed.incrementAndGet()
69 }
Roman Elizarovfa612f92017-02-07 12:11:16 +030070 }
71 completedAdder.incrementAndGet()
72 }
73 }
74 repeat(nRemoverThreads) { threadId ->
75 threads += thread(start = false, name = "remover-$threadId") {
76 while (System.currentTimeMillis() < deadline) {
77 val node = list.removeFirstOrNull()
78 if (node != null) removed.incrementAndGet()
79
80 }
81 completedRemover.incrementAndGet()
82 }
83 }
84 threads.forEach { it.start() }
85 threads.forEach { it.join() }
86 println("Completed successfully ${completedAdder.get()} adder threads")
87 println("Completed successfully ${completedRemover.get()} remover threads")
88 println(" Adders undone ${undone.get()} node additions")
89 println(" Adders missed ${missed.get()} nodes")
90 println("Remover removed ${removed.get()} nodes")
91 assertEquals(nAdderThreads, completedAdder.get())
92 assertEquals(nRemoverThreads, completedRemover.get())
93 assertEquals(missed.get(), removed.get())
94 assertTrue(undone.get() > 0)
95 assertTrue(missed.get() > 0)
96 list.validate()
97 }
98}