blob: 9aeabd3ac8d13a464939a6590ba556fe28100c6e [file] [log] [blame]
package kotlinx.coroutines.experimental.internal
import org.junit.Test
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import kotlin.concurrent.thread
class LockFreeLinkedListStressTest {
private data class IntNode(val i: Int) : LockFreeLinkedListNode()
private val list = LockFreeLinkedListHead()
val threads = mutableListOf<Thread>()
val nAdded = 10_000_000
val nAddThreads = 2
val nRemoveThreads = 6
val removeProbability = 0.2
val workingAdders = AtomicInteger(nAddThreads * 2)
fun shallRemove(i: Int) = i and 63 != 42
@Test
fun testStress() {
for (j in 0 until nAddThreads)
threads += thread(start = false, name = "firstAdder-$j") {
for (i in j until nAdded step nAddThreads) {
list.addFirst(IntNode(i))
}
workingAdders.decrementAndGet()
}
for (j in 0 until nAddThreads)
threads += thread(start = false, name = "lastAdder-$j") {
for (i in -j-1 downTo -nAdded step nAddThreads) {
list.addLast(IntNode(i))
}
workingAdders.decrementAndGet()
}
for (j in 0 until nRemoveThreads)
threads += thread(start = false, name = "remover-$j") {
val rnd = Random()
do {
val lastTurn = workingAdders.get() == 0
list.forEach<IntNode> { node ->
if (shallRemove(node.i) && (lastTurn || rnd.nextDouble() < removeProbability))
node.remove()
}
} while (!lastTurn)
}
println("Starting ${threads.size} threads")
for (thread in threads)
thread.start()
println("Joining threads")
for (thread in threads)
thread.join()
// verification
println("Verify result")
list.validate()
val expected = buildIterator {
for (i in nAdded - 1 downTo -nAdded)
if (!shallRemove(i))
yield(i)
}
list.forEach<IntNode> { node ->
require(node.i == expected.next())
}
require(!expected.hasNext())
}
}