@@ -18,7 +18,9 @@ package kotlinx.coroutines.experimental
18
18
19
19
import kotlinx.coroutines.experimental.internal.LockFreeLinkedListHead
20
20
import kotlinx.coroutines.experimental.internal.LockFreeLinkedListNode
21
- import java.util.concurrent.ConcurrentSkipListMap
21
+ import kotlinx.coroutines.experimental.internal.ThreadSafeHeap
22
+ import kotlinx.coroutines.experimental.internal.ThreadSafeHeapNode
23
+ import java.util.concurrent.Future
22
24
import java.util.concurrent.TimeUnit
23
25
import java.util.concurrent.atomic.AtomicLong
24
26
import java.util.concurrent.locks.LockSupport
@@ -67,7 +69,7 @@ internal class EventLoopImpl(
67
69
private val thread : Thread
68
70
) : CoroutineDispatcher(), EventLoop, Delay {
69
71
private val queue = LockFreeLinkedListHead ()
70
- private val delayed = ConcurrentSkipListMap < DelayedTask , DelayedTask >()
72
+ private val delayed = ThreadSafeHeap < DelayedTask >()
71
73
private val nextSequence = AtomicLong ()
72
74
private var parentJob: Job ? = null
73
75
@@ -78,9 +80,10 @@ internal class EventLoopImpl(
78
80
79
81
override fun dispatch (context : CoroutineContext , block : Runnable ) {
80
82
if (scheduleQueued(QueuedRunnableTask (block))) {
83
+ // todo: we should unpark only when this task became first in the queue
81
84
unpark()
82
85
} else {
83
- block.run ()
86
+ block.run () // otherwise run it right here (as if Unconfined)
84
87
}
85
88
}
86
89
@@ -89,42 +92,56 @@ internal class EventLoopImpl(
89
92
// todo: we should unpark only when this delayed task became first in the queue
90
93
unpark()
91
94
} else {
92
- scheduledExecutor.schedule(ResumeRunnable (continuation), time, unit)
95
+ scheduledExecutor.schedule(ResumeRunnable (continuation), time, unit) // otherwise reschedule to other time pool
93
96
}
94
97
}
95
98
96
- override fun invokeOnTimeout (time : Long , unit : TimeUnit , block : Runnable ): DisposableHandle =
97
- DelayedRunnableTask (time, unit, block).also { scheduleDelayed(it) }
99
+ override fun invokeOnTimeout (time : Long , unit : TimeUnit , block : Runnable ): DisposableHandle {
100
+ val delayedTask = DelayedRunnableTask (time, unit, block)
101
+ if (scheduleDelayed(delayedTask)) {
102
+ // todo: we should unpark only when this delayed task became first in the queue
103
+ unpark()
104
+ return delayedTask
105
+ }
106
+ return DisposableFutureHandle (scheduledExecutor.schedule(block, time, unit))
107
+ }
98
108
99
109
override fun processNextEvent (): Long {
100
110
if (Thread .currentThread() != = thread) return Long .MAX_VALUE
101
111
// queue all delayed tasks that are due to be executed
102
- while (true ) {
103
- val delayedTask = delayed.firstEntry()?.key ? : break
112
+ if (! delayed.isEmpty) {
104
113
val now = System .nanoTime()
105
- if (delayedTask.nanoTime - now > 0 ) break
106
- if (! scheduleQueued(delayedTask)) break
107
- delayed.remove(delayedTask)
114
+ while (true ) {
115
+ val delayedTask = delayed.removeFirstIf { it.timeToExecute(now) } ? : break
116
+ queue.addLast(delayedTask)
117
+ }
108
118
}
109
119
// then process one event from queue
110
120
(queue.removeFirstOrNull() as ? QueuedTask )?.let { queuedTask ->
111
- queuedTask()
121
+ queuedTask. run ()
112
122
}
113
123
if (! queue.isEmpty) return 0
114
- val nextDelayedTask = delayed.firstEntry()?.key ? : return Long .MAX_VALUE
115
- return nextDelayedTask.nanoTime - System .nanoTime()
124
+ val nextDelayedTask = delayed.peek() ? : return Long .MAX_VALUE
125
+ return ( nextDelayedTask.nanoTime - System .nanoTime()).coerceAtLeast( 0 )
116
126
}
117
127
128
+ private val isActive: Boolean get() = parentJob?.isCompleted != true
129
+
118
130
fun shutdown () {
131
+ assert (! isActive)
119
132
// complete processing of all queued tasks
120
133
while (true ) {
121
134
val queuedTask = (queue.removeFirstOrNull() ? : break ) as QueuedTask
122
- queuedTask()
135
+ queuedTask. run ()
123
136
}
124
- // cancel all delayed tasks
137
+ // reschedule or execute delayed tasks
125
138
while (true ) {
126
- val delayedTask = delayed.pollFirstEntry()?.key ? : break
127
- delayedTask.cancel()
139
+ val delayedTask = delayed.removeFirst() ? : break
140
+ val now = System .nanoTime()
141
+ if (delayedTask.timeToExecute(now))
142
+ delayedTask.run ()
143
+ else
144
+ delayedTask.rescheduleOnShutdown(now)
128
145
}
129
146
}
130
147
@@ -133,35 +150,38 @@ internal class EventLoopImpl(
133
150
queue.addLast(queuedTask)
134
151
return true
135
152
}
136
- return queue.addLastIf(queuedTask, { ! parentJob !! .isCompleted })
153
+ return queue.addLastIf(queuedTask) { isActive }
137
154
}
138
155
139
156
private fun scheduleDelayed (delayedTask : DelayedTask ): Boolean {
140
- delayed.put(delayedTask, delayedTask)
141
- if (parentJob?.isActive != false ) return true
142
- delayedTask.dispose()
143
- return false
157
+ if (parentJob == null ) {
158
+ delayed.addLast(delayedTask)
159
+ return true
160
+ }
161
+ return delayed.addLastIf(delayedTask) { isActive }
144
162
}
145
163
146
164
private fun unpark () {
147
165
if (Thread .currentThread() != = thread)
148
166
LockSupport .unpark(thread)
149
167
}
150
168
151
- private abstract class QueuedTask : LockFreeLinkedListNode (), () -> Unit
169
+ private abstract class QueuedTask : LockFreeLinkedListNode (), Runnable
152
170
153
171
private class QueuedRunnableTask (
154
172
private val block : Runnable
155
173
) : QueuedTask() {
156
- override fun invoke () { block.run () }
174
+ override fun run () { block.run () }
157
175
override fun toString (): String = block.toString()
158
176
}
159
177
160
178
private abstract inner class DelayedTask (
161
179
time : Long , timeUnit : TimeUnit
162
- ) : QueuedTask(), Comparable<DelayedTask>, DisposableHandle {
180
+ ) : QueuedTask(), Comparable<DelayedTask>, DisposableHandle, ThreadSafeHeapNode {
181
+ override var index: Int = - 1
163
182
@JvmField val nanoTime: Long = System .nanoTime() + timeUnit.toNanos(time)
164
183
@JvmField val sequence: Long = nextSequence.getAndIncrement()
184
+ private var scheduledAfterShutdown: Future <* >? = null
165
185
166
186
override fun compareTo (other : DelayedTask ): Int {
167
187
val dTime = nanoTime - other.nanoTime
@@ -171,12 +191,21 @@ internal class EventLoopImpl(
171
191
return if (dSequence > 0 ) 1 else if (dSequence < 0 ) - 1 else 0
172
192
}
173
193
174
- override final fun dispose () {
175
- delayed.remove(this )
176
- cancel()
194
+ fun timeToExecute (now : Long ): Boolean = now - nanoTime >= 0L
195
+
196
+ fun rescheduleOnShutdown (now : Long ) = synchronized(delayed) {
197
+ if (delayed.remove(this )) {
198
+ assert (scheduledAfterShutdown == null )
199
+ scheduledAfterShutdown = scheduledExecutor.schedule(this , nanoTime - now, TimeUnit .NANOSECONDS )
200
+ }
177
201
}
178
202
179
- open fun cancel () {}
203
+ override final fun dispose () = synchronized(delayed) {
204
+ if (! delayed.remove(this )) {
205
+ scheduledAfterShutdown?.cancel(false )
206
+ scheduledAfterShutdown = null
207
+ }
208
+ }
180
209
181
210
override fun toString (): String = " Delayed[nanos=$nanoTime ,seq=$sequence ]"
182
211
}
@@ -185,21 +214,16 @@ internal class EventLoopImpl(
185
214
time : Long , timeUnit : TimeUnit ,
186
215
private val cont : CancellableContinuation <Unit >
187
216
) : DelayedTask(time, timeUnit) {
188
- override fun invoke () {
217
+ override fun run () {
189
218
with (cont) { resumeUndispatched(Unit ) }
190
219
}
191
- override fun cancel () {
192
- if (! cont.isActive) return
193
- val remaining = nanoTime - System .nanoTime()
194
- scheduledExecutor.schedule(ResumeRunnable (cont), remaining, TimeUnit .NANOSECONDS )
195
- }
196
220
}
197
221
198
222
private inner class DelayedRunnableTask (
199
223
time : Long , timeUnit : TimeUnit ,
200
224
private val block : Runnable
201
225
) : DelayedTask(time, timeUnit) {
202
- override fun invoke () { block.run () }
226
+ override fun run () { block.run () }
203
227
override fun toString (): String = super .toString() + block.toString()
204
228
}
205
229
}
0 commit comments