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    *   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  package io.netty.util;
17  
18  import java.lang.invoke.MethodHandles;
19  import java.lang.invoke.VarHandle;
20  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
21  
22  import io.netty.util.internal.AtomicReferenceCountUpdater;
23  import io.netty.util.internal.PlatformDependent;
24  import io.netty.util.internal.ReferenceCountUpdater;
25  import io.netty.util.internal.UnsafeReferenceCountUpdater;
26  import io.netty.util.internal.VarHandleReferenceCountUpdater;
27  
28  import static io.netty.util.internal.ReferenceCountUpdater.getUnsafeOffset;
29  import static java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater;
30  
31  /**
32   * Abstract base class for classes wants to implement {@link ReferenceCounted}.
33   */
34  public abstract class AbstractReferenceCounted implements ReferenceCounted {
35      private static final long REFCNT_FIELD_OFFSET;
36      private static final AtomicIntegerFieldUpdater<AbstractReferenceCounted> AIF_UPDATER;
37      private static final Object REFCNT_FIELD_VH;
38      private static final ReferenceCountUpdater<AbstractReferenceCounted> updater;
39  
40      static {
41          switch (ReferenceCountUpdater.updaterTypeOf(AbstractReferenceCounted.class, "refCnt")) {
42              case Atomic:
43                  AIF_UPDATER = newUpdater(AbstractReferenceCounted.class, "refCnt");
44                  REFCNT_FIELD_OFFSET = -1;
45                  REFCNT_FIELD_VH = null;
46                  updater = new AtomicReferenceCountUpdater<AbstractReferenceCounted>() {
47                      @Override
48                      protected AtomicIntegerFieldUpdater<AbstractReferenceCounted> updater() {
49                          return AIF_UPDATER;
50                      }
51                  };
52                  break;
53              case Unsafe:
54                  AIF_UPDATER = null;
55                  REFCNT_FIELD_OFFSET = getUnsafeOffset(AbstractReferenceCounted.class, "refCnt");
56                  REFCNT_FIELD_VH = null;
57                  updater = new UnsafeReferenceCountUpdater<AbstractReferenceCounted>() {
58                      @Override
59                      protected long refCntFieldOffset() {
60                          return REFCNT_FIELD_OFFSET;
61                      }
62                  };
63                  break;
64              case VarHandle:
65                  AIF_UPDATER = null;
66                  REFCNT_FIELD_OFFSET = -1;
67                  REFCNT_FIELD_VH = PlatformDependent.findVarHandleOfIntField(MethodHandles.lookup(),
68                          AbstractReferenceCounted.class, "refCnt");
69                  updater = new VarHandleReferenceCountUpdater<AbstractReferenceCounted>() {
70                      @Override
71                      protected VarHandle varHandle() {
72                          return (VarHandle) REFCNT_FIELD_VH;
73                      }
74                  };
75                  break;
76              default:
77                  throw new Error("Unknown updater type for AbstractReferenceCounted");
78          }
79      }
80  
81      // Value might not equal "real" reference count, all access should be via the updater
82      @SuppressWarnings({"unused", "FieldMayBeFinal"})
83      private volatile int refCnt = updater.initialValue();
84  
85      @Override
86      public int refCnt() {
87          return updater.refCnt(this);
88      }
89  
90      /**
91       * An unsafe operation intended for use by a subclass that sets the reference count of the buffer directly
92       */
93      protected final void setRefCnt(int refCnt) {
94          updater.setRefCnt(this, refCnt);
95      }
96  
97      @Override
98      public ReferenceCounted retain() {
99          return updater.retain(this);
100     }
101 
102     @Override
103     public ReferenceCounted retain(int increment) {
104         return updater.retain(this, increment);
105     }
106 
107     @Override
108     public ReferenceCounted touch() {
109         return touch(null);
110     }
111 
112     @Override
113     public boolean release() {
114         return handleRelease(updater.release(this));
115     }
116 
117     @Override
118     public boolean release(int decrement) {
119         return handleRelease(updater.release(this, decrement));
120     }
121 
122     private boolean handleRelease(boolean result) {
123         if (result) {
124             deallocate();
125         }
126         return result;
127     }
128 
129     /**
130      * Called once {@link #refCnt()} is equals 0.
131      */
132     protected abstract void deallocate();
133 }