blob: ace0cfb0aadffa142253dc720ea1262946787ff6 [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +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
Roman Elizarova5676592017-01-19 10:31:13 +030017package kotlinx.coroutines.experimental.internal
Roman Elizarov3754f952017-01-18 20:47:54 +030018
Roman Elizarovebe18b42017-02-28 17:50:55 +030019import kotlinx.coroutines.experimental.TestBase
Roman Elizarov3754f952017-01-18 20:47:54 +030020import org.junit.Test
21import java.util.*
22import java.util.concurrent.atomic.AtomicInteger
23import kotlin.concurrent.thread
Roman Elizarovea4a51b2017-01-31 12:01:25 +030024import kotlin.coroutines.experimental.buildIterator
Roman Elizarov3754f952017-01-18 20:47:54 +030025
Roman Elizarovfa612f92017-02-07 12:11:16 +030026/**
27 * This stress test has 2 threads adding on one side on list, 2 more threads adding on the other,
28 * and 6 threads iterating and concurrently removing items. The resulting list that is being
29 * stressed is long.
30 */
Roman Elizarovebe18b42017-02-28 17:50:55 +030031class LockFreeLinkedListLongStressTest : TestBase() {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030032 data class IntNode(val i: Int) : LockFreeLinkedListNode()
33 val list = LockFreeLinkedListHead()
Roman Elizarov3754f952017-01-18 20:47:54 +030034
35 val threads = mutableListOf<Thread>()
Roman Elizarov43604bd2017-03-02 23:19:59 +030036 val nAdded = 10_000_000 // should not stress more, because that'll run out of memory
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030037 val nAddThreads = 4 // must be power of 2 (!!!)
Roman Elizarov3754f952017-01-18 20:47:54 +030038 val nRemoveThreads = 6
39 val removeProbability = 0.2
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030040 val workingAdders = AtomicInteger(nAddThreads)
Roman Elizarov3754f952017-01-18 20:47:54 +030041
42 fun shallRemove(i: Int) = i and 63 != 42
43
44 @Test
45 fun testStress() {
Roman Elizarovd3d335b2017-10-21 17:43:53 +030046 println("--- LockFreeLinkedListLongStressTest")
Roman Elizarov3754f952017-01-18 20:47:54 +030047 for (j in 0 until nAddThreads)
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030048 threads += thread(start = false, name = "adder-$j") {
Roman Elizarov3754f952017-01-18 20:47:54 +030049 for (i in j until nAdded step nAddThreads) {
Roman Elizarov3754f952017-01-18 20:47:54 +030050 list.addLast(IntNode(i))
51 }
Roman Elizarov12f961d2017-02-06 15:44:03 +030052 println("${Thread.currentThread().name} completed")
Roman Elizarov3754f952017-01-18 20:47:54 +030053 workingAdders.decrementAndGet()
54 }
55 for (j in 0 until nRemoveThreads)
56 threads += thread(start = false, name = "remover-$j") {
57 val rnd = Random()
58 do {
59 val lastTurn = workingAdders.get() == 0
60 list.forEach<IntNode> { node ->
61 if (shallRemove(node.i) && (lastTurn || rnd.nextDouble() < removeProbability))
62 node.remove()
63 }
64 } while (!lastTurn)
Roman Elizarov12f961d2017-02-06 15:44:03 +030065 println("${Thread.currentThread().name} completed")
Roman Elizarov3754f952017-01-18 20:47:54 +030066 }
67 println("Starting ${threads.size} threads")
68 for (thread in threads)
69 thread.start()
70 println("Joining threads")
71 for (thread in threads)
72 thread.join()
73 // verification
74 println("Verify result")
75 list.validate()
76 val expected = buildIterator {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +030077 for (i in 0 until nAdded)
Roman Elizarov3754f952017-01-18 20:47:54 +030078 if (!shallRemove(i))
79 yield(i)
80 }
81 list.forEach<IntNode> { node ->
82 require(node.i == expected.next())
83 }
84 require(!expected.hasNext())
85 }
86}