4
4
5
5
package kotlinx.coroutines
6
6
7
+ import kotlinx.coroutines.flow.*
7
8
import kotlinx.coroutines.internal.*
8
9
import java.io.*
9
10
import java.util.concurrent.*
@@ -39,6 +40,22 @@ public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closea
39
40
/* *
40
41
* Converts an instance of [ExecutorService] to an implementation of [ExecutorCoroutineDispatcher].
41
42
*
43
+ * ## Interaction with [delay] and time-based coroutines.
44
+ *
45
+ * If the given [ExecutorService] is an instance of [ScheduledExecutorService], then all time-related
46
+ * coroutine operations such as [delay], [withTimeout] and time-based [Flow] operators will be scheduled
47
+ * on this executor using [schedule][ScheduledExecutorService.schedule] method. If the corresponding
48
+ * coroutine is cancelled, [ScheduledFuture.cancel] will be invoked on the corresponding future.
49
+ *
50
+ * If the given [ExecutorService] is an instance of [ScheduledThreadPoolExecutor], then prior to any scheduling,
51
+ * remove on cancel policy will be set via [ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy] in order
52
+ * to reduce the memory pressure of cancelled coroutines.
53
+ *
54
+ * If the executor service is neither of this types, the separate internal thread will be used to
55
+ * _track_ the delay and time-related executions, but the coroutine itself will still be executed
56
+ * on top of the given executor.
57
+ *
58
+ * ## Rejected execution
42
59
* If the underlying executor throws [RejectedExecutionException] on
43
60
* attempt to submit a continuation task (it happens when [closing][ExecutorCoroutineDispatcher.close] the
44
61
* resulting dispatcher, on underlying executor [shutdown][ExecutorService.shutdown], or when it uses limited queues),
@@ -52,6 +69,23 @@ public fun ExecutorService.asCoroutineDispatcher(): ExecutorCoroutineDispatcher
52
69
/* *
53
70
* Converts an instance of [Executor] to an implementation of [CoroutineDispatcher].
54
71
*
72
+ * ## Interaction with [delay] and time-based coroutines.
73
+ *
74
+ * If the given [Executor] is an instance of [ScheduledExecutorService], then all time-related
75
+ * coroutine operations such as [delay], [withTimeout] and time-based [Flow] operators will be scheduled
76
+ * on this executor using [schedule][ScheduledExecutorService.schedule] method. If the corresponding
77
+ * coroutine is cancelled, [ScheduledFuture.cancel] will be invoked on the corresponding future.
78
+ *
79
+ * If the given [Executor] is an instance of [ScheduledThreadPoolExecutor], then prior to any scheduling,
80
+ * remove on cancel policy will be set via [ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy] in order
81
+ * to reduce the memory pressure of cancelled coroutines.
82
+ *
83
+ * If the executor is neither of this types, the separate internal thread will be used to
84
+ * _track_ the delay and time-related executions, but the coroutine itself will still be executed
85
+ * on top of the given executor.
86
+ *
87
+ * ## Rejected execution
88
+ *
55
89
* If the underlying executor throws [RejectedExecutionException] on
56
90
* attempt to submit a continuation task (it happens when [closing][ExecutorCoroutineDispatcher.close] the
57
91
* resulting dispatcher, on underlying executor [shutdown][ExecutorService.shutdown], or when it uses limited queues),
@@ -75,18 +109,15 @@ private class DispatcherExecutor(@JvmField val dispatcher: CoroutineDispatcher)
75
109
override fun toString (): String = dispatcher.toString()
76
110
}
77
111
78
- private class ExecutorCoroutineDispatcherImpl (override val executor : Executor ) : ExecutorCoroutineDispatcherBase() {
79
- init {
80
- initFutureCancellation()
81
- }
82
- }
112
+ internal class ExecutorCoroutineDispatcherImpl (override val executor : Executor ) : ExecutorCoroutineDispatcher(), Delay {
83
113
84
- internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispatcher (), Delay {
85
-
86
- private var removesFutureOnCancellation: Boolean = false
87
-
88
- internal fun initFutureCancellation () {
89
- removesFutureOnCancellation = removeFutureOnCancel(executor)
114
+ /*
115
+ * Attempts to reflectively (to be Java 6 compatible) invoke
116
+ * ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy in order to cleanup
117
+ * internal scheduler queue on cancellation.
118
+ */
119
+ init {
120
+ removeFutureOnCancel(executor)
90
121
}
91
122
92
123
override fun dispatch (context : CoroutineContext , block : Runnable ) {
@@ -99,17 +130,12 @@ internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispa
99
130
}
100
131
}
101
132
102
- /*
103
- * removesFutureOnCancellation is required to avoid memory leak.
104
- * On Java 7+ we reflectively invoke ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true) and we're fine.
105
- * On Java 6 we're scheduling time-based coroutines to our own thread safe heap which supports cancellation.
106
- */
107
133
override fun scheduleResumeAfterDelay (timeMillis : Long , continuation : CancellableContinuation <Unit >) {
108
- val future = if (removesFutureOnCancellation) {
109
- scheduleBlock( ResumeUndispatchedRunnable (this , continuation), continuation.context, timeMillis)
110
- } else {
111
- null
112
- }
134
+ val future = (executor as ? ScheduledExecutorService )?.scheduleBlock(
135
+ ResumeUndispatchedRunnable (this , continuation),
136
+ continuation.context,
137
+ timeMillis
138
+ )
113
139
// If everything went fine and the scheduling attempt was not rejected -- use it
114
140
if (future != null ) {
115
141
continuation.cancelFutureOnCancellation(future)
@@ -120,20 +146,16 @@ internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispa
120
146
}
121
147
122
148
override fun invokeOnTimeout (timeMillis : Long , block : Runnable , context : CoroutineContext ): DisposableHandle {
123
- val future = if (removesFutureOnCancellation) {
124
- scheduleBlock(block, context, timeMillis)
125
- } else {
126
- null
127
- }
149
+ val future = (executor as ? ScheduledExecutorService )?.scheduleBlock(block, context, timeMillis)
128
150
return when {
129
151
future != null -> DisposableFutureHandle (future)
130
152
else -> DefaultExecutor .invokeOnTimeout(timeMillis, block, context)
131
153
}
132
154
}
133
155
134
- private fun scheduleBlock (block : Runnable , context : CoroutineContext , timeMillis : Long ): ScheduledFuture <* >? {
156
+ private fun ScheduledExecutorService. scheduleBlock (block : Runnable , context : CoroutineContext , timeMillis : Long ): ScheduledFuture <* >? {
135
157
return try {
136
- (executor as ? ScheduledExecutorService )?. schedule(block, timeMillis, TimeUnit .MILLISECONDS )
158
+ schedule(block, timeMillis, TimeUnit .MILLISECONDS )
137
159
} catch (e: RejectedExecutionException ) {
138
160
cancelJobOnRejection(context, e)
139
161
null
@@ -149,7 +171,7 @@ internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispa
149
171
}
150
172
151
173
override fun toString (): String = executor.toString()
152
- override fun equals (other : Any? ): Boolean = other is ExecutorCoroutineDispatcherBase && other.executor == = executor
174
+ override fun equals (other : Any? ): Boolean = other is ExecutorCoroutineDispatcherImpl && other.executor == = executor
153
175
override fun hashCode (): Int = System .identityHashCode(executor)
154
176
}
155
177
0 commit comments