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             // Capacity is not allowed to change for a sliced ByteBuf, so length == capacity()
239             return retainedSlice(readerIndex(), capacity());
240         }
241 
242         @Override
243         public ByteBuf retainedSlice(int index, int length) {
244             return PooledSlicedByteBuf.newInstance(unwrap(), this, index, length);
245         }
246     }
247 
248     private static final class PooledNonRetainedSlicedByteBuf extends UnpooledSlicedByteBuf {
249         private final ByteBuf referenceCountDelegate;
250 
251         PooledNonRetainedSlicedByteBuf(ByteBuf referenceCountDelegate,
252                                        AbstractByteBuf buffer, int index, int length) {
253             super(buffer, index, length);
254             this.referenceCountDelegate = referenceCountDelegate;
255         }
256 
257         @Override
258         boolean isAccessible0() {
259             return referenceCountDelegate.isAccessible();
260         }
261 
262         @Override
263         int refCnt0() {
264             return referenceCountDelegate.refCnt();
265         }
266 
267         @Override
268         ByteBuf retain0() {
269             referenceCountDelegate.retain();
270             return this;
271         }
272 
273         @Override
274         ByteBuf retain0(int increment) {
275             referenceCountDelegate.retain(increment);
276             return this;
277         }
278 
279         @Override
280         ByteBuf touch0() {
281             referenceCountDelegate.touch();
282             return this;
283         }
284 
285         @Override
286         ByteBuf touch0(Object hint) {
287             referenceCountDelegate.touch(hint);
288             return this;
289         }
290 
291         @Override
292         boolean release0() {
293             return referenceCountDelegate.release();
294         }
295 
296         @Override
297         boolean release0(int decrement) {
298             return referenceCountDelegate.release(decrement);
299         }
300 
301         @Override
302         public ByteBuf duplicate() {
303             ensureAccessible();
304             return new PooledNonRetainedDuplicateByteBuf(referenceCountDelegate, unwrap())
305                     .setIndex(idx(readerIndex()), idx(writerIndex()));
306         }
307 
308         @Override
309         public ByteBuf retainedDuplicate() {
310             return PooledDuplicatedByteBuf.newInstance(unwrap(), this, idx(readerIndex()), idx(writerIndex()));
311         }
312 
313         @Override
314         public ByteBuf slice(int index, int length) {
315             checkIndex(index, length);
316             return new PooledNonRetainedSlicedByteBuf(referenceCountDelegate, unwrap(), idx(index), length);
317         }
318 
319         @Override
320         public ByteBuf retainedSlice() {
321             // Capacity is not allowed to change for a sliced ByteBuf, so length == capacity()
322             return retainedSlice(0, capacity());
323         }
324 
325         @Override
326         public ByteBuf retainedSlice(int index, int length) {
327             return PooledSlicedByteBuf.newInstance(unwrap(), this, idx(index), length);
328         }
329     }
330 }