1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.util.internal;
17
18 import io.netty.util.IllegalReferenceCountException;
19
20 import java.lang.invoke.MethodHandles;
21 import java.lang.invoke.VarHandle;
22 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
23
24 import static io.netty.util.internal.ObjectUtil.checkPositive;
25
26
27
28
29
30
31 @SuppressWarnings("deprecation")
32 public final class RefCnt {
33
34 private static final int UNSAFE = 0;
35 private static final int VAR_HANDLE = 1;
36 private static final int ATOMIC_UPDATER = 2;
37 private static final int REF_CNT_IMPL;
38
39 static {
40 if (PlatformDependent.hasUnsafe()) {
41 REF_CNT_IMPL = UNSAFE;
42 } else if (PlatformDependent.hasVarHandle()) {
43 REF_CNT_IMPL = VAR_HANDLE;
44 } else {
45 REF_CNT_IMPL = ATOMIC_UPDATER;
46 }
47 }
48
49
50
51
52
53
54
55
56
57
58 volatile int value;
59
60 public RefCnt() {
61 switch (REF_CNT_IMPL) {
62 case UNSAFE:
63 UnsafeRefCnt.init(this);
64 break;
65 case VAR_HANDLE:
66 VarHandleRefCnt.init(this);
67 break;
68 case ATOMIC_UPDATER:
69 default:
70 AtomicRefCnt.init(this);
71 break;
72 }
73 }
74
75
76
77
78
79
80
81 public static int refCnt(RefCnt ref) {
82 switch (REF_CNT_IMPL) {
83 case UNSAFE:
84 return UnsafeRefCnt.refCnt(ref);
85 case VAR_HANDLE:
86 return VarHandleRefCnt.refCnt(ref);
87 case ATOMIC_UPDATER:
88 default:
89 return AtomicRefCnt.refCnt(ref);
90 }
91 }
92
93
94
95
96
97
98 public static void retain(RefCnt ref) {
99 switch (REF_CNT_IMPL) {
100 case UNSAFE:
101 UnsafeRefCnt.retain(ref);
102 break;
103 case VAR_HANDLE:
104 VarHandleRefCnt.retain(ref);
105 break;
106 case ATOMIC_UPDATER:
107 default:
108 AtomicRefCnt.retain(ref);
109 break;
110 }
111 }
112
113
114
115
116
117
118
119
120 public static void retain(RefCnt ref, int increment) {
121 switch (REF_CNT_IMPL) {
122 case UNSAFE:
123 UnsafeRefCnt.retain(ref, increment);
124 break;
125 case VAR_HANDLE:
126 VarHandleRefCnt.retain(ref, increment);
127 break;
128 case ATOMIC_UPDATER:
129 default:
130 AtomicRefCnt.retain(ref, increment);
131 break;
132 }
133 }
134
135
136
137
138
139
140
141 public static boolean release(RefCnt ref) {
142 switch (REF_CNT_IMPL) {
143 case UNSAFE:
144 return UnsafeRefCnt.release(ref);
145 case VAR_HANDLE:
146 return VarHandleRefCnt.release(ref);
147 case ATOMIC_UPDATER:
148 default:
149 return AtomicRefCnt.release(ref);
150 }
151 }
152
153
154
155
156
157
158
159
160
161 public static boolean release(RefCnt ref, int decrement) {
162 switch (REF_CNT_IMPL) {
163 case UNSAFE:
164 return UnsafeRefCnt.release(ref, decrement);
165 case VAR_HANDLE:
166 return VarHandleRefCnt.release(ref, decrement);
167 case ATOMIC_UPDATER:
168 default:
169 return AtomicRefCnt.release(ref, decrement);
170 }
171 }
172
173
174
175
176
177
178
179
180 public static boolean isLiveNonVolatile(RefCnt ref) {
181 switch (REF_CNT_IMPL) {
182 case UNSAFE:
183 return UnsafeRefCnt.isLiveNonVolatile(ref);
184 case VAR_HANDLE:
185 return VarHandleRefCnt.isLiveNonVolatile(ref);
186 case ATOMIC_UPDATER:
187 default:
188 return AtomicRefCnt.isLiveNonVolatile(ref);
189 }
190 }
191
192
193
194
195
196
197
198
199 public static void setRefCnt(RefCnt ref, int refCnt) {
200 switch (REF_CNT_IMPL) {
201 case UNSAFE:
202 UnsafeRefCnt.setRefCnt(ref, refCnt);
203 break;
204 case VAR_HANDLE:
205 VarHandleRefCnt.setRefCnt(ref, refCnt);
206 break;
207 case ATOMIC_UPDATER:
208 default:
209 AtomicRefCnt.setRefCnt(ref, refCnt);
210 break;
211 }
212 }
213
214
215
216
217
218
219
220
221
222
223 public static void resetRefCnt(RefCnt ref) {
224 switch (REF_CNT_IMPL) {
225 case UNSAFE:
226 UnsafeRefCnt.resetRefCnt(ref);
227 break;
228 case VAR_HANDLE:
229 VarHandleRefCnt.resetRefCnt(ref);
230 break;
231 case ATOMIC_UPDATER:
232 default:
233 AtomicRefCnt.resetRefCnt(ref);
234 break;
235 }
236 }
237
238 static void throwIllegalRefCountOnRelease(int decrement, int curr) {
239 throw new IllegalReferenceCountException(curr >>> 1, -(decrement >>> 1));
240 }
241
242 private static final class AtomicRefCnt {
243 private static final AtomicIntegerFieldUpdater<RefCnt> UPDATER =
244 AtomicIntegerFieldUpdater.newUpdater(RefCnt.class, "value");
245
246 static void init(RefCnt instance) {
247 UPDATER.set(instance, 2);
248 }
249
250 static int refCnt(RefCnt instance) {
251 return UPDATER.get(instance) >>> 1;
252 }
253
254 static void retain(RefCnt instance) {
255 retain0(instance, 2);
256 }
257
258 static void retain(RefCnt instance, int increment) {
259 retain0(instance, checkPositive(increment, "increment") << 1);
260 }
261
262 private static void retain0(RefCnt instance, int increment) {
263
264
265
266 int oldRef = UPDATER.getAndAdd(instance, increment);
267 if ((oldRef & 0x80000001) != 0 || oldRef > Integer.MAX_VALUE - increment) {
268 UPDATER.getAndAdd(instance, -increment);
269 throw new IllegalReferenceCountException(0, increment >>> 1);
270 }
271 }
272
273 static boolean release(RefCnt instance) {
274 return release0(instance, 2);
275 }
276
277 static boolean release(RefCnt instance, int decrement) {
278 return release0(instance, checkPositive(decrement, "decrement") << 1);
279 }
280
281 private static boolean release0(RefCnt instance, int decrement) {
282 int curr, next;
283 do {
284 curr = instance.value;
285 if (curr == decrement) {
286 next = 1;
287 } else {
288 if (curr < decrement || (curr & 1) == 1) {
289 throwIllegalRefCountOnRelease(decrement, curr);
290 }
291 next = curr - decrement;
292 }
293 } while (!UPDATER.compareAndSet(instance, curr, next));
294 return (next & 1) == 1;
295 }
296
297 static void setRefCnt(RefCnt instance, int refCnt) {
298 int rawRefCnt = refCnt > 0? refCnt << 1 : 1;
299 UPDATER.lazySet(instance, rawRefCnt);
300 }
301
302 static void resetRefCnt(RefCnt instance) {
303 UPDATER.lazySet(instance, 2);
304 }
305
306 static boolean isLiveNonVolatile(RefCnt instance) {
307 final int rawCnt = instance.value;
308 if (rawCnt == 2) {
309 return true;
310 }
311 return (rawCnt & 1) == 0;
312 }
313 }
314
315 private static final class VarHandleRefCnt {
316
317 private static final VarHandle VH;
318
319 static {
320 VH = PlatformDependent.findVarHandleOfIntField(MethodHandles.lookup(), RefCnt.class, "value");
321 }
322
323 static void init(RefCnt instance) {
324 VH.set(instance, 2);
325 VarHandle.storeStoreFence();
326 }
327
328 static int refCnt(RefCnt instance) {
329 return (int) VH.getAcquire(instance) >>> 1;
330 }
331
332 static void retain(RefCnt instance) {
333 retain0(instance, 2);
334 }
335
336 static void retain(RefCnt instance, int increment) {
337 retain0(instance, checkPositive(increment, "increment") << 1);
338 }
339
340 private static void retain0(RefCnt instance, int increment) {
341
342
343
344 int oldRef = (int) VH.getAndAdd(instance, increment);
345 if ((oldRef & 0x80000001) != 0 || oldRef > Integer.MAX_VALUE - increment) {
346 VH.getAndAdd(instance, -increment);
347 throw new IllegalReferenceCountException(0, increment >>> 1);
348 }
349 }
350
351 static boolean release(RefCnt instance) {
352 return release0(instance, 2);
353 }
354
355 static boolean release(RefCnt instance, int decrement) {
356 return release0(instance, checkPositive(decrement, "decrement") << 1);
357 }
358
359 private static boolean release0(RefCnt instance, int decrement) {
360 int curr, next;
361 do {
362 curr = (int) VH.get(instance);
363 if (curr == decrement) {
364 next = 1;
365 } else {
366 if (curr < decrement || (curr & 1) == 1) {
367 throwIllegalRefCountOnRelease(decrement, curr);
368 }
369 next = curr - decrement;
370 }
371 } while (!(boolean) VH.compareAndSet(instance, curr, next));
372 return (next & 1) == 1;
373 }
374
375 static void setRefCnt(RefCnt instance, int refCnt) {
376 int rawRefCnt = refCnt > 0? refCnt << 1 : 1;
377 VH.setRelease(instance, rawRefCnt);
378 }
379
380 static void resetRefCnt(RefCnt instance) {
381 VH.setRelease(instance, 2);
382 }
383
384 static boolean isLiveNonVolatile(RefCnt instance) {
385 final int rawCnt = (int) VH.get(instance);
386 if (rawCnt == 2) {
387 return true;
388 }
389 return (rawCnt & 1) == 0;
390 }
391 }
392
393 private static final class UnsafeRefCnt {
394
395 private static final long VALUE_OFFSET = getUnsafeOffset(RefCnt.class, "value");
396
397 private static long getUnsafeOffset(Class<?> clz, String fieldName) {
398 try {
399 if (PlatformDependent.hasUnsafe()) {
400 return PlatformDependent.objectFieldOffset(clz.getDeclaredField(fieldName));
401 }
402 } catch (Throwable ignore) {
403
404 }
405 return -1;
406 }
407
408 static void init(RefCnt instance) {
409 PlatformDependent.safeConstructPutInt(instance, VALUE_OFFSET, 2);
410 }
411
412 static int refCnt(RefCnt instance) {
413 return PlatformDependent.getVolatileInt(instance, VALUE_OFFSET) >>> 1;
414 }
415
416 static void retain(RefCnt instance) {
417 retain0(instance, 2);
418 }
419
420 static void retain(RefCnt instance, int increment) {
421 retain0(instance, checkPositive(increment, "increment") << 1);
422 }
423
424 private static void retain0(RefCnt instance, int increment) {
425
426
427
428 int oldRef = PlatformDependent.getAndAddInt(instance, VALUE_OFFSET, increment);
429 if ((oldRef & 0x80000001) != 0 || oldRef > Integer.MAX_VALUE - increment) {
430 PlatformDependent.getAndAddInt(instance, VALUE_OFFSET, -increment);
431 throw new IllegalReferenceCountException(0, increment >>> 1);
432 }
433 }
434
435 static boolean release(RefCnt instance) {
436 return release0(instance, 2);
437 }
438
439 static boolean release(RefCnt instance, int decrement) {
440 return release0(instance, checkPositive(decrement, "decrement") << 1);
441 }
442
443 private static boolean release0(RefCnt instance, int decrement) {
444 int curr, next;
445 do {
446 curr = PlatformDependent.getInt(instance, VALUE_OFFSET);
447 if (curr == decrement) {
448 next = 1;
449 } else {
450 if (curr < decrement || (curr & 1) == 1) {
451 throwIllegalRefCountOnRelease(decrement, curr);
452 }
453 next = curr - decrement;
454 }
455 } while (!PlatformDependent.compareAndSwapInt(instance, VALUE_OFFSET, curr, next));
456 return (next & 1) == 1;
457 }
458
459 static void setRefCnt(RefCnt instance, int refCnt) {
460 int rawRefCnt = refCnt > 0? refCnt << 1 : 1;
461 PlatformDependent.putOrderedInt(instance, VALUE_OFFSET, rawRefCnt);
462 }
463
464 static void resetRefCnt(RefCnt instance) {
465 PlatformDependent.putOrderedInt(instance, VALUE_OFFSET, 2);
466 }
467
468 static boolean isLiveNonVolatile(RefCnt instance) {
469 final int rawCnt = PlatformDependent.getInt(instance, VALUE_OFFSET);
470 if (rawCnt == 2) {
471 return true;
472 }
473 return (rawCnt & 1) == 0;
474 }
475 }
476 }