@@ -7,6 +7,7 @@ package kotlinx.coroutines.scheduling
7
7
import kotlinx.atomicfu.*
8
8
import kotlinx.coroutines.*
9
9
import java.util.concurrent.atomic.*
10
+ import kotlin.jvm.internal.Ref.ObjectRef
10
11
11
12
internal const val BUFFER_CAPACITY_BASE = 7
12
13
internal const val BUFFER_CAPACITY = 1 shl BUFFER_CAPACITY_BASE
@@ -31,7 +32,7 @@ internal const val NOTHING_TO_STEAL = -2L
31
32
* (scheduler workers without a CPU permit steal blocking tasks via this mechanism). Such property enforces us to use CAS in
32
33
* order to properly claim value from the buffer.
33
34
* Moreover, [Task] objects are reusable, so it may seem that this queue is prone to ABA problem.
34
- * Indeed it formally has ABA-problem, but the whole processing logic is written in the way that such ABA is harmless.
35
+ * Indeed, it formally has ABA-problem, but the whole processing logic is written in the way that such ABA is harmless.
35
36
* I have discovered a truly marvelous proof of this, which this KDoc is too narrow to contain.
36
37
*/
37
38
internal class WorkQueue {
@@ -100,23 +101,22 @@ internal class WorkQueue {
100
101
}
101
102
102
103
/* *
103
- * Tries stealing from [victim] queue into this queue .
104
+ * Tries stealing from [victim] queue into the [stolenTaskRef] argument .
104
105
*
105
106
* Returns [NOTHING_TO_STEAL] if queue has nothing to steal, [TASK_STOLEN] if at least task was stolen
106
107
* or positive value of how many nanoseconds should pass until the head of this queue will be available to steal.
107
108
*/
108
- fun tryStealFrom (victim : WorkQueue ): Long {
109
+ fun tryStealFrom (victim : WorkQueue , stolenTaskRef : ObjectRef < Task ?> ): Long {
109
110
assert { bufferSize == 0 }
110
111
val task = victim.pollBuffer()
111
112
if (task != null ) {
112
- val notAdded = add(task)
113
- assert { notAdded == null }
113
+ stolenTaskRef.element = task
114
114
return TASK_STOLEN
115
115
}
116
- return tryStealLastScheduled(victim, blockingOnly = false )
116
+ return tryStealLastScheduled(victim, stolenTaskRef, blockingOnly = false )
117
117
}
118
118
119
- fun tryStealBlockingFrom (victim : WorkQueue ): Long {
119
+ fun tryStealBlockingFrom (victim : WorkQueue , stolenTaskRef : ObjectRef < Task ?> ): Long {
120
120
assert { bufferSize == 0 }
121
121
var start = victim.consumerIndex.value
122
122
val end = victim.producerIndex.value
@@ -128,13 +128,13 @@ internal class WorkQueue {
128
128
val value = buffer[index]
129
129
if (value != null && value.isBlocking && buffer.compareAndSet(index, value, null )) {
130
130
victim.blockingTasksInBuffer.decrementAndGet()
131
- add( value)
131
+ stolenTaskRef.element = value
132
132
return TASK_STOLEN
133
133
} else {
134
134
++ start
135
135
}
136
136
}
137
- return tryStealLastScheduled(victim, blockingOnly = true )
137
+ return tryStealLastScheduled(victim, stolenTaskRef, blockingOnly = true )
138
138
}
139
139
140
140
fun offloadAllWorkTo (globalQueue : GlobalQueue ) {
@@ -147,7 +147,7 @@ internal class WorkQueue {
147
147
/* *
148
148
* Contract on return value is the same as for [tryStealFrom]
149
149
*/
150
- private fun tryStealLastScheduled (victim : WorkQueue , blockingOnly : Boolean ): Long {
150
+ private fun tryStealLastScheduled (victim : WorkQueue , stolenTaskRef : ObjectRef < Task ?>, blockingOnly : Boolean ): Long {
151
151
while (true ) {
152
152
val lastScheduled = victim.lastScheduledTask.value ? : return NOTHING_TO_STEAL
153
153
if (blockingOnly && ! lastScheduled.isBlocking) return NOTHING_TO_STEAL
@@ -164,7 +164,7 @@ internal class WorkQueue {
164
164
* and dispatched another one. In the latter case we should retry to avoid missing task.
165
165
*/
166
166
if (victim.lastScheduledTask.compareAndSet(lastScheduled, null )) {
167
- add( lastScheduled)
167
+ stolenTaskRef.element = lastScheduled
168
168
return TASK_STOLEN
169
169
}
170
170
continue
0 commit comments