4
4
5
5
package kotlinx.coroutines.experimental
6
6
7
+ import kotlinx.coroutines.experimental.internal.*
7
8
import java.io.*
9
+ import java.io.Closeable
8
10
import java.util.concurrent.*
9
11
import kotlin.coroutines.experimental.*
10
12
@@ -18,7 +20,7 @@ import kotlin.coroutines.experimental.*
18
20
public abstract class ExecutorCoroutineDispatcher : CloseableCoroutineDispatcher (), Closeable {
19
21
/* *
20
22
* Closes this coroutine dispatcher and shuts down its executor.
21
- *
23
+ *
22
24
* It may throw an exception if this dispatcher is global and cannot be closed.
23
25
*/
24
26
public abstract override fun close ()
@@ -74,41 +76,67 @@ public fun ExecutorService.asCoroutineDispatcher_Deprecated(): CloseableCoroutin
74
76
public fun Executor.toCoroutineDispatcher (): CoroutineDispatcher =
75
77
asCoroutineDispatcher()
76
78
77
- private class ExecutorCoroutineDispatcherImpl (override val executor : Executor ) : ExecutorCoroutineDispatcherBase()
79
+ private class ExecutorCoroutineDispatcherImpl (override val executor : Executor ) : ExecutorCoroutineDispatcherBase() {
80
+ init {
81
+ initFutureCancellation()
82
+ }
83
+ }
78
84
79
85
/* *
80
86
* @suppress **This is unstable API and it is subject to change.**
81
87
*/
82
88
@InternalCoroutinesApi
83
89
public abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispatcher (), Delay {
84
90
91
+ private var removesFutureOnCancellation: Boolean = false
92
+
93
+ internal fun initFutureCancellation () {
94
+ removesFutureOnCancellation = removeFutureOnCancel(executor)
95
+ }
96
+
85
97
override fun dispatch (context : CoroutineContext , block : Runnable ) =
86
98
try { executor.execute(timeSource.trackTask(block)) }
87
99
catch (e: RejectedExecutionException ) {
88
100
timeSource.unTrackTask()
89
101
DefaultExecutor .execute(block)
90
102
}
91
103
104
+ /*
105
+ * removesFutureOnCancellation is required to avoid memory leak.
106
+ * On Java 7+ we reflectively invoke ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true) and we're fine.
107
+ * On Java 6 we're scheduling time-based coroutines to our own thread safe heap which supports cancellation.
108
+ */
92
109
override fun scheduleResumeAfterDelay (timeMillis : Long , continuation : CancellableContinuation <Unit >) {
93
- val timeout =
94
- try { (executor as ? ScheduledExecutorService )
95
- ?.schedule(ResumeUndispatchedRunnable (this , continuation), timeMillis, TimeUnit .MILLISECONDS ) }
96
- catch (e: RejectedExecutionException ) { null }
97
- if (timeout != null )
98
- continuation.cancelFutureOnCancellation(timeout)
99
- else
100
- DefaultExecutor .scheduleResumeAfterDelay(timeMillis, continuation)
110
+ val future = if (removesFutureOnCancellation) {
111
+ scheduleBlock(ResumeUndispatchedRunnable (this , continuation), timeMillis, TimeUnit .MILLISECONDS )
112
+ } else {
113
+ null
114
+ }
115
+ // If everything went fine and the scheduling attempt was not rejected -- use it
116
+ if (future != null ) {
117
+ continuation.cancelFutureOnCancellation(future)
118
+ return
119
+ }
120
+ // Otherwise fallback to default executor
121
+ DefaultExecutor .scheduleResumeAfterDelay(timeMillis, continuation)
101
122
}
102
123
103
124
override fun invokeOnTimeout (timeMillis : Long , block : Runnable ): DisposableHandle {
104
- val timeout =
105
- try { (executor as ? ScheduledExecutorService )
106
- ?.schedule(block, timeMillis, TimeUnit .MILLISECONDS ) }
107
- catch (e: RejectedExecutionException ) { null }
108
- return if (timeout != null )
109
- DisposableFutureHandle (timeout)
110
- else
111
- DefaultExecutor .invokeOnTimeout(timeMillis, block)
125
+ val future = if (removesFutureOnCancellation) {
126
+ scheduleBlock(block, timeMillis, TimeUnit .MILLISECONDS )
127
+ } else {
128
+ null
129
+ }
130
+
131
+ return if (future != null ) DisposableFutureHandle (future) else DefaultExecutor .invokeOnTimeout(timeMillis, block)
132
+ }
133
+
134
+ private fun scheduleBlock (block : Runnable , time : Long , unit : TimeUnit ): ScheduledFuture <* >? {
135
+ return try {
136
+ (executor as ? ScheduledExecutorService )?.schedule(block, time, unit)
137
+ } catch (e: RejectedExecutionException ) {
138
+ null
139
+ }
112
140
}
113
141
114
142
override fun close () {
0 commit comments