blob: d91ac7482ec689bb1bdae8d2833b4dadd9ae5180 [file] [log] [blame]
Roman Elizarov3754f952017-01-18 20:47:54 +03001package kotlinx.coroutines.experimental
2
3import java.util.concurrent.Executor
4import java.util.concurrent.Executors
5import java.util.concurrent.ForkJoinPool
6import java.util.concurrent.atomic.AtomicInteger
Roman Elizarov67891d82017-01-23 16:47:20 +03007import kotlin.coroutines.CoroutineContext
Roman Elizarov3754f952017-01-18 20:47:54 +03008
9/**
Roman Elizaroved7b8642017-01-19 11:22:28 +030010 * Represents common pool of shared threads as coroutine dispatcher for compute-intensive tasks.
Roman Elizarov3754f952017-01-18 20:47:54 +030011 * It uses [ForkJoinPool] when available, which implements efficient work-stealing algorithm for its queues, so every
12 * coroutine resumption is dispatched as a separate task even when it already executes inside the pool.
Roman Elizaroved7b8642017-01-19 11:22:28 +030013 * When available, it wraps [ForkJoinPool.commonPool] and provides a similar shared pool where not.
Roman Elizarov3754f952017-01-18 20:47:54 +030014 */
Roman Elizarov67891d82017-01-23 16:47:20 +030015object CommonPool : CoroutineDispatcher() {
Roman Elizarov3754f952017-01-18 20:47:54 +030016 private val pool: Executor = findPool()
17
18 private inline fun <T> Try(block: () -> T) = try { block() } catch (e: Throwable) { null }
19
20 private fun findPool(): Executor {
21 val fjpClass = Try { Class.forName("java.util.concurrent.ForkJoinPool") }
22 ?: return createPlainPool()
23 Try { fjpClass.getMethod("commonPool")?.invoke(null) as? Executor }
24 ?. let { return it }
25 Try { fjpClass.getConstructor(Int::class.java).newInstance(defaultParallelism()) as? Executor }
26 ?. let { return it }
27 return createPlainPool()
28 }
29
30 private fun createPlainPool(): Executor {
31 val threadId = AtomicInteger()
32 return Executors.newFixedThreadPool(defaultParallelism()) {
33 Thread(it, "CommonPool-worker-${threadId.incrementAndGet()}").apply { isDaemon = true }
34 }
35 }
36
37 private fun defaultParallelism() = (Runtime.getRuntime().availableProcessors() - 1).coerceAtLeast(1)
38
Roman Elizarov67891d82017-01-23 16:47:20 +030039 override fun isDispatchNeeded(context: CoroutineContext): Boolean = true
40 override fun dispatch(context: CoroutineContext, block: Runnable) = pool.execute(block)
Roman Elizarov3754f952017-01-18 20:47:54 +030041}