blob: 49bc4ef5d858adc3c89ec1666aab4f32d4735893 [file] [log] [blame]
/*
* Copyright 2016-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlinx.coroutines.experimental
private const val WAIT_LOST_THREADS = 10_000L // 10s
private val ignoreLostThreads = mutableSetOf<String>()
fun ignoreLostThreads(vararg s: String) { ignoreLostThreads += s }
fun currentThreads(): Set<Thread> {
var estimate = 0
while (true) {
estimate = estimate.coerceAtLeast(Thread.activeCount() + 1)
val arrayOfThreads = Array<Thread?>(estimate) { null }
val n = Thread.enumerate(arrayOfThreads)
if (n >= estimate) {
estimate = n + 1
continue // retry with a better size estimate
}
val threads = hashSetOf<Thread>()
for (i in 0 until n)
threads.add(arrayOfThreads[i]!!)
return threads
}
}
fun List<Thread>.dumpThreads(header: String) {
println("=== $header")
forEach { thread ->
println("Thread \"${thread.name}\" ${thread.state}")
val trace = thread.stackTrace
for (t in trace) println("\tat ${t.className}.${t.methodName}(${t.fileName}:${t.lineNumber})")
println()
}
println("===")
}
fun ThreadPoolDispatcher.dumpThreads(header: String) =
currentThreads().filter { it is PoolThread && it.dispatcher == this@dumpThreads }.dumpThreads(header)
fun checkTestThreads(threadsBefore: Set<Thread>) {
// give threads some time to shutdown
val waitTill = System.currentTimeMillis() + WAIT_LOST_THREADS
var diff: List<Thread>
do {
val threadsAfter = currentThreads()
diff = (threadsAfter - threadsBefore).filter { thread ->
ignoreLostThreads.none { prefix -> thread.name.startsWith(prefix) }
}
if (diff.isEmpty()) break
} while (System.currentTimeMillis() <= waitTill)
ignoreLostThreads.clear()
if (diff.isEmpty()) return
val message = "Lost threads ${diff.map { it.name }}"
println("!!! $message")
diff.dumpThreads("Dumping lost thread stack traces")
error(message)
}