1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  package io.netty5.util.concurrent;
18  
19  import java.util.Arrays;
20  import java.util.BitSet;
21  import java.util.concurrent.atomic.AtomicInteger;
22  
23  
24  
25  
26  
27  
28  final class InternalThreadLocalMap {
29  
30      private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap =
31              new ThreadLocal<>();
32      private static final AtomicInteger nextIndex = new AtomicInteger();
33  
34      private static final int ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD = 1 << 30;
35      
36      private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
37      private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;
38  
39      static final Object UNSET = new Object();
40  
41      
42      private Object[] indexedVariables;
43  
44      private BitSet cleanerFlags;
45  
46      static InternalThreadLocalMap getIfSet() {
47          Thread thread = Thread.currentThread();
48          if (thread instanceof FastThreadLocalThread) {
49              return ((FastThreadLocalThread) thread).threadLocalMap();
50          }
51          return slowThreadLocalMap.get();
52      }
53  
54      static InternalThreadLocalMap get() {
55          Thread thread = Thread.currentThread();
56          if (thread instanceof FastThreadLocalThread) {
57              return fastGet((FastThreadLocalThread) thread);
58          } else {
59              return slowGet();
60          }
61      }
62  
63      private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
64          InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
65          if (threadLocalMap == null) {
66              thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
67          }
68          return threadLocalMap;
69      }
70  
71      private static InternalThreadLocalMap slowGet() {
72          InternalThreadLocalMap ret = slowThreadLocalMap.get();
73          if (ret == null) {
74              ret = new InternalThreadLocalMap();
75              slowThreadLocalMap.set(ret);
76          }
77          return ret;
78      }
79  
80      static void remove() {
81          Thread thread = Thread.currentThread();
82          if (thread instanceof FastThreadLocalThread) {
83              ((FastThreadLocalThread) thread).setThreadLocalMap(null);
84          } else {
85              slowThreadLocalMap.remove();
86          }
87      }
88  
89      static void destroy() {
90          slowThreadLocalMap.remove();
91      }
92  
93      static int nextVariableIndex() {
94          int index = nextIndex.getAndIncrement();
95          if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {
96              nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);
97              throw new IllegalStateException("too many thread-local indexed variables");
98          }
99          return index;
100     }
101 
102     static int lastVariableIndex() {
103         return nextIndex.get() - 1;
104     }
105 
106     private InternalThreadLocalMap() {
107         indexedVariables = newIndexedVariableTable();
108     }
109 
110     private static Object[] newIndexedVariableTable() {
111         Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];
112         Arrays.fill(array, UNSET);
113         return array;
114     }
115 
116     int size() {
117         int count = 0;
118         for (Object o: indexedVariables) {
119             if (o != UNSET) {
120                 count ++;
121             }
122         }
123 
124         
125         
126         return count - 1;
127     }
128 
129     Object indexedVariable(int index) {
130         Object[] lookup = indexedVariables;
131         return index < lookup.length? lookup[index] : UNSET;
132     }
133 
134     
135 
136 
137     boolean setIndexedVariable(int index, Object value) {
138         Object[] lookup = indexedVariables;
139         if (index < lookup.length) {
140             Object oldValue = lookup[index];
141             lookup[index] = value;
142             return oldValue == UNSET;
143         } else {
144             expandIndexedVariableTableAndSet(index, value);
145             return true;
146         }
147     }
148 
149     private void expandIndexedVariableTableAndSet(int index, Object value) {
150         Object[] oldArray = indexedVariables;
151         final int oldCapacity = oldArray.length;
152         int newCapacity;
153         if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
154             newCapacity = index;
155             newCapacity |= newCapacity >>>  1;
156             newCapacity |= newCapacity >>>  2;
157             newCapacity |= newCapacity >>>  4;
158             newCapacity |= newCapacity >>>  8;
159             newCapacity |= newCapacity >>> 16;
160             newCapacity ++;
161         } else {
162             newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
163         }
164 
165         Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
166         Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
167         newArray[index] = value;
168         indexedVariables = newArray;
169     }
170 
171     Object removeIndexedVariable(int index) {
172         Object[] lookup = indexedVariables;
173         if (index < lookup.length) {
174             Object v = lookup[index];
175             lookup[index] = UNSET;
176             return v;
177         } else {
178             return UNSET;
179         }
180     }
181 
182     boolean isIndexedVariableSet(int index) {
183         Object[] lookup = indexedVariables;
184         return index < lookup.length && lookup[index] != UNSET;
185     }
186 
187     boolean isCleanerFlagSet(int index) {
188         return cleanerFlags != null && cleanerFlags.get(index);
189     }
190 
191     void setCleanerFlag(int index) {
192         if (cleanerFlags == null) {
193             cleanerFlags = new BitSet();
194         }
195         cleanerFlags.set(index);
196     }
197 }