View Javadoc
1   /*
2    * Copyright 2016 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty.buffer;
18  
19  import io.netty.util.IllegalReferenceCountException;
20  import io.netty.util.Recycler.EnhancedHandle;
21  import io.netty.util.internal.ObjectPool.Handle;
22  
23  import java.nio.ByteBuffer;
24  import java.nio.ByteOrder;
25  
26  /**
27   * Abstract base class for derived {@link ByteBuf} implementations.
28   */
29  abstract class AbstractPooledDerivedByteBuf extends AbstractReferenceCountedByteBuf {
30  
31      private final EnhancedHandle<AbstractPooledDerivedByteBuf> recyclerHandle;
32      private AbstractByteBuf rootParent;
33      /**
34       * Deallocations of a pooled derived buffer should always propagate through the entire chain of derived buffers.
35       * This is because each pooled derived buffer maintains its own reference count and we should respect each one.
36       * If deallocations cause a release of the "root parent" then we may prematurely release the underlying
37       * content before all the derived buffers have been released.
38       */
39      private ByteBuf parent;
40  
41      @SuppressWarnings("unchecked")
42      AbstractPooledDerivedByteBuf(Handle<? extends AbstractPooledDerivedByteBuf> recyclerHandle) {
43          super(0);
44          this.recyclerHandle = (EnhancedHandle<AbstractPooledDerivedByteBuf>) recyclerHandle;
45      }
46  
47      // Called from within SimpleLeakAwareByteBuf and AdvancedLeakAwareByteBuf.
48      final void parent(ByteBuf newParent) {
49          assert newParent instanceof SimpleLeakAwareByteBuf;
50          parent = newParent;
51      }
52  
53      @Override
54      public final AbstractByteBuf unwrap() {
55          AbstractByteBuf rootParent = this.rootParent;
56          if (rootParent == null) {
57              throw new IllegalReferenceCountException();
58          }
59          return rootParent;
60      }
61  
62      final <U extends AbstractPooledDerivedByteBuf> U init(
63              AbstractByteBuf unwrapped, ByteBuf wrapped, int readerIndex, int writerIndex, int maxCapacity) {
64          wrapped.retain(); // Retain up front to ensure the parent is accessible before doing more work.
65          parent = wrapped;
66          rootParent = unwrapped;
67  
68          try {
69              maxCapacity(maxCapacity);
70              setIndex0(readerIndex, writerIndex); // It is assumed the bounds checking is done by the caller.
71              resetRefCnt();
72  
73              @SuppressWarnings("unchecked")
74              final U castThis = (U) this;
75              wrapped = null;
76              return castThis;
77          } finally {
78              if (wrapped != null) {
79                  parent = rootParent = null;
80                  wrapped.release();
81              }
82          }
83      }
84  
85      @Override
86      protected final void deallocate() {
87          // We need to first store a reference to the parent before recycle this instance. This is needed as
88          // otherwise it is possible that the same AbstractPooledDerivedByteBuf is again obtained and init(...) is
89          // called before we actually have a chance to call release(). This leads to call release() on the wrong parent.
90          ByteBuf parent = this.parent;
91          // Remove references to parent and root so that they can be GCed for leak detection [netty/netty#14247]
92          this.parent = this.rootParent = null;
93          recyclerHandle.unguardedRecycle(this);
94          parent.release();
95      }
96  
97      @Override
98      public final ByteBufAllocator alloc() {
99          return unwrap().alloc();
100     }
101 
102     @Override
103     @Deprecated
104     public final ByteOrder order() {
105         return unwrap().order();
106     }
107 
108     @Override
109     public boolean isReadOnly() {
110         return unwrap().isReadOnly();
111     }
112 
113     @Override
114     public final boolean isDirect() {
115         return unwrap().isDirect();
116     }
117 
118     @Override
119     public boolean hasArray() {
120         return unwrap().hasArray();
121     }
122 
123     @Override
124     public byte[] array() {
125         return unwrap().array();
126     }
127 
128     @Override
129     public boolean hasMemoryAddress() {
130         return unwrap().hasMemoryAddress();
131     }
132 
133     @Override
134     public boolean isContiguous() {
135         return unwrap().isContiguous();
136     }
137 
138     @Override
139     public final int nioBufferCount() {
140         return unwrap().nioBufferCount();
141     }
142 
143     @Override
144     public final ByteBuffer internalNioBuffer(int index, int length) {
145         return nioBuffer(index, length);
146     }
147 
148     @Override
149     public final ByteBuf retainedSlice() {
150         final int index = readerIndex();
151         return retainedSlice(index, writerIndex() - index);
152     }
153 
154     @Override
155     public ByteBuf slice(int index, int length) {
156         ensureAccessible();
157         // All reference count methods should be inherited from this object (this is the "parent").
158         return new PooledNonRetainedSlicedByteBuf(this, unwrap(), index, length);
159     }
160 
161     final ByteBuf duplicate0() {
162         ensureAccessible();
163         // All reference count methods should be inherited from this object (this is the "parent").
164         return new PooledNonRetainedDuplicateByteBuf(this, unwrap());
165     }
166 
167     private static final class PooledNonRetainedDuplicateByteBuf extends UnpooledDuplicatedByteBuf {
168         private final ByteBuf referenceCountDelegate;
169 
170         PooledNonRetainedDuplicateByteBuf(ByteBuf referenceCountDelegate, AbstractByteBuf buffer) {
171             super(buffer);
172             this.referenceCountDelegate = referenceCountDelegate;
173         }
174 
175         @Override
176         boolean isAccessible0() {
177             return referenceCountDelegate.isAccessible();
178         }
179 
180         @Override
181         int refCnt0() {
182             return referenceCountDelegate.refCnt();
183         }
184 
185         @Override
186         ByteBuf retain0() {
187             referenceCountDelegate.retain();
188             return this;
189         }
190 
191         @Override
192         ByteBuf retain0(int increment) {
193             referenceCountDelegate.retain(increment);
194             return this;
195         }
196 
197         @Override
198         ByteBuf touch0() {
199             referenceCountDelegate.touch();
200             return this;
201         }
202 
203         @Override
204         ByteBuf touch0(Object hint) {
205             referenceCountDelegate.touch(hint);
206             return this;
207         }
208 
209         @Override
210         boolean release0() {
211             return referenceCountDelegate.release();
212         }
213 
214         @Override
215         boolean release0(int decrement) {
216             return referenceCountDelegate.release(decrement);
217         }
218 
219         @Override
220         public ByteBuf duplicate() {
221             ensureAccessible();
222             return new PooledNonRetainedDuplicateByteBuf(referenceCountDelegate, this);
223         }
224 
225         @Override
226         public ByteBuf retainedDuplicate() {
227             return PooledDuplicatedByteBuf.newInstance(unwrap(), this, readerIndex(), writerIndex());
228         }
229 
230         @Override
231         public ByteBuf slice(int index, int length) {
232             checkIndex(index, length);
233             return new PooledNonRetainedSlicedByteBuf(referenceCountDelegate, unwrap(), index, length);
234         }
235 
236         @Override
237         public ByteBuf retainedSlice() {
238             return retainedSlice(readerIndex(), readableBytes());
239         }
240 
241         @Override
242         public ByteBuf retainedSlice(int index, int length) {
243             return PooledSlicedByteBuf.newInstance(unwrap(), this, index, length);
244         }
245     }
246 
247     private static final class PooledNonRetainedSlicedByteBuf extends UnpooledSlicedByteBuf {
248         private final ByteBuf referenceCountDelegate;
249 
250         PooledNonRetainedSlicedByteBuf(ByteBuf referenceCountDelegate,
251                                        AbstractByteBuf buffer, int index, int length) {
252             super(buffer, index, length);
253             this.referenceCountDelegate = referenceCountDelegate;
254         }
255 
256         @Override
257         boolean isAccessible0() {
258             return referenceCountDelegate.isAccessible();
259         }
260 
261         @Override
262         int refCnt0() {
263             return referenceCountDelegate.refCnt();
264         }
265 
266         @Override
267         ByteBuf retain0() {
268             referenceCountDelegate.retain();
269             return this;
270         }
271 
272         @Override
273         ByteBuf retain0(int increment) {
274             referenceCountDelegate.retain(increment);
275             return this;
276         }
277 
278         @Override
279         ByteBuf touch0() {
280             referenceCountDelegate.touch();
281             return this;
282         }
283 
284         @Override
285         ByteBuf touch0(Object hint) {
286             referenceCountDelegate.touch(hint);
287             return this;
288         }
289 
290         @Override
291         boolean release0() {
292             return referenceCountDelegate.release();
293         }
294 
295         @Override
296         boolean release0(int decrement) {
297             return referenceCountDelegate.release(decrement);
298         }
299 
300         @Override
301         public ByteBuf duplicate() {
302             ensureAccessible();
303             return new PooledNonRetainedDuplicateByteBuf(referenceCountDelegate, unwrap())
304                     .setIndex(idx(readerIndex()), idx(writerIndex()));
305         }
306 
307         @Override
308         public ByteBuf retainedDuplicate() {
309             return PooledDuplicatedByteBuf.newInstance(unwrap(), this, idx(readerIndex()), idx(writerIndex()));
310         }
311 
312         @Override
313         public ByteBuf slice(int index, int length) {
314             checkIndex(index, length);
315             return new PooledNonRetainedSlicedByteBuf(referenceCountDelegate, unwrap(), idx(index), length);
316         }
317 
318         @Override
319         public ByteBuf retainedSlice() {
320             return retainedSlice(readerIndex(), readableBytes());
321         }
322 
323         @Override
324         public ByteBuf retainedSlice(int index, int length) {
325             return PooledSlicedByteBuf.newInstance(unwrap(), this, idx(index), length);
326         }
327     }
328 }