1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty.channel;
16
17 import io.netty.buffer.ByteBuf;
18 import io.netty.buffer.ByteBufAllocator;
19 import io.netty.buffer.CompositeByteBuf;
20 import io.netty.util.internal.UnstableApi;
21 import io.netty.util.internal.logging.InternalLogger;
22 import io.netty.util.internal.logging.InternalLoggerFactory;
23
24 import java.util.ArrayDeque;
25
26 import static io.netty.util.ReferenceCountUtil.safeRelease;
27 import static io.netty.util.internal.ObjectUtil.checkNotNull;
28 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
29 import static io.netty.util.internal.PlatformDependent.throwException;
30
31 @UnstableApi
32 public abstract class AbstractCoalescingBufferQueue {
33 private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractCoalescingBufferQueue.class);
34 private final ArrayDeque<Object> bufAndListenerPairs;
35 private final PendingBytesTracker tracker;
36 private int readableBytes;
37
38
39
40
41
42
43
44
45 protected AbstractCoalescingBufferQueue(Channel channel, int initSize) {
46 bufAndListenerPairs = new ArrayDeque<Object>(initSize);
47 tracker = channel == null ? null : PendingBytesTracker.newTracker(channel);
48 }
49
50
51
52
53
54
55
56 public final void addFirst(ByteBuf buf, ChannelPromise promise) {
57 addFirst(buf, toChannelFutureListener(promise));
58 }
59
60 private void addFirst(ByteBuf buf, ChannelFutureListener listener) {
61
62 buf.touch();
63
64 if (listener != null) {
65 bufAndListenerPairs.addFirst(listener);
66 }
67 bufAndListenerPairs.addFirst(buf);
68 incrementReadableBytes(buf.readableBytes());
69 }
70
71
72
73
74 public final void add(ByteBuf buf) {
75 add(buf, (ChannelFutureListener) null);
76 }
77
78
79
80
81
82
83
84 public final void add(ByteBuf buf, ChannelPromise promise) {
85
86
87 add(buf, toChannelFutureListener(promise));
88 }
89
90
91
92
93
94
95
96 public final void add(ByteBuf buf, ChannelFutureListener listener) {
97
98 buf.touch();
99
100
101
102 bufAndListenerPairs.add(buf);
103 if (listener != null) {
104 bufAndListenerPairs.add(listener);
105 }
106 incrementReadableBytes(buf.readableBytes());
107 }
108
109
110
111
112
113
114 public final ByteBuf removeFirst(ChannelPromise aggregatePromise) {
115 Object entry = bufAndListenerPairs.poll();
116 if (entry == null) {
117 return null;
118 }
119 assert entry instanceof ByteBuf;
120 ByteBuf result = (ByteBuf) entry;
121
122 decrementReadableBytes(result.readableBytes());
123
124 entry = bufAndListenerPairs.peek();
125 if (entry instanceof ChannelFutureListener) {
126 aggregatePromise.addListener((ChannelFutureListener) entry);
127 bufAndListenerPairs.poll();
128 }
129 reconcileReadableBytes();
130 return result;
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144 public final ByteBuf remove(ByteBufAllocator alloc, int bytes, ChannelPromise aggregatePromise) {
145 checkPositiveOrZero(bytes, "bytes");
146 checkNotNull(aggregatePromise, "aggregatePromise");
147
148
149 if (bufAndListenerPairs.isEmpty()) {
150 reconcileReadableBytes();
151 return removeEmptyValue();
152 }
153 bytes = Math.min(bytes, readableBytes);
154
155 ByteBuf toReturn = null;
156 ByteBuf entryBuffer = null;
157 int originalBytes = bytes;
158 Object entry = null;
159 try {
160 for (;;) {
161 entry = bufAndListenerPairs.poll();
162 if (entry == null) {
163 break;
164 }
165
166 if (entry instanceof ByteBuf) {
167 entryBuffer = (ByteBuf) entry;
168 int bufferBytes = entryBuffer.readableBytes();
169
170 if (bufferBytes > bytes) {
171
172 bufAndListenerPairs.addFirst(entryBuffer);
173 if (bytes > 0) {
174
175 entryBuffer = entryBuffer.readRetainedSlice(bytes);
176
177 toReturn = toReturn == null ? entryBuffer
178 : compose(alloc, toReturn, entryBuffer);
179 bytes = 0;
180 }
181 break;
182 }
183
184 bytes -= bufferBytes;
185 if (toReturn == null) {
186
187 toReturn = bytes == 0
188 ? entryBuffer
189 : composeFirst(alloc, entryBuffer, bufferBytes + bytes);
190 } else {
191 toReturn = compose(alloc, toReturn, entryBuffer);
192 }
193 entryBuffer = null;
194 } else if (entry instanceof DelegatingChannelPromiseNotifier) {
195 aggregatePromise.addListener((DelegatingChannelPromiseNotifier) entry);
196 } else if (entry instanceof ChannelFutureListener) {
197 aggregatePromise.addListener((ChannelFutureListener) entry);
198 }
199 }
200 } catch (Throwable cause) {
201
202
203
204 decrementReadableBytes(originalBytes - bytes);
205
206
207 entry = bufAndListenerPairs.peek();
208 if (entry instanceof ChannelFutureListener) {
209 aggregatePromise.addListener((ChannelFutureListener) entry);
210 bufAndListenerPairs.poll();
211 }
212
213 safeRelease(entryBuffer);
214 safeRelease(toReturn);
215 aggregatePromise.setFailure(cause);
216 throwException(cause);
217 }
218 decrementReadableBytes(originalBytes - bytes);
219 reconcileReadableBytes();
220 return toReturn;
221 }
222
223
224
225
226 public final int readableBytes() {
227 return readableBytes;
228 }
229
230
231
232
233 public final boolean isEmpty() {
234 return bufAndListenerPairs.isEmpty();
235 }
236
237
238
239
240 public final void releaseAndFailAll(ChannelOutboundInvoker invoker, Throwable cause) {
241 releaseAndCompleteAll(invoker.newFailedFuture(cause));
242 }
243
244
245
246
247
248 public final void copyTo(AbstractCoalescingBufferQueue dest) {
249 dest.bufAndListenerPairs.addAll(bufAndListenerPairs);
250 dest.incrementReadableBytes(readableBytes);
251 }
252
253
254
255
256
257 public final void writeAndRemoveAll(ChannelHandlerContext ctx) {
258 Throwable pending = null;
259 ByteBuf previousBuf = null;
260 for (;;) {
261 Object entry = bufAndListenerPairs.poll();
262 try {
263 if (entry == null) {
264 if (previousBuf != null) {
265 decrementReadableBytes(previousBuf.readableBytes());
266 ctx.write(previousBuf, ctx.voidPromise());
267 }
268 break;
269 }
270
271 if (entry instanceof ByteBuf) {
272 if (previousBuf != null) {
273 decrementReadableBytes(previousBuf.readableBytes());
274 ctx.write(previousBuf, ctx.voidPromise());
275 }
276 previousBuf = (ByteBuf) entry;
277 } else if (entry instanceof ChannelPromise) {
278 decrementReadableBytes(previousBuf.readableBytes());
279 ctx.write(previousBuf, (ChannelPromise) entry);
280 previousBuf = null;
281 } else {
282 decrementReadableBytes(previousBuf.readableBytes());
283 ctx.write(previousBuf).addListener((ChannelFutureListener) entry);
284 previousBuf = null;
285 }
286 } catch (Throwable t) {
287 if (pending == null) {
288 pending = t;
289 } else {
290 logger.info("Throwable being suppressed because Throwable {} is already pending", pending, t);
291 }
292 }
293 }
294 reconcileReadableBytes();
295 if (pending != null) {
296 throw new IllegalStateException(pending);
297 }
298 }
299
300 @Override
301 public String toString() {
302 return "bytes: " + readableBytes + " buffers: " + (size() >> 1);
303 }
304
305
306
307
308 protected abstract ByteBuf compose(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf next);
309
310
311
312
313 protected final ByteBuf composeIntoComposite(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf next) {
314
315
316 CompositeByteBuf composite = alloc.compositeBuffer(size() + 2);
317 try {
318 composite.addComponent(true, cumulation);
319 composite.addComponent(true, next);
320 } catch (Throwable cause) {
321 composite.release();
322 safeRelease(next);
323 throwException(cause);
324 }
325 return composite;
326 }
327
328
329
330
331
332
333
334
335 protected final ByteBuf copyAndCompose(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf next) {
336 ByteBuf newCumulation = alloc.ioBuffer(cumulation.readableBytes() + next.readableBytes());
337 try {
338 newCumulation.writeBytes(cumulation).writeBytes(next);
339 } catch (Throwable cause) {
340 newCumulation.release();
341 safeRelease(next);
342 throwException(cause);
343 }
344 cumulation.release();
345 next.release();
346 return newCumulation;
347 }
348
349
350
351
352
353
354
355 protected ByteBuf composeFirst(ByteBufAllocator allocator, ByteBuf first, int bufferSize) {
356 return composeFirst(allocator, first);
357 }
358
359
360
361
362
363
364
365
366 @Deprecated
367 protected ByteBuf composeFirst(ByteBufAllocator allocator, ByteBuf first) {
368 return first;
369 }
370
371
372
373
374
375 protected abstract ByteBuf removeEmptyValue();
376
377
378
379
380
381 protected final int size() {
382 return bufAndListenerPairs.size();
383 }
384
385 private void releaseAndCompleteAll(ChannelFuture future) {
386 Throwable pending = null;
387 for (;;) {
388 Object entry = bufAndListenerPairs.poll();
389 if (entry == null) {
390 break;
391 }
392 try {
393 if (entry instanceof ByteBuf) {
394 ByteBuf buffer = (ByteBuf) entry;
395 decrementReadableBytes(buffer.readableBytes());
396 safeRelease(buffer);
397 } else {
398 ((ChannelFutureListener) entry).operationComplete(future);
399 }
400 } catch (Throwable t) {
401 if (pending == null) {
402 pending = t;
403 } else {
404 logger.info("Throwable being suppressed because Throwable {} is already pending", pending, t);
405 }
406 }
407 }
408 reconcileReadableBytes();
409 if (pending != null) {
410 throw new IllegalStateException(pending);
411 }
412 }
413
414 private void incrementReadableBytes(int increment) {
415 int nextReadableBytes = readableBytes + increment;
416 if (nextReadableBytes < readableBytes) {
417 throw new IllegalStateException("buffer queue length overflow: " + readableBytes + " + " + increment);
418 }
419 readableBytes = nextReadableBytes;
420 if (tracker != null) {
421 tracker.incrementPendingOutboundBytes(increment);
422 }
423 }
424
425 private void decrementReadableBytes(int decrement) {
426 readableBytes -= decrement;
427 assert readableBytes >= 0;
428 if (tracker != null) {
429 tracker.decrementPendingOutboundBytes(decrement);
430 }
431 }
432
433
434
435
436
437
438
439
440 private void reconcileReadableBytes() {
441 if (readableBytes != 0 && bufAndListenerPairs.isEmpty()) {
442 logger.error("readableBytes is {} but the queue is empty: a queued buffer was released or consumed " +
443 "while still referenced by the queue. This indicates a bug in the code that produced the " +
444 "buffer. Resetting readableBytes to 0.", readableBytes);
445 decrementReadableBytes(readableBytes);
446 }
447 }
448
449 private static ChannelFutureListener toChannelFutureListener(ChannelPromise promise) {
450 return promise.isVoid() ? null : new DelegatingChannelPromiseNotifier(promise);
451 }
452 }