View Javadoc
1   /*
2    * Copyright 2013 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    *   http://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.internal.PlatformDependent;
21  
22  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
23  
24  /**
25   * Abstract base class for {@link ByteBuf} implementations that count references.
26   */
27  public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
28  
29      private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater;
30  
31      static {
32          AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> updater =
33                  PlatformDependent.newAtomicIntegerFieldUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
34          if (updater == null) {
35              updater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
36          }
37          refCntUpdater = updater;
38      }
39  
40      private volatile int refCnt = 1;
41  
42      protected AbstractReferenceCountedByteBuf(int maxCapacity) {
43          super(maxCapacity);
44      }
45  
46      @Override
47      public final int refCnt() {
48          return refCnt;
49      }
50  
51      /**
52       * An unsafe operation intended for use by a subclass that sets the reference count of the buffer directly
53       */
54      protected final void setRefCnt(int refCnt) {
55          this.refCnt = refCnt;
56      }
57  
58      @Override
59      public ByteBuf retain() {
60          for (;;) {
61              int refCnt = this.refCnt;
62              if (refCnt == 0) {
63                  throw new IllegalReferenceCountException(0, 1);
64              }
65              if (refCnt == Integer.MAX_VALUE) {
66                  throw new IllegalReferenceCountException(Integer.MAX_VALUE, 1);
67              }
68              if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
69                  break;
70              }
71          }
72          return this;
73      }
74  
75      @Override
76      public ByteBuf retain(int increment) {
77          if (increment <= 0) {
78              throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)");
79          }
80  
81          for (;;) {
82              int refCnt = this.refCnt;
83              if (refCnt == 0) {
84                  throw new IllegalReferenceCountException(0, increment);
85              }
86              if (refCnt > Integer.MAX_VALUE - increment) {
87                  throw new IllegalReferenceCountException(refCnt, increment);
88              }
89              if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) {
90                  break;
91              }
92          }
93          return this;
94      }
95  
96      @Override
97      public ByteBuf touch() {
98          return this;
99      }
100 
101     @Override
102     public ByteBuf touch(Object hint) {
103         return this;
104     }
105 
106     @Override
107     public final boolean release() {
108         for (;;) {
109             int refCnt = this.refCnt;
110             if (refCnt == 0) {
111                 throw new IllegalReferenceCountException(0, -1);
112             }
113 
114             if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
115                 if (refCnt == 1) {
116                     deallocate();
117                     return true;
118                 }
119                 return false;
120             }
121         }
122     }
123 
124     @Override
125     public final boolean release(int decrement) {
126         if (decrement <= 0) {
127             throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)");
128         }
129 
130         for (;;) {
131             int refCnt = this.refCnt;
132             if (refCnt < decrement) {
133                 throw new IllegalReferenceCountException(refCnt, -decrement);
134             }
135 
136             if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {
137                 if (refCnt == decrement) {
138                     deallocate();
139                     return true;
140                 }
141                 return false;
142             }
143         }
144     }
145 
146     /**
147      * Called once {@link #refCnt()} is equals 0.
148      */
149     protected abstract void deallocate();
150 }