|
4 | 4 |
|
5 | 5 | package kotlinx.coroutines.experimental
|
6 | 6 |
|
| 7 | +import kotlinx.coroutines.experimental.internal.* |
7 | 8 | import kotlinx.coroutines.experimental.timeunit.TimeUnit
|
8 |
| -import java.io.* |
| 9 | +import java.io.Closeable |
9 | 10 | import java.util.concurrent.*
|
10 | 11 | import kotlin.coroutines.experimental.*
|
11 | 12 |
|
@@ -63,40 +64,66 @@ public fun ExecutorService.asCoroutineDispatcher_Deprecated(): CloseableCoroutin
|
63 | 64 | public fun Executor.toCoroutineDispatcher(): CoroutineDispatcher =
|
64 | 65 | asCoroutineDispatcher()
|
65 | 66 |
|
66 |
| -private class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcherBase() |
| 67 | +private class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcherBase() { |
| 68 | + init { |
| 69 | + initFutureCancellation() |
| 70 | + } |
| 71 | +} |
67 | 72 |
|
68 | 73 | /**
|
69 | 74 | * @suppress **This is unstable API and it is subject to change.**
|
70 | 75 | */
|
71 | 76 | public abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispatcher(), Delay {
|
72 | 77 |
|
| 78 | + private var removesFutureOnCancellation: Boolean = false |
| 79 | + |
| 80 | + internal fun initFutureCancellation() { |
| 81 | + removesFutureOnCancellation = removeFutureOnCancel(executor) |
| 82 | + } |
| 83 | + |
73 | 84 | override fun dispatch(context: CoroutineContext, block: Runnable) =
|
74 | 85 | try { executor.execute(timeSource.trackTask(block)) }
|
75 | 86 | catch (e: RejectedExecutionException) {
|
76 | 87 | timeSource.unTrackTask()
|
77 | 88 | DefaultExecutor.execute(block)
|
78 | 89 | }
|
79 | 90 |
|
| 91 | + /* |
| 92 | + * removesFutureOnCancellation is required to avoid memory leak. |
| 93 | + * On Java 7+ we reflectively invoke ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true) and we're fine. |
| 94 | + * On Java 6 we're scheduling time-based coroutines to our own thread safe heap which supports cancellation. |
| 95 | + */ |
80 | 96 | override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
|
81 |
| - val timeout = |
82 |
| - try { (executor as? ScheduledExecutorService) |
83 |
| - ?.schedule(ResumeUndispatchedRunnable(this, continuation), time, unit) } |
84 |
| - catch (e: RejectedExecutionException) { null } |
85 |
| - if (timeout != null) |
86 |
| - continuation.cancelFutureOnCancellation(timeout) |
87 |
| - else |
88 |
| - DefaultExecutor.scheduleResumeAfterDelay(time, unit, continuation) |
| 97 | + val future = if (removesFutureOnCancellation) { |
| 98 | + scheduleBlock(ResumeUndispatchedRunnable(this, continuation), time, unit) |
| 99 | + } else { |
| 100 | + null |
| 101 | + } |
| 102 | + |
| 103 | + if (future != null) { |
| 104 | + continuation.cancelFutureOnCancellation(future) |
| 105 | + return |
| 106 | + } |
| 107 | + |
| 108 | + DefaultExecutor.scheduleResumeAfterDelay(time, unit, continuation) |
89 | 109 | }
|
90 | 110 |
|
91 | 111 | override fun invokeOnTimeout(time: Long, unit: TimeUnit, block: Runnable): DisposableHandle {
|
92 |
| - val timeout = |
93 |
| - try { (executor as? ScheduledExecutorService) |
94 |
| - ?.schedule(block, time, unit) } |
95 |
| - catch (e: RejectedExecutionException) { null } |
96 |
| - return if (timeout != null) |
97 |
| - DisposableFutureHandle(timeout) |
98 |
| - else |
99 |
| - DefaultExecutor.invokeOnTimeout(time, unit, block) |
| 112 | + val future = if (removesFutureOnCancellation) { |
| 113 | + scheduleBlock(block, time, unit) |
| 114 | + } else { |
| 115 | + null |
| 116 | + } |
| 117 | + |
| 118 | + return if (future != null ) DisposableFutureHandle(future) else DefaultExecutor.invokeOnTimeout(time, unit, block) |
| 119 | + } |
| 120 | + |
| 121 | + private fun scheduleBlock(block: Runnable, time: Long, unit: TimeUnit): ScheduledFuture<*>? { |
| 122 | + return try { |
| 123 | + (executor as? ScheduledExecutorService)?.schedule(block, time, unit) |
| 124 | + } catch (e: RejectedExecutionException) { |
| 125 | + null |
| 126 | + } |
100 | 127 | }
|
101 | 128 |
|
102 | 129 | override fun close() {
|
|
0 commit comments