1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.util.internal;
17
18 import io.netty5.util.IllegalReferenceCountException;
19 import io.netty5.util.ReferenceCounted;
20
21 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
22
23 import static io.netty5.util.internal.ObjectUtil.checkPositive;
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 private static int realRefCnt(int rawCnt) {
63 return rawCnt != 2 && rawCnt != 4 && (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1;
64 }
65
66
67
68
69 private static int toLiveRealRefCnt(int rawCnt, int decrement) {
70 if (rawCnt == 2 || rawCnt == 4 || (rawCnt & 1) == 0) {
71 return rawCnt >>> 1;
72 }
73
74 throw new IllegalReferenceCountException(0, -decrement);
75 }
76
77 private int nonVolatileRawCnt(T instance) {
78
79 final long offset = unsafeOffset();
80 return offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);
81 }
82
83 public final int refCnt(T instance) {
84 return realRefCnt(updater().get(instance));
85 }
86
87 public final boolean isLiveNonVolatile(T instance) {
88 final long offset = unsafeOffset();
89 final int rawCnt = offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);
90
91
92 return rawCnt == 2 || rawCnt == 4 || rawCnt == 6 || rawCnt == 8 || (rawCnt & 1) == 0;
93 }
94
95
96
97
98 public final void setRefCnt(T instance, int refCnt) {
99 updater().set(instance, refCnt > 0 ? refCnt << 1 : 1);
100 }
101
102
103
104
105 public final void resetRefCnt(T instance) {
106 updater().set(instance, initialValue());
107 }
108
109 public final T retain(T instance) {
110 return retain0(instance, 1, 2);
111 }
112
113 public final T retain(T instance, int increment) {
114
115 int rawIncrement = checkPositive(increment, "increment") << 1;
116 return retain0(instance, increment, rawIncrement);
117 }
118
119
120 private T retain0(T instance, final int increment, final int rawIncrement) {
121 int oldRef = updater().getAndAdd(instance, rawIncrement);
122 if (oldRef != 2 && oldRef != 4 && (oldRef & 1) != 0) {
123 throw new IllegalReferenceCountException(0, increment);
124 }
125
126 if ((oldRef <= 0 && oldRef + rawIncrement >= 0)
127 || (oldRef >= 0 && oldRef + rawIncrement < oldRef)) {
128
129 updater().getAndAdd(instance, -rawIncrement);
130 throw new IllegalReferenceCountException(realRefCnt(oldRef), increment);
131 }
132 return instance;
133 }
134
135 public final boolean release(T instance) {
136 int rawCnt = nonVolatileRawCnt(instance);
137 return rawCnt == 2 ? tryFinalRelease0(instance, 2) || retryRelease0(instance, 1)
138 : nonFinalRelease0(instance, 1, rawCnt, toLiveRealRefCnt(rawCnt, 1));
139 }
140
141 public final boolean release(T instance, int decrement) {
142 int rawCnt = nonVolatileRawCnt(instance);
143 int realCnt = toLiveRealRefCnt(rawCnt, checkPositive(decrement, "decrement"));
144 return decrement == realCnt ? tryFinalRelease0(instance, rawCnt) || retryRelease0(instance, decrement)
145 : nonFinalRelease0(instance, decrement, rawCnt, realCnt);
146 }
147
148 private boolean tryFinalRelease0(T instance, int expectRawCnt) {
149 return updater().compareAndSet(instance, expectRawCnt, 1);
150 }
151
152 private boolean nonFinalRelease0(T instance, int decrement, int rawCnt, int realCnt) {
153 if (decrement < realCnt
154
155 && updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
156 return false;
157 }
158 return retryRelease0(instance, decrement);
159 }
160
161 private boolean retryRelease0(T instance, int decrement) {
162 for (;;) {
163 int rawCnt = updater().get(instance), realCnt = toLiveRealRefCnt(rawCnt, decrement);
164 if (decrement == realCnt) {
165 if (tryFinalRelease0(instance, rawCnt)) {
166 return true;
167 }
168 } else if (decrement < realCnt) {
169
170 if (updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
171 return false;
172 }
173 } else {
174 throw new IllegalReferenceCountException(realCnt, -decrement);
175 }
176 Thread.yield();
177 }
178 }
179 }