1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.buffer.api.pool;
17
18 import java.util.concurrent.locks.ReentrantLock;
19
20 import static io.netty5.buffer.api.pool.PoolChunk.IS_SUBPAGE_SHIFT;
21 import static io.netty5.buffer.api.pool.PoolChunk.IS_USED_SHIFT;
22 import static io.netty5.buffer.api.pool.PoolChunk.RUN_OFFSET_SHIFT;
23 import static io.netty5.buffer.api.pool.PoolChunk.SIZE_SHIFT;
24 import static io.netty5.buffer.api.pool.SizeClasses.LOG2_QUANTUM;
25
26 final class PoolSubpage implements PoolSubpageMetric {
27 final PoolChunk chunk;
28 private final int pageShifts;
29 private final int runOffset;
30 private final int runSize;
31 private final long[] bitmap;
32
33 PoolSubpage prev;
34 PoolSubpage next;
35
36 boolean doNotDestroy;
37 int elemSize;
38 private int maxNumElems;
39 private int bitmapLength;
40 private int nextAvail;
41 private int numAvail;
42
43 private final ReentrantLock lock = new ReentrantLock();
44
45
46 PoolSubpage() {
47 chunk = null;
48 pageShifts = -1;
49 runOffset = -1;
50 elemSize = -1;
51 runSize = -1;
52 bitmap = null;
53 }
54
55 PoolSubpage(PoolSubpage head, PoolChunk chunk, int pageShifts, int runOffset, int runSize, int elemSize) {
56 this.chunk = chunk;
57 this.pageShifts = pageShifts;
58 this.runOffset = runOffset;
59 this.runSize = runSize;
60 this.elemSize = elemSize;
61 bitmap = new long[runSize >>> 6 + LOG2_QUANTUM];
62
63 doNotDestroy = true;
64 if (elemSize != 0) {
65 maxNumElems = numAvail = runSize / elemSize;
66 nextAvail = 0;
67 bitmapLength = maxNumElems >>> 6;
68 if ((maxNumElems & 63) != 0) {
69 bitmapLength ++;
70 }
71
72 for (int i = 0; i < bitmapLength; i ++) {
73 bitmap[i] = 0;
74 }
75 }
76 addToPool(head);
77 }
78
79
80
81
82 long allocate() {
83 if (numAvail == 0 || !doNotDestroy) {
84 return -1;
85 }
86
87 final int bitmapIdx = getNextAvail();
88 int q = bitmapIdx >>> 6;
89 int r = bitmapIdx & 63;
90 assert (bitmap[q] >>> r & 1) == 0;
91 bitmap[q] |= 1L << r;
92
93 if (-- numAvail == 0) {
94 removeFromPool();
95 }
96
97 return toHandle(bitmapIdx);
98 }
99
100
101
102
103
104 boolean free(PoolSubpage head, int bitmapIdx) {
105 if (elemSize == 0) {
106 return true;
107 }
108 int q = bitmapIdx >>> 6;
109 int r = bitmapIdx & 63;
110 assert (bitmap[q] >>> r & 1) != 0;
111 bitmap[q] ^= 1L << r;
112
113 setNextAvail(bitmapIdx);
114
115 if (numAvail++ == 0) {
116 addToPool(head);
117
118
119
120
121 if (maxNumElems > 1) {
122 return true;
123 }
124 }
125
126 if (numAvail != maxNumElems) {
127 return true;
128 } else {
129
130 if (prev == next) {
131
132 return true;
133 }
134
135
136 doNotDestroy = false;
137 removeFromPool();
138 return false;
139 }
140 }
141
142 private void addToPool(PoolSubpage head) {
143 assert prev == null && next == null;
144 prev = head;
145 next = head.next;
146 next.prev = this;
147 head.next = this;
148 }
149
150 private void removeFromPool() {
151 assert prev != null && next != null;
152 prev.next = next;
153 next.prev = prev;
154 next = null;
155 prev = null;
156 }
157
158 private void setNextAvail(int bitmapIdx) {
159 nextAvail = bitmapIdx;
160 }
161
162 private int getNextAvail() {
163 int nextAvail = this.nextAvail;
164 if (nextAvail >= 0) {
165 this.nextAvail = -1;
166 return nextAvail;
167 }
168 return findNextAvail();
169 }
170
171 private int findNextAvail() {
172 final long[] bitmap = this.bitmap;
173 final int bitmapLength = this.bitmapLength;
174 for (int i = 0; i < bitmapLength; i ++) {
175 long bits = bitmap[i];
176 if (~bits != 0) {
177 return findNextAvail0(i, bits);
178 }
179 }
180 return -1;
181 }
182
183 private int findNextAvail0(int i, long bits) {
184 final int maxNumElems = this.maxNumElems;
185 final int baseVal = i << 6;
186
187 for (int j = 0; j < 64; j ++) {
188 if ((bits & 1) == 0) {
189 int val = baseVal | j;
190 if (val < maxNumElems) {
191 return val;
192 } else {
193 break;
194 }
195 }
196 bits >>>= 1;
197 }
198 return -1;
199 }
200
201 private long toHandle(int bitmapIdx) {
202 int pages = runSize >> pageShifts;
203 return (long) runOffset << RUN_OFFSET_SHIFT
204 | (long) pages << SIZE_SHIFT
205 | 1L << IS_USED_SHIFT
206 | 1L << IS_SUBPAGE_SHIFT
207 | bitmapIdx;
208 }
209
210 @Override
211 public String toString() {
212 final boolean doNotDestroy;
213 final int maxNumElems;
214 final int numAvail;
215 final int elemSize;
216 if (chunk == null) {
217
218 doNotDestroy = true;
219 maxNumElems = 0;
220 numAvail = 0;
221 elemSize = -1;
222 } else {
223 chunk.arena.lock();
224 try {
225 if (!this.doNotDestroy) {
226 doNotDestroy = false;
227
228 maxNumElems = numAvail = elemSize = -1;
229 } else {
230 doNotDestroy = true;
231 maxNumElems = this.maxNumElems;
232 numAvail = this.numAvail;
233 elemSize = this.elemSize;
234 }
235 } finally {
236 chunk.arena.unlock();
237 }
238 }
239
240 if (!doNotDestroy) {
241 return "(" + runOffset + ": not in use)";
242 }
243
244 return "(" + runOffset + ": " + (maxNumElems - numAvail) + '/' + maxNumElems +
245 ", offset: " + runOffset + ", length: " + runSize + ", elemSize: " + elemSize + ')';
246 }
247
248 @Override
249 public int maxNumElements() {
250 if (chunk == null) {
251
252 return 0;
253 }
254 chunk.arena.lock();
255 try {
256 return maxNumElems;
257 } finally {
258 chunk.arena.unlock();
259 }
260 }
261
262 @Override
263 public int numAvailable() {
264 if (chunk == null) {
265
266 return 0;
267 }
268 chunk.arena.lock();
269 try {
270 return numAvail;
271 } finally {
272 chunk.arena.unlock();
273 }
274 }
275
276 @Override
277 public int elementSize() {
278 if (chunk == null) {
279
280 return -1;
281 }
282 chunk.arena.lock();
283 try {
284 return elemSize;
285 } finally {
286 chunk.arena.unlock();
287 }
288 }
289
290 @Override
291 public int pageSize() {
292 return 1 << pageShifts;
293 }
294
295 void destroy() {
296 if (chunk != null) {
297 chunk.destroy();
298 }
299 }
300
301 void lock() {
302 lock.lock();
303 }
304
305 void unlock() {
306 lock.unlock();
307 }
308 }