@@ -6,6 +6,48 @@ import kotlin.concurrent.Volatile
6
6
import kotlin.coroutines.*
7
7
import kotlin.jvm.*
8
8
9
+ internal interface UnconfinedEventLoop {
10
+ /* *
11
+ * Returns `true` if calling [yield] in a coroutine in this event loop can avoid yielding and continue executing
12
+ * due to there being no other tasks in the queue.
13
+ *
14
+ * This can only be called from the thread that owns this event loop.
15
+ */
16
+ val thisLoopsTaskCanAvoidYielding: Boolean
17
+
18
+ /* *
19
+ * Returns `true` if someone (typically a call to [runUnconfinedEventLoop]) is currently processing the tasks,
20
+ * so calling [dispatchUnconfined] is guaranteed to be processed eventually.
21
+ *
22
+ * This can only be called from the thread that owns this event loop.
23
+ */
24
+ val isUnconfinedLoopActive: Boolean
25
+
26
+ /* *
27
+ * Executes [initialBlock] and then processes unconfined tasks until there are no more, blocking the current thread.
28
+ *
29
+ * This can only be called when no other [runUnconfinedEventLoop] is currently active on this event loop.
30
+ *
31
+ * This can only be called from the thread that owns this event loop.
32
+ */
33
+ fun runUnconfinedEventLoop (initialBlock : () -> Unit )
34
+
35
+ /* *
36
+ * Sends the [task] to this event loop for execution.
37
+ *
38
+ * This method should only be called while [isUnconfinedLoopActive] is `true`.
39
+ * Otherwise, the task may be left unprocessed.
40
+ *
41
+ * This can only be called from the thread that owns this event loop.
42
+ */
43
+ fun dispatchUnconfined (task : DispatchedTask <* >)
44
+
45
+ /* *
46
+ * Tries to interpret this event loop for unconfined tasks as a proper event loop and returns it if successful.
47
+ */
48
+ fun tryUseAsEventLoop (): EventLoop ?
49
+ }
50
+
9
51
/* *
10
52
* Extended by [CoroutineDispatcher] implementations that have event loop inside and can
11
53
* be asked to process next event from their event queue.
@@ -16,7 +58,7 @@ import kotlin.jvm.*
16
58
*
17
59
* @suppress **This an internal API and should not be used from general code.**
18
60
*/
19
- internal abstract class EventLoop : CoroutineDispatcher () {
61
+ internal abstract class EventLoop : CoroutineDispatcher (), UnconfinedEventLoop {
20
62
/* *
21
63
* Counts the number of nested `runBlocking` and [Dispatchers.Unconfined] that use this event loop.
22
64
*/
@@ -51,8 +93,6 @@ internal abstract class EventLoop : CoroutineDispatcher() {
51
93
return 0
52
94
}
53
95
54
- protected open val isEmpty: Boolean get() = isUnconfinedQueueEmpty
55
-
56
96
protected open val nextTime: Long
57
97
get() {
58
98
val queue = unconfinedQueue ? : return Long .MAX_VALUE
@@ -65,6 +105,7 @@ internal abstract class EventLoop : CoroutineDispatcher() {
65
105
task.run ()
66
106
return true
67
107
}
108
+
68
109
/* *
69
110
* Returns `true` if the invoking `runBlocking(context) { ... }` that was passed this event loop in its context
70
111
* parameter should call [processNextEvent] for this event loop (otherwise, it will process thread-local one).
@@ -77,28 +118,26 @@ internal abstract class EventLoop : CoroutineDispatcher() {
77
118
* Dispatches task whose dispatcher returned `false` from [CoroutineDispatcher.isDispatchNeeded]
78
119
* into the current event loop.
79
120
*/
80
- fun dispatchUnconfined (task : DispatchedTask <* >) {
81
- val queue = unconfinedQueue ? :
82
- ArrayDeque <DispatchedTask <* >>().also { unconfinedQueue = it }
121
+ override fun dispatchUnconfined (task : DispatchedTask <* >) {
122
+ val queue = unconfinedQueue ? : ArrayDeque <DispatchedTask <* >>().also { unconfinedQueue = it }
83
123
queue.addLast(task)
84
124
}
85
125
86
126
val isActive: Boolean
87
127
get() = useCount > 0
88
128
89
- val isUnconfinedLoopActive: Boolean
129
+ override val isUnconfinedLoopActive: Boolean
90
130
get() = useCount >= delta(unconfined = true )
91
131
92
- // May only be used from the event loop's thread
93
- val isUnconfinedQueueEmpty: Boolean
94
- get() = unconfinedQueue?.isEmpty() ? : true
132
+ override val thisLoopsTaskCanAvoidYielding: Boolean
133
+ get() = unconfinedQueue?.isEmpty() != false
95
134
96
135
private fun delta (unconfined : Boolean ) =
97
136
if (unconfined) (1L shl 32 ) else 1L
98
137
99
138
fun incrementUseCount (unconfined : Boolean = false) {
100
139
useCount + = delta(unconfined)
101
- if (! unconfined) shared = true
140
+ if (! unconfined) shared = true
102
141
}
103
142
104
143
fun decrementUseCount (unconfined : Boolean = false) {
@@ -117,22 +156,37 @@ internal abstract class EventLoop : CoroutineDispatcher() {
117
156
}
118
157
119
158
open fun shutdown () {}
159
+
160
+ override fun runUnconfinedEventLoop (initialBlock : () -> Unit ) {
161
+ incrementUseCount(unconfined = true )
162
+ try {
163
+ initialBlock()
164
+ while (true ) {
165
+ // break when all unconfined continuations where executed
166
+ if (! processUnconfinedEvent()) break
167
+ }
168
+ } finally {
169
+ decrementUseCount(unconfined = true )
170
+ }
171
+ }
172
+
173
+ override fun tryUseAsEventLoop (): EventLoop ? = this
120
174
}
121
175
122
176
internal object ThreadLocalEventLoop {
123
- private val ref = commonThreadLocal<EventLoop ?>(Symbol (" ThreadLocalEventLoop" ))
177
+ private val ref = commonThreadLocal<UnconfinedEventLoop ?>(Symbol (" ThreadLocalEventLoop" ))
124
178
125
- internal val eventLoop : EventLoop
179
+ internal val unconfinedEventLoop : UnconfinedEventLoop
126
180
get() = ref.get() ? : createEventLoop().also { ref.set(it) }
127
181
128
- internal fun currentOrNull (): EventLoop ? =
182
+ internal fun currentOrNull (): UnconfinedEventLoop ? =
129
183
ref.get()
130
184
131
185
internal fun resetEventLoop () {
132
186
ref.set(null )
133
187
}
134
188
135
- internal fun setEventLoop (eventLoop : EventLoop ) {
189
+ internal fun setEventLoop (eventLoop : UnconfinedEventLoop ) {
136
190
ref.set(eventLoop)
137
191
}
138
192
}
@@ -183,8 +237,10 @@ internal abstract class EventLoopImplBase: EventLoopImplPlatform(), Delay {
183
237
get() = _isCompleted .value
184
238
set(value) { _isCompleted .value = value }
185
239
186
- override val isEmpty: Boolean get() {
187
- if (! isUnconfinedQueueEmpty) return false
240
+ /* *
241
+ * Checks that at the moment this method is called, there are no tasks in the delayed tasks queue.
242
+ */
243
+ protected val delayedQueueIsEmpty: Boolean get() {
188
244
val delayed = _delayed .value
189
245
if (delayed != null && ! delayed.isEmpty) return false
190
246
return when (val queue = _queue .value) {
@@ -383,12 +439,6 @@ internal abstract class EventLoopImplBase: EventLoopImplPlatform(), Delay {
383
439
return delayedTask.scheduleTask(now, delayedQueue, this )
384
440
}
385
441
386
- // It performs "hard" shutdown for test cleanup purposes
387
- protected fun resetAll () {
388
- _queue .value = null
389
- _delayed .value = null
390
- }
391
-
392
442
// This is a "soft" (normal) shutdown
393
443
private fun rescheduleAllDelayed () {
394
444
val now = nanoTime()
0 commit comments