@@ -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, 16 ))
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,6 +69,7 @@ internal open class ArrayChannel<E>(
64
69
}
65
70
}
66
71
}
72
+ ensureCapacity(size)
67
73
buffer[(head + size) % capacity] = element // actually queue element
68
74
return OFFER_SUCCESS
69
75
}
@@ -112,6 +118,7 @@ internal open class ArrayChannel<E>(
112
118
this .size = size // restore size
113
119
return ALREADY_SELECTED
114
120
}
121
+ ensureCapacity(size)
115
122
buffer[(head + size) % capacity] = element // actually queue element
116
123
return OFFER_SUCCESS
117
124
}
@@ -123,6 +130,16 @@ 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
+ buffer.copyInto(newBuffer)
139
+ buffer = newBuffer
140
+ }
141
+ }
142
+
126
143
// result is `E | POLL_FAILED | Closed`
127
144
protected override fun pollInternal (): Any? {
128
145
var send: Send ? = null
0 commit comments