• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines.scheduling
6 
7 import kotlinx.coroutines.*
8 import kotlinx.coroutines.internal.*
9 import java.util.concurrent.*
10 
11 
12 // Internal debuggability name + thread name prefixes
13 internal const val DEFAULT_SCHEDULER_NAME = "DefaultDispatcher"
14 
15 // 100us as default
16 @JvmField
17 internal val WORK_STEALING_TIME_RESOLUTION_NS = systemProp(
18     "kotlinx.coroutines.scheduler.resolution.ns", 100000L
19 )
20 
21 /**
22  * The maximum number of threads allocated for CPU-bound tasks at the default set of dispatchers.
23  *
24  * NOTE: we coerce default to at least two threads to give us chances that multi-threading problems
25  * get reproduced even on a single-core machine, but support explicit setting of 1 thread scheduler if needed
26  */
27 @JvmField
28 internal val CORE_POOL_SIZE = systemProp(
29     "kotlinx.coroutines.scheduler.core.pool.size",
30     AVAILABLE_PROCESSORS.coerceAtLeast(2),
31     minValue = CoroutineScheduler.MIN_SUPPORTED_POOL_SIZE
32 )
33 
34 /** The maximum number of threads allocated for blocking tasks at the default set of dispatchers. */
35 @JvmField
36 internal val MAX_POOL_SIZE = systemProp(
37     "kotlinx.coroutines.scheduler.max.pool.size",
38     CoroutineScheduler.MAX_SUPPORTED_POOL_SIZE,
39     maxValue = CoroutineScheduler.MAX_SUPPORTED_POOL_SIZE
40 )
41 
42 @JvmField
43 internal val IDLE_WORKER_KEEP_ALIVE_NS = TimeUnit.SECONDS.toNanos(
44     systemProp("kotlinx.coroutines.scheduler.keep.alive.sec", 60L)
45 )
46 
47 @JvmField
48 internal var schedulerTimeSource: SchedulerTimeSource = NanoTimeSource
49 
50 /**
51  * Marker indicating that task is CPU-bound and will not block
52  */
53 internal const val TASK_NON_BLOCKING = 0
54 
55 /**
56  * Marker indicating that task may potentially block, thus giving scheduler a hint that additional thread may be required
57  */
58 internal const val TASK_PROBABLY_BLOCKING = 1
59 
60 internal interface TaskContext {
61     val taskMode: Int // TASK_XXX
afterTasknull62     fun afterTask()
63 }
64 
65 private class TaskContextImpl(override val taskMode: Int): TaskContext {
66     override fun afterTask() {
67         // Nothing for non-blocking context
68     }
69 }
70 
71 @JvmField
72 internal val NonBlockingContext: TaskContext = TaskContextImpl(TASK_NON_BLOCKING)
73 
74 @JvmField
75 internal val BlockingContext: TaskContext = TaskContextImpl(TASK_PROBABLY_BLOCKING)
76 
77 internal abstract class Task(
78     @JvmField var submissionTime: Long,
79     @JvmField var taskContext: TaskContext
80 ) : Runnable {
81     constructor() : this(0, NonBlockingContext)
82     inline val mode: Int get() = taskContext.taskMode // TASK_XXX
83 }
84 
85 internal inline val Task.isBlocking get() = taskContext.taskMode == TASK_PROBABLY_BLOCKING
86 
87 // Non-reusable Task implementation to wrap Runnable instances that do not otherwise implement task
88 internal class TaskImpl(
89     @JvmField val block: Runnable,
90     submissionTime: Long,
91     taskContext: TaskContext
92 ) : Task(submissionTime, taskContext) {
runnull93     override fun run() {
94         try {
95             block.run()
96         } finally {
97             taskContext.afterTask()
98         }
99     }
100 
toStringnull101     override fun toString(): String =
102         "Task[${block.classSimpleName}@${block.hexAddress}, $submissionTime, $taskContext]"
103 }
104 
105 // Open for tests
106 internal class GlobalQueue : LockFreeTaskQueue<Task>(singleConsumer = false)
107 
108 // Was previously TimeSource, renamed due to KT-42625 and KT-23727
109 internal abstract class SchedulerTimeSource {
110     abstract fun nanoTime(): Long
111 }
112 
113 internal object NanoTimeSource : SchedulerTimeSource() {
nanoTimenull114     override fun nanoTime() = System.nanoTime()
115 }
116