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