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