<lambda>null1package kotlinx.coroutines.testing 2 3 import kotlinx.coroutines.* 4 import java.lang.Runnable 5 6 private const val WAIT_LOST_THREADS = 10_000L // 10s 7 private val ignoreLostThreads = mutableSetOf<String>() 8 9 fun ignoreLostThreads(vararg s: String) { ignoreLostThreads += s } 10 currentThreadsnull11fun currentThreads(): Set<Thread> { 12 var estimate = 0 13 while (true) { 14 estimate = estimate.coerceAtLeast(Thread.activeCount() + 1) 15 val arrayOfThreads = Array<Thread?>(estimate) { null } 16 val n = Thread.enumerate(arrayOfThreads) 17 if (n >= estimate) { 18 estimate = n + 1 19 continue // retry with a better size estimate 20 } 21 val threads = hashSetOf<Thread>() 22 for (i in 0 until n) 23 threads.add(arrayOfThreads[i]!!) 24 return threads 25 } 26 } 27 dumpThreadsnull28fun List<Thread>.dumpThreads(header: String) { 29 println("=== $header") 30 forEach { thread -> 31 println("Thread \"${thread.name}\" ${thread.state}") 32 val trace = thread.stackTrace 33 for (t in trace) println("\tat ${t.className}.${t.methodName}(${t.fileName}:${t.lineNumber})") 34 println() 35 } 36 println("===") 37 } 38 39 class PoolThread( 40 @JvmField val dispatcher: ExecutorCoroutineDispatcher, // for debugging & tests 41 target: Runnable, name: String 42 ) : Thread(target, name) { 43 init { 44 isDaemon = true 45 } 46 } 47 dumpThreadsnull48fun ExecutorCoroutineDispatcher.dumpThreads(header: String) = 49 currentThreads().filter { it is PoolThread && it.dispatcher == this@dumpThreads }.dumpThreads(header) 50 checkTestThreadsnull51fun checkTestThreads(threadsBefore: Set<Thread>) { 52 // give threads some time to shutdown 53 val waitTill = System.currentTimeMillis() + WAIT_LOST_THREADS 54 var diff: List<Thread> 55 do { 56 val threadsAfter = currentThreads() 57 diff = (threadsAfter - threadsBefore).filter { thread -> 58 ignoreLostThreads.none { prefix -> thread.name.startsWith(prefix) } 59 } 60 if (diff.isEmpty()) break 61 } while (System.currentTimeMillis() <= waitTill) 62 ignoreLostThreads.clear() 63 if (diff.isEmpty()) return 64 val message = "Lost threads ${diff.map { it.name }}" 65 println("!!! $message") 66 diff.dumpThreads("Dumping lost thread stack traces") 67 error(message) 68 } 69