View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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.IdentityHashMap;
30  import java.util.Map;
31  import java.util.WeakHashMap;
32  
33  /**
34   * The internal data structure that stores the thread-local variables for Netty and all {@link FastThreadLocal}s.
35   * Note that this class is for internal use only and is subject to change at any time.  Use {@link FastThreadLocal}
36   * unless you know what you are doing.
37   */
38  public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
39  
40      private static final InternalLogger logger = InternalLoggerFactory.getInstance(InternalThreadLocalMap.class);
41  
42      private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
43      private static final int STRING_BUILDER_INITIAL_SIZE;
44      private static final int STRING_BUILDER_MAX_SIZE;
45  
46      public static final Object UNSET = new Object();
47  
48      static {
49          STRING_BUILDER_INITIAL_SIZE =
50                  SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024);
51          logger.debug("-Dio.netty.threadLocalMap.stringBuilder.initialSize: {}", STRING_BUILDER_INITIAL_SIZE);
52  
53          STRING_BUILDER_MAX_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.maxSize", 1024 * 4);
54          logger.debug("-Dio.netty.threadLocalMap.stringBuilder.maxSize: {}", STRING_BUILDER_MAX_SIZE);
55      }
56  
57      public static InternalThreadLocalMap getIfSet() {
58          Thread thread = Thread.currentThread();
59          if (thread instanceof FastThreadLocalThread) {
60              return ((FastThreadLocalThread) thread).threadLocalMap();
61          }
62          return slowThreadLocalMap.get();
63      }
64  
65      public static InternalThreadLocalMap get() {
66          Thread thread = Thread.currentThread();
67          if (thread instanceof FastThreadLocalThread) {
68              return fastGet((FastThreadLocalThread) thread);
69          } else {
70              return slowGet();
71          }
72      }
73  
74      private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
75          InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
76          if (threadLocalMap == null) {
77              thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
78          }
79          return threadLocalMap;
80      }
81  
82      private static InternalThreadLocalMap slowGet() {
83          ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
84          InternalThreadLocalMap ret = slowThreadLocalMap.get();
85          if (ret == null) {
86              ret = new InternalThreadLocalMap();
87              slowThreadLocalMap.set(ret);
88          }
89          return ret;
90      }
91  
92      public static void remove() {
93          Thread thread = Thread.currentThread();
94          if (thread instanceof FastThreadLocalThread) {
95              ((FastThreadLocalThread) thread).setThreadLocalMap(null);
96          } else {
97              slowThreadLocalMap.remove();
98          }
99      }
100 
101     public static void destroy() {
102         slowThreadLocalMap.remove();
103     }
104 
105     public static int nextVariableIndex() {
106         int index = nextIndex.getAndIncrement();
107         if (index < 0) {
108             nextIndex.decrementAndGet();
109             throw new IllegalStateException("too many thread-local indexed variables");
110         }
111         return index;
112     }
113 
114     public static int lastVariableIndex() {
115         return nextIndex.get() - 1;
116     }
117 
118     // Cache line padding (must be public)
119     // With CompressedOops enabled, an instance of this class should occupy at least 128 bytes.
120     public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9;
121 
122     private InternalThreadLocalMap() {
123         super(newIndexedVariableTable());
124     }
125 
126     private static Object[] newIndexedVariableTable() {
127         Object[] array = new Object[32];
128         Arrays.fill(array, UNSET);
129         return array;
130     }
131 
132     public int size() {
133         int count = 0;
134 
135         if (futureListenerStackDepth != 0) {
136             count ++;
137         }
138         if (localChannelReaderStackDepth != 0) {
139             count ++;
140         }
141         if (handlerSharableCache != null) {
142             count ++;
143         }
144         if (counterHashCode != null) {
145             count ++;
146         }
147         if (random != null) {
148             count ++;
149         }
150         if (typeParameterMatcherGetCache != null) {
151             count ++;
152         }
153         if (typeParameterMatcherFindCache != null) {
154             count ++;
155         }
156         if (stringBuilder != null) {
157             count ++;
158         }
159         if (charsetEncoderCache != null) {
160             count ++;
161         }
162         if (charsetDecoderCache != null) {
163             count ++;
164         }
165         if (arrayList != null) {
166             count ++;
167         }
168 
169         for (Object o: indexedVariables) {
170             if (o != UNSET) {
171                 count ++;
172             }
173         }
174 
175         // We should subtract 1 from the count because the first element in 'indexedVariables' is reserved
176         // by 'FastThreadLocal' to keep the list of 'FastThreadLocal's to remove on 'FastThreadLocal.removeAll()'.
177         return count - 1;
178     }
179 
180     public StringBuilder stringBuilder() {
181         StringBuilder sb = stringBuilder;
182         if (sb == null) {
183             return stringBuilder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE);
184         }
185         if (sb.capacity() > STRING_BUILDER_MAX_SIZE) {
186             sb.setLength(STRING_BUILDER_INITIAL_SIZE);
187             sb.trimToSize();
188         }
189         sb.setLength(0);
190         return sb;
191     }
192 
193     public Map<Charset, CharsetEncoder> charsetEncoderCache() {
194         Map<Charset, CharsetEncoder> cache = charsetEncoderCache;
195         if (cache == null) {
196             charsetEncoderCache = cache = new IdentityHashMap<Charset, CharsetEncoder>();
197         }
198         return cache;
199     }
200 
201     public Map<Charset, CharsetDecoder> charsetDecoderCache() {
202         Map<Charset, CharsetDecoder> cache = charsetDecoderCache;
203         if (cache == null) {
204             charsetDecoderCache = cache = new IdentityHashMap<Charset, CharsetDecoder>();
205         }
206         return cache;
207     }
208 
209     public <E> ArrayList<E> arrayList() {
210         return arrayList(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY);
211     }
212 
213     @SuppressWarnings("unchecked")
214     public <E> ArrayList<E> arrayList(int minCapacity) {
215         ArrayList<E> list = (ArrayList<E>) arrayList;
216         if (list == null) {
217             arrayList = new ArrayList<Object>(minCapacity);
218             return (ArrayList<E>) arrayList;
219         }
220         list.clear();
221         list.ensureCapacity(minCapacity);
222         return list;
223     }
224 
225     public int futureListenerStackDepth() {
226         return futureListenerStackDepth;
227     }
228 
229     public void setFutureListenerStackDepth(int futureListenerStackDepth) {
230         this.futureListenerStackDepth = futureListenerStackDepth;
231     }
232 
233     public ThreadLocalRandom random() {
234         ThreadLocalRandom r = random;
235         if (r == null) {
236             random = r = new ThreadLocalRandom();
237         }
238         return r;
239     }
240 
241     public Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache() {
242         Map<Class<?>, TypeParameterMatcher> cache = typeParameterMatcherGetCache;
243         if (cache == null) {
244             typeParameterMatcherGetCache = cache = new IdentityHashMap<Class<?>, TypeParameterMatcher>();
245         }
246         return cache;
247     }
248 
249     public Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache() {
250         Map<Class<?>, Map<String, TypeParameterMatcher>> cache = typeParameterMatcherFindCache;
251         if (cache == null) {
252             typeParameterMatcherFindCache = cache = new IdentityHashMap<Class<?>, Map<String, TypeParameterMatcher>>();
253         }
254         return cache;
255     }
256 
257     public IntegerHolder counterHashCode() {
258         return counterHashCode;
259     }
260 
261     public void setCounterHashCode(IntegerHolder counterHashCode) {
262         this.counterHashCode = counterHashCode;
263     }
264 
265     public Map<Class<?>, Boolean> handlerSharableCache() {
266         Map<Class<?>, Boolean> cache = handlerSharableCache;
267         if (cache == null) {
268             // Start with small capacity to keep memory overhead as low as possible.
269             handlerSharableCache = cache = new WeakHashMap<Class<?>, Boolean>(4);
270         }
271         return cache;
272     }
273 
274     public int localChannelReaderStackDepth() {
275         return localChannelReaderStackDepth;
276     }
277 
278     public void setLocalChannelReaderStackDepth(int localChannelReaderStackDepth) {
279         this.localChannelReaderStackDepth = localChannelReaderStackDepth;
280     }
281 
282     public Object indexedVariable(int index) {
283         Object[] lookup = indexedVariables;
284         return index < lookup.length? lookup[index] : UNSET;
285     }
286 
287     /**
288      * @return {@code true} if and only if a new thread-local variable has been created
289      */
290     public boolean setIndexedVariable(int index, Object value) {
291         Object[] lookup = indexedVariables;
292         if (index < lookup.length) {
293             Object oldValue = lookup[index];
294             lookup[index] = value;
295             return oldValue == UNSET;
296         } else {
297             expandIndexedVariableTableAndSet(index, value);
298             return true;
299         }
300     }
301 
302     private void expandIndexedVariableTableAndSet(int index, Object value) {
303         Object[] oldArray = indexedVariables;
304         final int oldCapacity = oldArray.length;
305         int newCapacity = index;
306         newCapacity |= newCapacity >>>  1;
307         newCapacity |= newCapacity >>>  2;
308         newCapacity |= newCapacity >>>  4;
309         newCapacity |= newCapacity >>>  8;
310         newCapacity |= newCapacity >>> 16;
311         newCapacity ++;
312 
313         Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
314         Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
315         newArray[index] = value;
316         indexedVariables = newArray;
317     }
318 
319     public Object removeIndexedVariable(int index) {
320         Object[] lookup = indexedVariables;
321         if (index < lookup.length) {
322             Object v = lookup[index];
323             lookup[index] = UNSET;
324             return v;
325         } else {
326             return UNSET;
327         }
328     }
329 
330     public boolean isIndexedVariableSet(int index) {
331         Object[] lookup = indexedVariables;
332         return index < lookup.length && lookup[index] != UNSET;
333     }
334 }