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