1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.util.internal;
18
19 import io.netty.util.concurrent.FastThreadLocal;
20 import io.netty.util.concurrent.FastThreadLocalThread;
21 import io.netty.util.internal.logging.InternalLogger;
22 import io.netty.util.internal.logging.InternalLoggerFactory;
23
24 import java.nio.charset.Charset;
25 import java.nio.charset.CharsetDecoder;
26 import java.nio.charset.CharsetEncoder;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.BitSet;
30 import java.util.IdentityHashMap;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.WeakHashMap;
34 import java.util.concurrent.atomic.AtomicInteger;
35
36
37
38
39
40
41 public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
42 private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap =
43 new ThreadLocal<InternalThreadLocalMap>();
44 private static final AtomicInteger nextIndex = new AtomicInteger();
45
46 public static final int VARIABLES_TO_REMOVE_INDEX = nextVariableIndex();
47
48 private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
49 private static final int ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD = 1 << 30;
50
51 private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
52
53 private static final int HANDLER_SHARABLE_CACHE_INITIAL_CAPACITY = 4;
54 private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;
55
56 private static final int STRING_BUILDER_INITIAL_SIZE;
57 private static final int STRING_BUILDER_MAX_SIZE;
58
59 private static final InternalLogger logger;
60
61 public static final Object UNSET = new Object();
62
63
64 private Object[] indexedVariables;
65
66
67 private int futureListenerStackDepth;
68 private int localChannelReaderStackDepth;
69 private Map<Class<?>, Boolean> handlerSharableCache;
70 private IntegerHolder counterHashCode;
71 private ThreadLocalRandom random;
72 private Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache;
73 private Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache;
74
75
76 private StringBuilder stringBuilder;
77 private Map<Charset, CharsetEncoder> charsetEncoderCache;
78 private Map<Charset, CharsetDecoder> charsetDecoderCache;
79
80
81 private ArrayList<Object> arrayList;
82
83 private BitSet cleanerFlags;
84
85
86 public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8;
87
88 static {
89 STRING_BUILDER_INITIAL_SIZE =
90 SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024);
91 STRING_BUILDER_MAX_SIZE =
92 SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.maxSize", 1024 * 4);
93
94
95
96
97
98
99 logger = InternalLoggerFactory.getInstance(InternalThreadLocalMap.class);
100 logger.debug("-Dio.netty.threadLocalMap.stringBuilder.initialSize: {}", STRING_BUILDER_INITIAL_SIZE);
101 logger.debug("-Dio.netty.threadLocalMap.stringBuilder.maxSize: {}", STRING_BUILDER_MAX_SIZE);
102 }
103
104 public static InternalThreadLocalMap getIfSet() {
105 Thread thread = Thread.currentThread();
106 if (thread instanceof FastThreadLocalThread) {
107 return ((FastThreadLocalThread) thread).threadLocalMap();
108 }
109 return slowThreadLocalMap.get();
110 }
111
112 public static InternalThreadLocalMap get() {
113 Thread thread = Thread.currentThread();
114 if (thread instanceof FastThreadLocalThread) {
115 return fastGet((FastThreadLocalThread) thread);
116 } else {
117 return slowGet();
118 }
119 }
120
121 private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
122 InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
123 if (threadLocalMap == null) {
124 thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
125 }
126 return threadLocalMap;
127 }
128
129 private static InternalThreadLocalMap slowGet() {
130 InternalThreadLocalMap ret = slowThreadLocalMap.get();
131 if (ret == null) {
132 ret = new InternalThreadLocalMap();
133 slowThreadLocalMap.set(ret);
134 }
135 return ret;
136 }
137
138 public static void remove() {
139 Thread thread = Thread.currentThread();
140 if (thread instanceof FastThreadLocalThread) {
141 ((FastThreadLocalThread) thread).setThreadLocalMap(null);
142 } else {
143 slowThreadLocalMap.remove();
144 }
145 }
146
147 public static void destroy() {
148 slowThreadLocalMap.remove();
149 }
150
151 public static int nextVariableIndex() {
152 int index = nextIndex.getAndIncrement();
153 if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {
154 nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);
155 throw new IllegalStateException("too many thread-local indexed variables");
156 }
157 return index;
158 }
159
160 public static int lastVariableIndex() {
161 return nextIndex.get() - 1;
162 }
163
164 private InternalThreadLocalMap() {
165 indexedVariables = newIndexedVariableTable();
166 }
167
168 private static Object[] newIndexedVariableTable() {
169 Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];
170 Arrays.fill(array, UNSET);
171 return array;
172 }
173
174 public int size() {
175 int count = 0;
176
177 if (futureListenerStackDepth != 0) {
178 count ++;
179 }
180 if (localChannelReaderStackDepth != 0) {
181 count ++;
182 }
183 if (handlerSharableCache != null) {
184 count ++;
185 }
186 if (counterHashCode != null) {
187 count ++;
188 }
189 if (random != null) {
190 count ++;
191 }
192 if (typeParameterMatcherGetCache != null) {
193 count ++;
194 }
195 if (typeParameterMatcherFindCache != null) {
196 count ++;
197 }
198 if (stringBuilder != null) {
199 count ++;
200 }
201 if (charsetEncoderCache != null) {
202 count ++;
203 }
204 if (charsetDecoderCache != null) {
205 count ++;
206 }
207 if (arrayList != null) {
208 count ++;
209 }
210
211 Object v = indexedVariable(VARIABLES_TO_REMOVE_INDEX);
212 if (v != null && v != InternalThreadLocalMap.UNSET) {
213 @SuppressWarnings("unchecked")
214 Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
215 count += variablesToRemove.size();
216 }
217
218 return count;
219 }
220
221 public StringBuilder stringBuilder() {
222 StringBuilder sb = stringBuilder;
223 if (sb == null) {
224 return stringBuilder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE);
225 }
226 if (sb.capacity() > STRING_BUILDER_MAX_SIZE) {
227 sb.setLength(STRING_BUILDER_INITIAL_SIZE);
228 sb.trimToSize();
229 }
230 sb.setLength(0);
231 return sb;
232 }
233
234 public Map<Charset, CharsetEncoder> charsetEncoderCache() {
235 Map<Charset, CharsetEncoder> cache = charsetEncoderCache;
236 if (cache == null) {
237 charsetEncoderCache = cache = new IdentityHashMap<Charset, CharsetEncoder>();
238 }
239 return cache;
240 }
241
242 public Map<Charset, CharsetDecoder> charsetDecoderCache() {
243 Map<Charset, CharsetDecoder> cache = charsetDecoderCache;
244 if (cache == null) {
245 charsetDecoderCache = cache = new IdentityHashMap<Charset, CharsetDecoder>();
246 }
247 return cache;
248 }
249
250 public <E> ArrayList<E> arrayList() {
251 return arrayList(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY);
252 }
253
254 @SuppressWarnings("unchecked")
255 public <E> ArrayList<E> arrayList(int minCapacity) {
256 ArrayList<E> list = (ArrayList<E>) arrayList;
257 if (list == null) {
258 arrayList = new ArrayList<Object>(minCapacity);
259 return (ArrayList<E>) arrayList;
260 }
261 list.clear();
262 list.ensureCapacity(minCapacity);
263 return list;
264 }
265
266 public int futureListenerStackDepth() {
267 return futureListenerStackDepth;
268 }
269
270 public void setFutureListenerStackDepth(int futureListenerStackDepth) {
271 this.futureListenerStackDepth = futureListenerStackDepth;
272 }
273
274 public ThreadLocalRandom random() {
275 ThreadLocalRandom r = random;
276 if (r == null) {
277 random = r = new ThreadLocalRandom();
278 }
279 return r;
280 }
281
282 public Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache() {
283 Map<Class<?>, TypeParameterMatcher> cache = typeParameterMatcherGetCache;
284 if (cache == null) {
285 typeParameterMatcherGetCache = cache = new IdentityHashMap<Class<?>, TypeParameterMatcher>();
286 }
287 return cache;
288 }
289
290 public Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache() {
291 Map<Class<?>, Map<String, TypeParameterMatcher>> cache = typeParameterMatcherFindCache;
292 if (cache == null) {
293 typeParameterMatcherFindCache = cache = new IdentityHashMap<Class<?>, Map<String, TypeParameterMatcher>>();
294 }
295 return cache;
296 }
297
298 @Deprecated
299 public IntegerHolder counterHashCode() {
300 return counterHashCode;
301 }
302
303 @Deprecated
304 public void setCounterHashCode(IntegerHolder counterHashCode) {
305 this.counterHashCode = counterHashCode;
306 }
307
308 public Map<Class<?>, Boolean> handlerSharableCache() {
309 Map<Class<?>, Boolean> cache = handlerSharableCache;
310 if (cache == null) {
311
312 handlerSharableCache = cache = new WeakHashMap<Class<?>, Boolean>(HANDLER_SHARABLE_CACHE_INITIAL_CAPACITY);
313 }
314 return cache;
315 }
316
317 public int localChannelReaderStackDepth() {
318 return localChannelReaderStackDepth;
319 }
320
321 public void setLocalChannelReaderStackDepth(int localChannelReaderStackDepth) {
322 this.localChannelReaderStackDepth = localChannelReaderStackDepth;
323 }
324
325 public Object indexedVariable(int index) {
326 Object[] lookup = indexedVariables;
327 return index < lookup.length? lookup[index] : UNSET;
328 }
329
330
331
332
333 public boolean setIndexedVariable(int index, Object value) {
334 Object[] lookup = indexedVariables;
335 if (index < lookup.length) {
336 Object oldValue = lookup[index];
337 lookup[index] = value;
338 return oldValue == UNSET;
339 } else {
340 expandIndexedVariableTableAndSet(index, value);
341 return true;
342 }
343 }
344
345 private void expandIndexedVariableTableAndSet(int index, Object value) {
346 Object[] oldArray = indexedVariables;
347 final int oldCapacity = oldArray.length;
348 int newCapacity;
349 if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
350 newCapacity = index;
351 newCapacity |= newCapacity >>> 1;
352 newCapacity |= newCapacity >>> 2;
353 newCapacity |= newCapacity >>> 4;
354 newCapacity |= newCapacity >>> 8;
355 newCapacity |= newCapacity >>> 16;
356 newCapacity ++;
357 } else {
358 newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
359 }
360
361 Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
362 Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
363 newArray[index] = value;
364 indexedVariables = newArray;
365 }
366
367 public Object removeIndexedVariable(int index) {
368 Object[] lookup = indexedVariables;
369 if (index < lookup.length) {
370 Object v = lookup[index];
371 lookup[index] = UNSET;
372 return v;
373 } else {
374 return UNSET;
375 }
376 }
377
378 public boolean isIndexedVariableSet(int index) {
379 Object[] lookup = indexedVariables;
380 return index < lookup.length && lookup[index] != UNSET;
381 }
382
383 public boolean isCleanerFlagSet(int index) {
384 return cleanerFlags != null && cleanerFlags.get(index);
385 }
386
387 public void setCleanerFlag(int index) {
388 if (cleanerFlags == null) {
389 cleanerFlags = new BitSet();
390 }
391 cleanerFlags.set(index);
392 }
393 }