Skip to content

Commit 16e2034

Browse files
elizarovqwwdfsad
authored andcommitted
Avoid allocation of CoroutineScheduler task object
* DispatchedContinuation and CancellableContinuationImpl have all the necessarily field to be scheduled
1 parent cc73d39 commit 16e2034

File tree

9 files changed

+91
-21
lines changed

9 files changed

+91
-21
lines changed

common/kotlinx-coroutines-core-common/src/AbstractContinuation.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ private const val RESUMED = 2
1919
internal abstract class AbstractContinuation<in T>(
2020
public final override val delegate: Continuation<T>,
2121
public final override val resumeMode: Int
22-
) : Continuation<T>, DispatchedTask<T> {
22+
) : SchedulerTaskBase(), Continuation<T>, DispatchedTask<T> {
2323

2424
/*
2525
* Implementation notes

common/kotlinx-coroutines-core-common/src/Dispatched.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ internal object UndispatchedEventLoop {
8181
internal class DispatchedContinuation<in T>(
8282
@JvmField val dispatcher: CoroutineDispatcher,
8383
@JvmField val continuation: Continuation<T>
84-
) : Continuation<T> by continuation, DispatchedTask<T> {
84+
) : SchedulerTaskBase(), Continuation<T> by continuation, DispatchedTask<T> {
8585
@JvmField
8686
@Suppress("PropertyName")
8787
internal var _state: Any? = UNDEFINED
@@ -204,7 +204,7 @@ internal fun <T> Continuation<T>.resumeDirectWithException(exception: Throwable)
204204
else -> resumeWithException(exception)
205205
}
206206

207-
internal interface DispatchedTask<in T> : Runnable {
207+
internal interface DispatchedTask<in T> : SchedulerTask {
208208
public val delegate: Continuation<T>
209209
public val resumeMode: Int get() = MODE_CANCELLABLE
210210

@@ -237,6 +237,8 @@ internal interface DispatchedTask<in T> : Runnable {
237237
}
238238
} catch (e: Throwable) {
239239
throw DispatchException("Unexpected exception running $this", e)
240+
} finally {
241+
afterTask()
240242
}
241243
}
242244
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
internal expect interface SchedulerTask : Runnable
8+
9+
internal expect abstract class SchedulerTaskBase() : SchedulerTask
10+
11+
internal expect inline fun SchedulerTask.afterTask()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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.scheduling.*
8+
9+
internal actual typealias SchedulerTask = Task
10+
11+
internal actual abstract class SchedulerTaskBase actual constructor() : SchedulerTask {
12+
override var submissionTime: Long = 0
13+
override var taskContext: TaskContext = NonBlockingContext
14+
}
15+
16+
@Suppress("NOTHING_TO_INLINE")
17+
internal actual inline fun SchedulerTask.afterTask() = taskContext.afterTask()

core/kotlinx-coroutines-core/src/scheduling/CoroutineScheduler.kt

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ internal class CoroutineScheduler(
340340
*/
341341
fun dispatch(block: Runnable, taskContext: TaskContext = NonBlockingContext, fair: Boolean = false) {
342342
timeSource.trackTask() // this is needed for virtual time support
343-
// TODO at some point make DispatchTask extend Task and make its field settable to save an allocation
344343
val task = createTask(block, taskContext)
345344
// try to submit the task to the local queue and act depending on the result
346345
when (submitToLocalQueue(task, fair)) {
@@ -357,7 +356,15 @@ internal class CoroutineScheduler(
357356
}
358357
}
359358

360-
internal fun createTask(block: Runnable, taskContext: TaskContext) = Task(block, schedulerTimeSource.nanoTime(), taskContext)
359+
internal fun createTask(block: Runnable, taskContext: TaskContext): Task {
360+
val nanoTime = schedulerTimeSource.nanoTime()
361+
if (block is Task) {
362+
block.submissionTime = nanoTime
363+
block.taskContext = taskContext
364+
return block
365+
}
366+
return TaskImpl(block, nanoTime, taskContext)
367+
}
361368

362369
/**
363370
* Unparks or creates a new [Worker] for executing non-blocking tasks if there are idle cores
@@ -724,20 +731,22 @@ internal class CoroutineScheduler(
724731
}
725732
wasIdle = true
726733
} else {
734+
// Note: read task.mode before running the task, because Task object will be reused after run
735+
val taskMode = task.mode
727736
if (wasIdle) {
728-
idleReset(task.mode)
737+
idleReset(taskMode)
729738
wasIdle = false
730739
}
731-
beforeTask(task)
740+
beforeTask(taskMode, task.submissionTime)
732741
runSafely(task)
733-
afterTask(task)
742+
afterTask(taskMode)
734743
}
735744
}
736745
tryReleaseCpu(WorkerState.TERMINATED)
737746
}
738747

739-
private fun beforeTask(task: Task) {
740-
if (task.mode != TaskMode.NON_BLOCKING) {
748+
private fun beforeTask(taskMode: TaskMode, taskSubmissionTime: Long) {
749+
if (taskMode != TaskMode.NON_BLOCKING) {
741750
/*
742751
* We should release CPU *before* checking for CPU starvation,
743752
* otherwise requestCpuWorker() will not count current thread as blocking
@@ -756,16 +765,16 @@ internal class CoroutineScheduler(
756765
return
757766
}
758767
val now = schedulerTimeSource.nanoTime()
759-
if (now - task.submissionTime >= WORK_STEALING_TIME_RESOLUTION_NS &&
768+
if (now - taskSubmissionTime >= WORK_STEALING_TIME_RESOLUTION_NS &&
760769
now - lastExhaustionTime >= WORK_STEALING_TIME_RESOLUTION_NS * 5
761770
) {
762771
lastExhaustionTime = now
763772
requestCpuWorker()
764773
}
765774
}
766775

767-
private fun afterTask(task: Task) {
768-
if (task.mode != TaskMode.NON_BLOCKING) {
776+
private fun afterTask(taskMode: TaskMode) {
777+
if (taskMode != TaskMode.NON_BLOCKING) {
769778
decrementBlockingWorkers()
770779
val currentState = state
771780
// Shutdown sequence of blocking dispatcher

core/kotlinx-coroutines-core/src/scheduling/Tasks.kt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,22 @@ internal object NonBlockingContext : TaskContext {
8282
}
8383
}
8484

85-
internal class Task(
86-
@JvmField val block: Runnable,
87-
@JvmField val submissionTime: Long,
88-
@JvmField val taskContext: TaskContext
89-
) : Runnable {
85+
internal interface Task : Runnable {
86+
var submissionTime: Long
87+
var taskContext: TaskContext
9088
val mode: TaskMode get() = taskContext.taskMode
91-
89+
}
90+
91+
internal class TaskImpl(
92+
@JvmField val block: Runnable,
93+
override var submissionTime: Long,
94+
override var taskContext: TaskContext
95+
) : SchedulerTaskBase() {
9296
override fun run() {
9397
try {
9498
block.run()
9599
} finally {
96-
taskContext.afterTask()
100+
afterTask()
97101
}
98102
}
99103

core/kotlinx-coroutines-core/test/scheduling/WorkQueueTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ internal fun GlobalQueue.asTimeList(): List<Long> {
130130
return result
131131
}
132132

133-
internal fun task(n: Long) = Task(Runnable {}, n, NonBlockingContext)
133+
internal fun task(n: Long) = TaskImpl(Runnable {}, n, NonBlockingContext)
134134

135135
internal fun WorkQueue.drain(): List<Long> {
136136
var task: Task? = poll()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
@Suppress("ACTUAL_WITHOUT_EXPECT")
8+
internal actual typealias SchedulerTask = Runnable
9+
10+
internal actual abstract class SchedulerTaskBase actual constructor() : SchedulerTask
11+
12+
@Suppress("NOTHING_TO_INLINE")
13+
internal actual inline fun SchedulerTask.afterTask() {}
14+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
@Suppress("ACTUAL_WITHOUT_EXPECT")
8+
internal actual typealias SchedulerTask = Runnable
9+
10+
internal actual abstract class SchedulerTaskBase actual constructor() : SchedulerTask
11+
12+
@Suppress("NOTHING_TO_INLINE")
13+
internal actual inline fun SchedulerTask.afterTask() {}

0 commit comments

Comments
 (0)