1 /*
2  * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import kotlinx.coroutines.internal.*
8 import java.util.concurrent.*
9 import java.util.concurrent.atomic.AtomicInteger
10 import kotlin.coroutines.*
11 
12 /**
13  * Creates a coroutine execution context using a single thread with built-in [yield] support.
14  * **NOTE: The resulting [ExecutorCoroutineDispatcher] owns native resources (its thread).
15  * Resources are reclaimed by [ExecutorCoroutineDispatcher.close].**
16  *
17  * **NOTE: This API will be replaced in the future**. A different API to create thread-limited thread pools
18  * that is based on a shared thread-pool and does not require the resulting dispatcher to be explicitly closed
19  * will be provided, thus avoiding potential thread leaks and also significantly improving performance, due
20  * to coroutine-oriented scheduling policy and thread-switch minimization.
21  * See [issue #261](https://github.com/Kotlin/kotlinx.coroutines/issues/261) for details.
22  * If you need a completely separate thread-pool with scheduling policy that is based on the standard
23  * JDK executors, use the following expression:
24  * `Executors.newSingleThreadExecutor().asCoroutineDispatcher()`.
25  * See [Executor.asCoroutineDispatcher] for details.
26  *
27  * @param name the base name of the created thread.
28  */
29 @ObsoleteCoroutinesApi
30 fun newSingleThreadContext(name: String): ExecutorCoroutineDispatcher =
31     newFixedThreadPoolContext(1, name)
32 
33 /**
34  * Creates a coroutine execution context with the fixed-size thread-pool and built-in [yield] support.
35  * **NOTE: The resulting [ExecutorCoroutineDispatcher] owns native resources (its threads).
36  * Resources are reclaimed by [ExecutorCoroutineDispatcher.close].**
37  *
38  * **NOTE: This API will be replaced in the future**. A different API to create thread-limited thread pools
39  * that is based on a shared thread-pool and does not require the resulting dispatcher to be explicitly closed
40  * will be provided, thus avoiding potential thread leaks and also significantly improving performance, due
41  * to coroutine-oriented scheduling policy and thread-switch minimization.
42  * See [issue #261](https://github.com/Kotlin/kotlinx.coroutines/issues/261) for details.
43  * If you need a completely separate thread-pool with scheduling policy that is based on the standard
44  * JDK executors, use the following expression:
45  * `Executors.newFixedThreadPool().asCoroutineDispatcher()`.
46  * See [Executor.asCoroutineDispatcher] for details.
47  *
48  * @param nThreads the number of threads.
49  * @param name the base name of the created threads.
50  */
51 @ObsoleteCoroutinesApi
52 fun newFixedThreadPoolContext(nThreads: Int, name: String): ExecutorCoroutineDispatcher {
53     require(nThreads >= 1) { "Expected at least one thread, but $nThreads specified" }
54     return ThreadPoolDispatcher(nThreads, name)
55 }
56 
57 internal class PoolThread(
58     @JvmField val dispatcher: ThreadPoolDispatcher, // for debugging & tests
59     target: Runnable, name: String
60 ) : Thread(target, name) {
61     init { isDaemon = true }
62 }
63 
64 /**
65  * Dispatches coroutine execution to a thread pool of a fixed size. Instances of this dispatcher are
66  * created with [newSingleThreadContext] and [newFixedThreadPoolContext].
67  */
68 internal class ThreadPoolDispatcher internal constructor(
69     private val nThreads: Int,
70     private val name: String
71 ) : ExecutorCoroutineDispatcherBase() {
72     private val threadNo = AtomicInteger()
73 
74     override val executor: Executor = Executors.newScheduledThreadPool(nThreads) { target ->
75         PoolThread(this, target, if (nThreads == 1) name else name + "-" + threadNo.incrementAndGet())
76     }
77 
78     init {
79         initFutureCancellation()
80     }
81 
82     /**
83      * Closes this dispatcher -- shuts down all threads in this pool and releases resources.
84      */
85     public override fun close() {
86         (executor as ExecutorService).shutdown()
87     }
88 
89     override fun toString(): String = "ThreadPoolDispatcher[$nThreads, $name]"
90 }
91