Created
October 28, 2025 15:54
-
-
Save franz1981/e81b474c415b58e2601407fa254dfc00 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright 2025 The Netty Project | |
| * | |
| * The Netty Project licenses this file to you under the Apache License, | |
| * version 2.0 (the "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at: | |
| * | |
| * https://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| * License for the specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| package io.netty.util.internal; | |
| import io.netty.util.IllegalReferenceCountException; | |
| import java.lang.invoke.MethodHandles; | |
| import java.lang.invoke.VarHandle; | |
| import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; | |
| import static io.netty.util.internal.ObjectUtil.checkPositive; | |
| /** | |
| * Concrete class for reference counting implementations. | |
| * Provides a factory method to create instances using the most efficient available atomic updater. | |
| */ | |
| public class RefCnt { | |
| private static final int UNSAFE = 0; | |
| private static final int VAR_HANDLE = 1; | |
| private static final int ATOMIC_UPDATER = 2; | |
| private static final int REF_CNT_IMPL; | |
| static { | |
| if (PlatformDependent.hasUnsafe()) { | |
| REF_CNT_IMPL = UNSAFE; | |
| } else if (PlatformDependent.hasVarHandle()) { | |
| REF_CNT_IMPL = VAR_HANDLE; | |
| } else { | |
| REF_CNT_IMPL = ATOMIC_UPDATER; | |
| } | |
| } | |
| /* | |
| * Implementation notes: | |
| * | |
| * For the updated int field: | |
| * Even => "real" refcount is (refCnt >>> 1) | |
| * Odd => "real" refcount is 0 | |
| */ | |
| private volatile int value; | |
| private RefCnt() { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| UnsafeRefCnt.init(this); | |
| break; | |
| case VAR_HANDLE: | |
| VarHandleRefCnt.init(this); | |
| break; | |
| case ATOMIC_UPDATER: | |
| default: | |
| AtomicRefCnt.init(this); | |
| break; | |
| } | |
| } | |
| /** | |
| * Creates a new reference counting instance using the best available updater. | |
| * The implementation is chosen based on platform capabilities: Unsafe, VarHandle, or Atomic. | |
| * | |
| * @return a new RefCnt instance | |
| */ | |
| public static RefCnt create() { | |
| return new RefCnt(); | |
| } | |
| private static long getUnsafeOffset(Class<?> clz, String fieldName) { | |
| try { | |
| if (PlatformDependent.hasUnsafe()) { | |
| return PlatformDependent.objectFieldOffset(clz.getDeclaredField(fieldName)); | |
| } | |
| } catch (Throwable ignore) { | |
| // fall-back | |
| } | |
| return -1; | |
| } | |
| /** | |
| * Returns the current reference count of this object with a load acquire semantic. | |
| * | |
| * @return the reference count | |
| */ | |
| public final int refCnt() { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| return UnsafeRefCnt.refCnt(this); | |
| case VAR_HANDLE: | |
| return VarHandleRefCnt.refCnt(this); | |
| case ATOMIC_UPDATER: | |
| default: | |
| return AtomicRefCnt.refCnt(this); | |
| } | |
| } | |
| /** | |
| * Increases the reference count by 1. | |
| */ | |
| public final void retain() { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| UnsafeRefCnt.retain(this); | |
| break; | |
| case VAR_HANDLE: | |
| VarHandleRefCnt.retain(this); | |
| break; | |
| case ATOMIC_UPDATER: | |
| default: | |
| AtomicRefCnt.retain(this); | |
| break; | |
| } | |
| } | |
| /** | |
| * Increases the reference count by the specified increment. | |
| * | |
| * @param increment the amount to increase the reference count by | |
| * @throws IllegalArgumentException if increment is not positive | |
| */ | |
| public final void retain(int increment) { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| UnsafeRefCnt.retain(this, increment); | |
| break; | |
| case VAR_HANDLE: | |
| VarHandleRefCnt.retain(this, increment); | |
| break; | |
| case ATOMIC_UPDATER: | |
| default: | |
| AtomicRefCnt.retain(this, increment); | |
| break; | |
| } | |
| } | |
| /** | |
| * Decreases the reference count by 1. | |
| * | |
| * @return true if the reference count became 0 and the object should be deallocated | |
| */ | |
| public final boolean release() { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| return UnsafeRefCnt.release(this); | |
| case VAR_HANDLE: | |
| return VarHandleRefCnt.release(this); | |
| case ATOMIC_UPDATER: | |
| default: | |
| return AtomicRefCnt.release(this); | |
| } | |
| } | |
| /** | |
| * Decreases the reference count by the specified decrement. | |
| * | |
| * @param decrement the amount to decrease the reference count by | |
| * @return true if the reference count became 0 and the object should be deallocated | |
| * @throws IllegalArgumentException if decrement is not positive | |
| */ | |
| public final boolean release(int decrement) { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| return UnsafeRefCnt.release(this, decrement); | |
| case VAR_HANDLE: | |
| return VarHandleRefCnt.release(this, decrement); | |
| case ATOMIC_UPDATER: | |
| default: | |
| return AtomicRefCnt.release(this, decrement); | |
| } | |
| } | |
| /** | |
| * Returns {@code true} if and only if this reference counter is alive. | |
| * This method is useful to check if the object is alive without incurring the cost of a volatile read. | |
| */ | |
| public final boolean isLiveNonVolatile() { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| return UnsafeRefCnt.isLiveNonVolatile(this); | |
| case VAR_HANDLE: | |
| return VarHandleRefCnt.isLiveNonVolatile(this); | |
| case ATOMIC_UPDATER: | |
| default: | |
| return AtomicRefCnt.isLiveNonVolatile(this); | |
| } | |
| } | |
| /** | |
| * <strong>WARNING:</strong> | |
| * An unsafe operation that sets the reference count directly. | |
| */ | |
| public final void setRefCnt(int refCnt) { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| UnsafeRefCnt.setRefCnt(this, refCnt); | |
| break; | |
| case VAR_HANDLE: | |
| VarHandleRefCnt.setRefCnt(this, refCnt); | |
| break; | |
| case ATOMIC_UPDATER: | |
| default: | |
| AtomicRefCnt.setRefCnt(this, refCnt); | |
| break; | |
| } | |
| } | |
| /** | |
| * Resets the reference count to 1. | |
| * <p> | |
| * <strong>Warning:</strong> This method uses release memory semantics, meaning the change may not be | |
| * immediately visible to other threads. It should only be used in quiescent states where no other | |
| * threads are accessing the reference count. | |
| */ | |
| public final void resetRefCnt() { | |
| switch (REF_CNT_IMPL) { | |
| case UNSAFE: | |
| UnsafeRefCnt.resetRefCnt(this); | |
| break; | |
| case VAR_HANDLE: | |
| VarHandleRefCnt.resetRefCnt(this); | |
| break; | |
| case ATOMIC_UPDATER: | |
| default: | |
| AtomicRefCnt.resetRefCnt(this); | |
| break; | |
| } | |
| } | |
| private static final class AtomicRefCnt { | |
| private static final AtomicIntegerFieldUpdater<RefCnt> UPDATER = | |
| AtomicIntegerFieldUpdater.newUpdater(RefCnt.class, "value"); | |
| static void init(RefCnt instance) { | |
| UPDATER.set(instance, 2); | |
| } | |
| static int refCnt(RefCnt instance) { | |
| return UPDATER.get(instance) >>> 1; | |
| } | |
| static void retain(RefCnt instance) { | |
| retain0(instance, 2); | |
| } | |
| static void retain(RefCnt instance, int increment) { | |
| retain0(instance, checkPositive(increment, "increment") << 1); | |
| } | |
| private static void retain0(RefCnt instance, int increment) { | |
| int oldRef = UPDATER.getAndAdd(instance, increment); | |
| if ((oldRef & 0x80000001) != 0 || oldRef > Integer.MAX_VALUE - increment) { | |
| UPDATER.getAndAdd(instance, -increment); | |
| throw new IllegalReferenceCountException(0, increment >>> 1); | |
| } | |
| } | |
| static boolean release(RefCnt instance) { | |
| return release0(instance, 2); | |
| } | |
| static boolean release(RefCnt instance, int decrement) { | |
| return release0(instance, checkPositive(decrement, "decrement") << 1); | |
| } | |
| private static boolean release0(RefCnt instance, int decrement) { | |
| int curr, next; | |
| do { | |
| curr = UPDATER.get(instance); | |
| if (curr == decrement) { | |
| next = 1; | |
| } else { | |
| if (curr < decrement || (curr & 1) == 1) { | |
| throw new IllegalReferenceCountException(curr >>> 1, -(decrement >>> 1)); | |
| } | |
| next = curr - decrement; | |
| } | |
| } while (!UPDATER.compareAndSet(instance, curr, next)); | |
| return (next & 1) == 1; | |
| } | |
| static void setRefCnt(RefCnt instance, int refCnt) { | |
| int rawRefCnt = refCnt > 0 ? refCnt << 1 : 1; | |
| UPDATER.lazySet(instance, rawRefCnt); | |
| } | |
| static void resetRefCnt(RefCnt instance) { | |
| UPDATER.lazySet(instance, 2); | |
| } | |
| static boolean isLiveNonVolatile(RefCnt instance) { | |
| final int rawCnt = instance.value; | |
| if (rawCnt == 2) { | |
| return true; | |
| } | |
| return (rawCnt & 1) == 0; | |
| } | |
| } | |
| private static final class VarHandleRefCnt { | |
| private static final VarHandle VH; | |
| static { | |
| VH = PlatformDependent.findVarHandleOfIntField(MethodHandles.lookup(), RefCnt.class, "value"); | |
| } | |
| static void init(RefCnt instance) { | |
| VH.set(instance, 2); | |
| VarHandle.storeStoreFence(); | |
| } | |
| static int refCnt(RefCnt instance) { | |
| return (int) VH.getAcquire(instance) >>> 1; | |
| } | |
| static void retain(RefCnt instance) { | |
| retain0(instance, 2); | |
| } | |
| static void retain(RefCnt instance, int increment) { | |
| retain0(instance, checkPositive(increment, "increment") << 1); | |
| } | |
| private static void retain0(RefCnt instance, int increment) { | |
| int oldRef = (int) VH.getAndAdd(instance, increment); | |
| if ((oldRef & 0x80000001) != 0 || oldRef > Integer.MAX_VALUE - increment) { | |
| VH.getAndAdd(instance, -increment); | |
| throw new IllegalReferenceCountException(0, increment >>> 1); | |
| } | |
| } | |
| static boolean release(RefCnt instance) { | |
| return release0(instance, 2); | |
| } | |
| static boolean release(RefCnt instance, int decrement) { | |
| return release0(instance, checkPositive(decrement, "decrement") << 1); | |
| } | |
| private static boolean release0(RefCnt instance, int decrement) { | |
| int curr, next; | |
| do { | |
| curr = (int) VH.get(instance); | |
| if (curr == decrement) { | |
| next = 1; | |
| } else { | |
| if (curr < decrement || (curr & 1) == 1) { | |
| throw new IllegalReferenceCountException(curr >>> 1, -(decrement >>> 1)); | |
| } | |
| next = curr - decrement; | |
| } | |
| } while (!(boolean) VH.compareAndSet(instance, curr, next)); | |
| return (next & 1) == 1; | |
| } | |
| static void setRefCnt(RefCnt instance, int refCnt) { | |
| int rawRefCnt = refCnt > 0 ? refCnt << 1 : 1; | |
| VH.setRelease(instance, rawRefCnt); | |
| } | |
| static void resetRefCnt(RefCnt instance) { | |
| VH.setRelease(instance, 2); | |
| } | |
| static boolean isLiveNonVolatile(RefCnt instance) { | |
| final int rawCnt = (int) VH.get(instance); | |
| if (rawCnt == 2) { | |
| return true; | |
| } | |
| return (rawCnt & 1) == 0; | |
| } | |
| } | |
| private static final class UnsafeRefCnt { | |
| private static final long VALUE_OFFSET = getUnsafeOffset(RefCnt.class, "value"); | |
| static void init(RefCnt instance) { | |
| PlatformDependent.safeConstructPutInt(instance, VALUE_OFFSET, 2); | |
| } | |
| static int refCnt(RefCnt instance) { | |
| return PlatformDependent.getVolatileInt(instance, VALUE_OFFSET) >>> 1; | |
| } | |
| static void retain(RefCnt instance) { | |
| retain0(instance, 2); | |
| } | |
| static void retain(RefCnt instance, int increment) { | |
| retain0(instance, checkPositive(increment, "increment") << 1); | |
| } | |
| private static void retain0(RefCnt instance, int increment) { | |
| int oldRef = PlatformDependent.getAndAddInt(instance, VALUE_OFFSET, increment); | |
| if ((oldRef & 0x80000001) != 0 || oldRef > Integer.MAX_VALUE - increment) { | |
| PlatformDependent.getAndAddInt(instance, VALUE_OFFSET, -increment); | |
| throw new IllegalReferenceCountException(0, increment >>> 1); | |
| } | |
| } | |
| static boolean release(RefCnt instance) { | |
| return release0(instance, 2); | |
| } | |
| static boolean release(RefCnt instance, int decrement) { | |
| return release0(instance, checkPositive(decrement, "decrement") << 1); | |
| } | |
| private static boolean release0(RefCnt instance, int decrement) { | |
| int curr, next; | |
| do { | |
| curr = PlatformDependent.getInt(instance, VALUE_OFFSET); | |
| if (curr == decrement) { | |
| next = 1; | |
| } else { | |
| if (curr < decrement || (curr & 1) == 1) { | |
| throw new IllegalReferenceCountException(curr >>> 1, -(decrement >>> 1)); | |
| } | |
| next = curr - decrement; | |
| } | |
| } while (!PlatformDependent.compareAndSwapInt(instance, VALUE_OFFSET, curr, next)); | |
| return (next & 1) == 1; | |
| } | |
| static void setRefCnt(RefCnt instance, int refCnt) { | |
| int rawRefCnt = refCnt > 0 ? refCnt << 1 : 1; | |
| PlatformDependent.putOrderedInt(instance, VALUE_OFFSET, rawRefCnt); | |
| } | |
| static void resetRefCnt(RefCnt instance) { | |
| PlatformDependent.putOrderedInt(instance, VALUE_OFFSET, 2); | |
| } | |
| static boolean isLiveNonVolatile(RefCnt instance) { | |
| final int rawCnt = PlatformDependent.getInt(instance, VALUE_OFFSET); | |
| if (rawCnt == 2) { | |
| return true; | |
| } | |
| return (rawCnt & 1) == 0; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment