View Javadoc
1   /*
2    * Copyright 2019 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.internal;
17  
18  import static io.netty.util.internal.ObjectUtil.checkPositive;
19  
20  import io.netty.util.IllegalReferenceCountException;
21  import io.netty.util.ReferenceCounted;
22  
23  /**
24   * Common logic for {@link ReferenceCounted} implementations
25   * @deprecated Instead of extending this class, prefer instead to include a {@link RefCnt} field and delegate to that.
26   * This approach has better compatibility with Graal Native Image.
27   */
28  @Deprecated
29  public abstract class ReferenceCountUpdater<T extends ReferenceCounted> {
30      /*
31       * Implementation notes:
32       *
33       * For the updated int field:
34       *   Even => "real" refcount is (refCnt >>> 1)
35       *   Odd  => "real" refcount is 0
36       */
37  
38      protected ReferenceCountUpdater() {
39      }
40  
41      protected abstract void safeInitializeRawRefCnt(T refCntObj, int value);
42  
43      protected abstract int getAndAddRawRefCnt(T refCntObj, int increment);
44  
45      protected abstract int getRawRefCnt(T refCnt);
46  
47      protected abstract int getAcquireRawRefCnt(T refCnt);
48  
49      protected abstract void setReleaseRawRefCnt(T refCnt, int value);
50  
51      protected abstract boolean casRawRefCnt(T refCnt, int expected, int value);
52  
53      public final int initialValue() {
54          return 2;
55      }
56  
57      public final void setInitialValue(T instance) {
58          safeInitializeRawRefCnt(instance, initialValue());
59      }
60  
61      private static int realRefCnt(int rawCnt) {
62          return rawCnt >>> 1;
63      }
64  
65      public final int refCnt(T instance) {
66          return realRefCnt(getAcquireRawRefCnt(instance));
67      }
68  
69      public final boolean isLiveNonVolatile(T instance) {
70          final int rawCnt = getRawRefCnt(instance);
71          if (rawCnt == 2) {
72              return true;
73          }
74          return (rawCnt & 1) == 0;
75      }
76  
77      /**
78       * An unsafe operation that sets the reference count directly
79       */
80      public final void setRefCnt(T instance, int refCnt) {
81          int rawRefCnt = refCnt > 0 ? refCnt << 1 : 1; // overflow OK here
82          setReleaseRawRefCnt(instance, rawRefCnt);
83      }
84  
85      /**
86       * Resets the reference count to 1
87       */
88      public final void resetRefCnt(T instance) {
89          // no need of a volatile set, it should happen in a quiescent state
90          setReleaseRawRefCnt(instance, initialValue());
91      }
92  
93      public final T retain(T instance) {
94          return retain0(instance, 2);
95      }
96  
97      public final T retain(T instance, int increment) {
98          return retain0(instance, checkPositive(increment, "increment") << 1);
99      }
100 
101     private T retain0(T instance, int increment) {
102         int oldRef = getAndAddRawRefCnt(instance, increment);
103         // oldRef & 0x80000001 stands for oldRef < 0 || oldRef is odd
104         // NOTE: we're optimizing for inlined and constant folded increment here -> which will make
105         // Integer.MAX_VALUE - increment to be computed at compile time
106         if ((oldRef & 0x80000001) != 0 || oldRef > Integer.MAX_VALUE - increment) {
107             getAndAddRawRefCnt(instance, -increment);
108             throw new IllegalReferenceCountException(0, increment >>> 1);
109         }
110         return instance;
111     }
112 
113     public final boolean release(T instance) {
114         return release0(instance, 2);
115     }
116 
117     public final boolean release(T instance, int decrement) {
118         return release0(instance, checkPositive(decrement, "decrement") << 1);
119     }
120 
121     private boolean release0(final T instance, final int decrement) {
122         int curr, next;
123         do {
124             curr = getRawRefCnt(instance);
125             if (curr == decrement) {
126                 next = 1;
127             } else {
128                 if (curr < decrement || (curr & 1) == 1) {
129                     throwIllegalRefCountOnRelease(decrement, curr);
130                 }
131                 next = curr - decrement;
132             }
133         } while (!casRawRefCnt(instance, curr, next));
134         return (next & 1) == 1;
135     }
136 
137     private static void throwIllegalRefCountOnRelease(int decrement, int curr) {
138         throw new IllegalReferenceCountException(curr >>> 1, -(decrement >>> 1));
139     }
140 
141     public enum UpdaterType {
142         Unsafe,
143         VarHandle,
144         Atomic
145     }
146 
147     public static <T extends ReferenceCounted> UpdaterType updaterTypeOf(Class<T> clz, String fieldName) {
148         long fieldOffset = getUnsafeOffset(clz, fieldName);
149         if (fieldOffset >= 0) {
150             return UpdaterType.Unsafe;
151         }
152         if (PlatformDependent.hasVarHandle()) {
153             return UpdaterType.VarHandle;
154         }
155         return UpdaterType.Atomic;
156     }
157 
158     public static long getUnsafeOffset(Class<? extends ReferenceCounted> clz, String fieldName) {
159         try {
160             if (PlatformDependent.hasUnsafe()) {
161                 return PlatformDependent.objectFieldOffset(clz.getDeclaredField(fieldName));
162             }
163         } catch (Throwable ignore) {
164             // fall-back
165         }
166         return -1;
167     }
168 }