1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.util.internal;
17
18 import static io.netty.util.internal.ObjectUtil.checkPositive;
19
20 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
21
22 import io.netty.util.IllegalReferenceCountException;
23 import io.netty.util.ReferenceCounted;
24
25
26
27
28 public abstract class ReferenceCountUpdater<T extends ReferenceCounted> {
29
30
31
32
33
34
35
36
37
38
39
40
41 protected ReferenceCountUpdater() { }
42
43 public static long getUnsafeOffset(Class<? extends ReferenceCounted> clz, String fieldName) {
44 try {
45 if (PlatformDependent.hasUnsafe()) {
46 return PlatformDependent.objectFieldOffset(clz.getDeclaredField(fieldName));
47 }
48 } catch (Throwable ignore) {
49
50 }
51 return -1;
52 }
53
54 protected abstract AtomicIntegerFieldUpdater<T> updater();
55
56 protected abstract long unsafeOffset();
57
58 public final int initialValue() {
59 return 2;
60 }
61
62 public void setInitialValue(T instance) {
63 final long offset = unsafeOffset();
64 if (offset == -1) {
65 updater().set(instance, initialValue());
66 } else {
67 PlatformDependent.safeConstructPutInt(instance, offset, initialValue());
68 }
69 }
70
71 private static int realRefCnt(int rawCnt) {
72 return rawCnt != 2 && rawCnt != 4 && (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1;
73 }
74
75
76
77
78 private static int toLiveRealRefCnt(int rawCnt, int decrement) {
79 if (rawCnt == 2 || rawCnt == 4 || (rawCnt & 1) == 0) {
80 return rawCnt >>> 1;
81 }
82
83 throw new IllegalReferenceCountException(0, -decrement);
84 }
85
86 private int nonVolatileRawCnt(T instance) {
87
88 final long offset = unsafeOffset();
89 return offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);
90 }
91
92 public final int refCnt(T instance) {
93 return realRefCnt(updater().get(instance));
94 }
95
96 public final boolean isLiveNonVolatile(T instance) {
97 final long offset = unsafeOffset();
98 final int rawCnt = offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);
99
100
101 return rawCnt == 2 || rawCnt == 4 || rawCnt == 6 || rawCnt == 8 || (rawCnt & 1) == 0;
102 }
103
104
105
106
107 public final void setRefCnt(T instance, int refCnt) {
108 updater().set(instance, refCnt > 0 ? refCnt << 1 : 1);
109 }
110
111
112
113
114 public final void resetRefCnt(T instance) {
115
116 updater().lazySet(instance, initialValue());
117 }
118
119 public final T retain(T instance) {
120 return retain0(instance, 1, 2);
121 }
122
123 public final T retain(T instance, int increment) {
124
125 int rawIncrement = checkPositive(increment, "increment") << 1;
126 return retain0(instance, increment, rawIncrement);
127 }
128
129
130 private T retain0(T instance, final int increment, final int rawIncrement) {
131 int oldRef = updater().getAndAdd(instance, rawIncrement);
132 if (oldRef != 2 && oldRef != 4 && (oldRef & 1) != 0) {
133 throw new IllegalReferenceCountException(0, increment);
134 }
135
136 if ((oldRef <= 0 && oldRef + rawIncrement >= 0)
137 || (oldRef >= 0 && oldRef + rawIncrement < oldRef)) {
138
139 updater().getAndAdd(instance, -rawIncrement);
140 throw new IllegalReferenceCountException(realRefCnt(oldRef), increment);
141 }
142 return instance;
143 }
144
145 public final boolean release(T instance) {
146 int rawCnt = nonVolatileRawCnt(instance);
147 return rawCnt == 2 ? tryFinalRelease0(instance, 2) || retryRelease0(instance, 1)
148 : nonFinalRelease0(instance, 1, rawCnt, toLiveRealRefCnt(rawCnt, 1));
149 }
150
151 public final boolean release(T instance, int decrement) {
152 int rawCnt = nonVolatileRawCnt(instance);
153 int realCnt = toLiveRealRefCnt(rawCnt, checkPositive(decrement, "decrement"));
154 return decrement == realCnt ? tryFinalRelease0(instance, rawCnt) || retryRelease0(instance, decrement)
155 : nonFinalRelease0(instance, decrement, rawCnt, realCnt);
156 }
157
158 private boolean tryFinalRelease0(T instance, int expectRawCnt) {
159 return updater().compareAndSet(instance, expectRawCnt, 1);
160 }
161
162 private boolean nonFinalRelease0(T instance, int decrement, int rawCnt, int realCnt) {
163 if (decrement < realCnt
164
165 && updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
166 return false;
167 }
168 return retryRelease0(instance, decrement);
169 }
170
171 private boolean retryRelease0(T instance, int decrement) {
172 for (;;) {
173 int rawCnt = updater().get(instance), realCnt = toLiveRealRefCnt(rawCnt, decrement);
174 if (decrement == realCnt) {
175 if (tryFinalRelease0(instance, rawCnt)) {
176 return true;
177 }
178 } else if (decrement < realCnt) {
179
180 if (updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
181 return false;
182 }
183 } else {
184 throw new IllegalReferenceCountException(realCnt, -decrement);
185 }
186 Thread.yield();
187 }
188 }
189 }