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