@@ -8,6 +8,7 @@ import kotlinx.coroutines.*
8
8
import kotlinx.coroutines.internal.*
9
9
import kotlinx.coroutines.selects.*
10
10
import kotlin.jvm.*
11
+ import kotlin.math.*
11
12
12
13
/* *
13
14
* Channel with array buffer of a fixed [capacity].
@@ -29,10 +30,14 @@ internal open class ArrayChannel<E>(
29
30
}
30
31
31
32
private val lock = ReentrantLock ()
32
- private val buffer: Array <Any ?> = arrayOfNulls<Any ?>(capacity)
33
+ /*
34
+ * Guarded by lock.
35
+ * Allocate minimum of capacity and 16 to avoid excess memory pressure for large channels when it's not necessary.
36
+ */
37
+ private var buffer: Array <Any ?> = arrayOfNulls<Any ?>(min(capacity, 8 ))
33
38
private var head: Int = 0
34
39
@Volatile
35
- private var size: Int = 0
40
+ private var size: Int = 0 // Invariant: size <= capacity
36
41
37
42
protected final override val isBufferAlwaysEmpty: Boolean get() = false
38
43
protected final override val isBufferEmpty: Boolean get() = size == 0
@@ -64,7 +69,8 @@ internal open class ArrayChannel<E>(
64
69
}
65
70
}
66
71
}
67
- buffer[(head + size) % capacity] = element // actually queue element
72
+ ensureCapacity(size)
73
+ buffer[(head + size) % buffer.size] = element // actually queue element
68
74
return OFFER_SUCCESS
69
75
}
70
76
// size == capacity: full
@@ -112,7 +118,8 @@ internal open class ArrayChannel<E>(
112
118
this .size = size // restore size
113
119
return ALREADY_SELECTED
114
120
}
115
- buffer[(head + size) % capacity] = element // actually queue element
121
+ ensureCapacity(size)
122
+ buffer[(head + size) % buffer.size] = element // actually queue element
116
123
return OFFER_SUCCESS
117
124
}
118
125
// size == capacity: full
@@ -123,6 +130,19 @@ internal open class ArrayChannel<E>(
123
130
return receive!! .offerResult
124
131
}
125
132
133
+ // Guarded by lock
134
+ private fun ensureCapacity (currentSize : Int ) {
135
+ if (currentSize >= buffer.size) {
136
+ val newSize = min(buffer.size * 2 , capacity)
137
+ val newBuffer = arrayOfNulls<Any ?>(newSize)
138
+ for (i in 0 until currentSize) {
139
+ newBuffer[i] = buffer[(head + i) % buffer.size]
140
+ }
141
+ buffer = newBuffer
142
+ head = 0
143
+ }
144
+ }
145
+
126
146
// result is `E | POLL_FAILED | Closed`
127
147
protected override fun pollInternal (): Any? {
128
148
var send: Send ? = null
@@ -149,9 +169,9 @@ internal open class ArrayChannel<E>(
149
169
}
150
170
if (replacement != = POLL_FAILED && replacement !is Closed <* >) {
151
171
this .size = size // restore size
152
- buffer[(head + size) % capacity ] = replacement
172
+ buffer[(head + size) % buffer.size ] = replacement
153
173
}
154
- head = (head + 1 ) % capacity
174
+ head = (head + 1 ) % buffer.size
155
175
}
156
176
// complete send the we're taken replacement from
157
177
if (token != null )
@@ -203,7 +223,7 @@ internal open class ArrayChannel<E>(
203
223
}
204
224
if (replacement != = POLL_FAILED && replacement !is Closed <* >) {
205
225
this .size = size // restore size
206
- buffer[(head + size) % capacity ] = replacement
226
+ buffer[(head + size) % buffer.size ] = replacement
207
227
} else {
208
228
// failed to poll or is already closed --> let's try to select receiving this element from buffer
209
229
if (! select.trySelect(null )) { // :todo: move trySelect completion outside of lock
@@ -212,7 +232,7 @@ internal open class ArrayChannel<E>(
212
232
return ALREADY_SELECTED
213
233
}
214
234
}
215
- head = (head + 1 ) % capacity
235
+ head = (head + 1 ) % buffer.size
216
236
}
217
237
// complete send the we're taken replacement from
218
238
if (token != null )
@@ -226,7 +246,7 @@ internal open class ArrayChannel<E>(
226
246
lock.withLock {
227
247
repeat(size) {
228
248
buffer[head] = 0
229
- head = (head + 1 ) % capacity
249
+ head = (head + 1 ) % buffer.size
230
250
}
231
251
size = 0
232
252
}
@@ -237,5 +257,5 @@ internal open class ArrayChannel<E>(
237
257
// ------ debug ------
238
258
239
259
override val bufferDebugString: String
240
- get() = " (buffer:capacity=${buffer.size} ,size=$size )"
260
+ get() = " (buffer:capacity=$capacity ,size=$size )"
241
261
}
0 commit comments