| /* |
| * Copyright 2014 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: |
| * |
| * http://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 com.alibaba.dubbo.common.threadlocal; |
| |
| import java.util.Arrays; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| /** |
| * The internal data structure that stores the threadLocal variables for Netty and all {@link InternalThread}s. |
| * Note that this class is for internal use only. Use {@link InternalThread} |
| * unless you know what you are doing. |
| */ |
| public final class InternalThreadLocalMap { |
| |
| private Object[] indexedVariables; |
| |
| private static ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>(); |
| |
| private static final AtomicInteger nextIndex = new AtomicInteger(); |
| |
| public static final Object UNSET = new Object(); |
| |
| public static InternalThreadLocalMap getIfSet() { |
| Thread thread = Thread.currentThread(); |
| if (thread instanceof InternalThread) { |
| return ((InternalThread) thread).threadLocalMap(); |
| } |
| return slowThreadLocalMap.get(); |
| } |
| |
| public static InternalThreadLocalMap get() { |
| Thread thread = Thread.currentThread(); |
| if (thread instanceof InternalThread) { |
| return fastGet((InternalThread) thread); |
| } |
| return slowGet(); |
| } |
| |
| public static void remove() { |
| Thread thread = Thread.currentThread(); |
| if (thread instanceof InternalThread) { |
| ((InternalThread) thread).setThreadLocalMap(null); |
| } else { |
| slowThreadLocalMap.remove(); |
| } |
| } |
| |
| public static void destroy() { |
| slowThreadLocalMap = null; |
| } |
| |
| public static int nextVariableIndex() { |
| int index = nextIndex.getAndIncrement(); |
| if (index < 0) { |
| nextIndex.decrementAndGet(); |
| throw new IllegalStateException("Too many thread-local indexed variables"); |
| } |
| return index; |
| } |
| |
| public static int lastVariableIndex() { |
| return nextIndex.get() - 1; |
| } |
| |
| private InternalThreadLocalMap() { |
| indexedVariables = newIndexedVariableTable(); |
| } |
| |
| public Object indexedVariable(int index) { |
| Object[] lookup = indexedVariables; |
| return index < lookup.length ? lookup[index] : UNSET; |
| } |
| |
| /** |
| * @return {@code true} if and only if a new thread-local variable has been created |
| */ |
| public boolean setIndexedVariable(int index, Object value) { |
| Object[] lookup = indexedVariables; |
| if (index < lookup.length) { |
| Object oldValue = lookup[index]; |
| lookup[index] = value; |
| return oldValue == UNSET; |
| } else { |
| expandIndexedVariableTableAndSet(index, value); |
| return true; |
| } |
| } |
| |
| public Object removeIndexedVariable(int index) { |
| Object[] lookup = indexedVariables; |
| if (index < lookup.length) { |
| Object v = lookup[index]; |
| lookup[index] = UNSET; |
| return v; |
| } else { |
| return UNSET; |
| } |
| } |
| |
| public int size() { |
| int count = 0; |
| for (Object o : indexedVariables) { |
| if (o != UNSET) { |
| ++count; |
| } |
| } |
| |
| //the fist element in `indexedVariables` is a set to keep all the InternalThreadLocal to remove |
| //look at method `addToVariablesToRemove` |
| return count - 1; |
| } |
| |
| private static Object[] newIndexedVariableTable() { |
| Object[] array = new Object[32]; |
| Arrays.fill(array, UNSET); |
| return array; |
| } |
| |
| private static InternalThreadLocalMap fastGet(InternalThread thread) { |
| InternalThreadLocalMap threadLocalMap = thread.threadLocalMap(); |
| if (threadLocalMap == null) { |
| thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap()); |
| } |
| return threadLocalMap; |
| } |
| |
| private static InternalThreadLocalMap slowGet() { |
| ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = InternalThreadLocalMap.slowThreadLocalMap; |
| InternalThreadLocalMap ret = slowThreadLocalMap.get(); |
| if (ret == null) { |
| ret = new InternalThreadLocalMap(); |
| slowThreadLocalMap.set(ret); |
| } |
| return ret; |
| } |
| |
| private void expandIndexedVariableTableAndSet(int index, Object value) { |
| Object[] oldArray = indexedVariables; |
| final int oldCapacity = oldArray.length; |
| int newCapacity = index; |
| newCapacity |= newCapacity >>> 1; |
| newCapacity |= newCapacity >>> 2; |
| newCapacity |= newCapacity >>> 4; |
| newCapacity |= newCapacity >>> 8; |
| newCapacity |= newCapacity >>> 16; |
| newCapacity++; |
| |
| Object[] newArray = Arrays.copyOf(oldArray, newCapacity); |
| Arrays.fill(newArray, oldCapacity, newArray.length, UNSET); |
| newArray[index] = value; |
| indexedVariables = newArray; |
| } |
| } |