1 /* 2 * Copyright 2016-2020 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 // TODO most of these fields will be moved to 'object ExperimentalDispatcher' 13 14 // User-visible name 15 internal const val DEFAULT_DISPATCHER_NAME = "Dispatchers.Default" 16 // Internal debuggability name + thread name prefixes 17 internal const val DEFAULT_SCHEDULER_NAME = "DefaultDispatcher" 18 19 // 100us as default 20 @JvmField 21 internal val WORK_STEALING_TIME_RESOLUTION_NS = systemProp( 22 "kotlinx.coroutines.scheduler.resolution.ns", 100000L 23 ) 24 25 @JvmField 26 internal val BLOCKING_DEFAULT_PARALLELISM = systemProp( 27 "kotlinx.coroutines.scheduler.blocking.parallelism", 16 28 ) 29 30 // NOTE: we coerce default to at least two threads to give us chances that multi-threading problems 31 // get reproduced even on a single-core machine, but support explicit setting of 1 thread scheduler if needed. 32 @JvmField 33 internal val CORE_POOL_SIZE = systemProp( 34 "kotlinx.coroutines.scheduler.core.pool.size", 35 AVAILABLE_PROCESSORS.coerceAtLeast(2), // !!! at least two here 36 minValue = CoroutineScheduler.MIN_SUPPORTED_POOL_SIZE 37 ) 38 39 @JvmField 40 internal val MAX_POOL_SIZE = systemProp( 41 "kotlinx.coroutines.scheduler.max.pool.size", 42 (AVAILABLE_PROCESSORS * 128).coerceIn( 43 CORE_POOL_SIZE, 44 CoroutineScheduler.MAX_SUPPORTED_POOL_SIZE 45 ), 46 maxValue = CoroutineScheduler.MAX_SUPPORTED_POOL_SIZE 47 ) 48 49 @JvmField 50 internal val IDLE_WORKER_KEEP_ALIVE_NS = TimeUnit.SECONDS.toNanos( 51 systemProp("kotlinx.coroutines.scheduler.keep.alive.sec", 60L) 52 ) 53 54 @JvmField 55 internal var schedulerTimeSource: TimeSource = NanoTimeSource 56 57 /** 58 * Marker indicating that task is CPU-bound and will not block 59 */ 60 internal const val TASK_NON_BLOCKING = 0 61 62 /** 63 * Marker indicating that task may potentially block, thus giving scheduler a hint that additional thread may be required 64 */ 65 internal const val TASK_PROBABLY_BLOCKING = 1 66 67 internal interface TaskContext { 68 val taskMode: Int // TASK_XXX afterTasknull69 fun afterTask() 70 } 71 72 internal object NonBlockingContext : TaskContext { 73 override val taskMode: Int = TASK_NON_BLOCKING 74 75 override fun afterTask() { 76 // Nothing for non-blocking context 77 } 78 } 79 80 internal abstract class Task( 81 @JvmField var submissionTime: Long, 82 @JvmField var taskContext: TaskContext 83 ) : Runnable { 84 constructor() : this(0, NonBlockingContext) 85 inline val mode: Int get() = taskContext.taskMode // TASK_XXX 86 } 87 88 internal inline val Task.isBlocking get() = taskContext.taskMode == TASK_PROBABLY_BLOCKING 89 90 // Non-reusable Task implementation to wrap Runnable instances that do not otherwise implement task 91 internal class TaskImpl( 92 @JvmField val block: Runnable, 93 submissionTime: Long, 94 taskContext: TaskContext 95 ) : Task(submissionTime, taskContext) { runnull96 override fun run() { 97 try { 98 block.run() 99 } finally { 100 taskContext.afterTask() 101 } 102 } 103 toStringnull104 override fun toString(): String = 105 "Task[${block.classSimpleName}@${block.hexAddress}, $submissionTime, $taskContext]" 106 } 107 108 // Open for tests 109 internal class GlobalQueue : LockFreeTaskQueue<Task>(singleConsumer = false) 110 111 internal abstract class TimeSource { 112 abstract fun nanoTime(): Long 113 } 114 115 internal object NanoTimeSource : TimeSource() { nanoTimenull116 override fun nanoTime() = System.nanoTime() 117 } 118