1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.util.concurrent;
17
18 import io.netty.util.internal.InternalThreadLocalMap;
19 import io.netty.util.internal.LongLongHashMap;
20 import io.netty.util.internal.logging.InternalLogger;
21 import io.netty.util.internal.logging.InternalLoggerFactory;
22
23 import java.util.concurrent.atomic.AtomicReference;
24
25
26
27
28 public class FastThreadLocalThread extends Thread {
29
30 private static final InternalLogger logger = InternalLoggerFactory.getInstance(FastThreadLocalThread.class);
31
32
33
34
35 private static final AtomicReference<FallbackThreadSet> fallbackThreads =
36 new AtomicReference<>(FallbackThreadSet.EMPTY);
37
38
39 private final boolean cleanupFastThreadLocals;
40
41 private InternalThreadLocalMap threadLocalMap;
42
43 public FastThreadLocalThread() {
44 cleanupFastThreadLocals = false;
45 }
46
47 public FastThreadLocalThread(Runnable target) {
48 super(FastThreadLocalRunnable.wrap(target));
49 cleanupFastThreadLocals = true;
50 }
51
52 public FastThreadLocalThread(ThreadGroup group, Runnable target) {
53 super(group, FastThreadLocalRunnable.wrap(target));
54 cleanupFastThreadLocals = true;
55 }
56
57 public FastThreadLocalThread(String name) {
58 super(name);
59 cleanupFastThreadLocals = false;
60 }
61
62 public FastThreadLocalThread(ThreadGroup group, String name) {
63 super(group, name);
64 cleanupFastThreadLocals = false;
65 }
66
67 public FastThreadLocalThread(Runnable target, String name) {
68 super(FastThreadLocalRunnable.wrap(target), name);
69 cleanupFastThreadLocals = true;
70 }
71
72 public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
73 super(group, FastThreadLocalRunnable.wrap(target), name);
74 cleanupFastThreadLocals = true;
75 }
76
77 public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) {
78 super(group, FastThreadLocalRunnable.wrap(target), name, stackSize);
79 cleanupFastThreadLocals = true;
80 }
81
82
83
84
85
86 public final InternalThreadLocalMap threadLocalMap() {
87 if (this != Thread.currentThread() && logger.isWarnEnabled()) {
88 logger.warn(new RuntimeException("It's not thread-safe to get 'threadLocalMap' " +
89 "which doesn't belong to the caller thread"));
90 }
91 return threadLocalMap;
92 }
93
94
95
96
97
98 public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
99 if (this != Thread.currentThread() && logger.isWarnEnabled()) {
100 logger.warn(new RuntimeException("It's not thread-safe to set 'threadLocalMap' " +
101 "which doesn't belong to the caller thread"));
102 }
103 this.threadLocalMap = threadLocalMap;
104 }
105
106
107
108
109
110
111 @Deprecated
112 public boolean willCleanupFastThreadLocals() {
113 return cleanupFastThreadLocals;
114 }
115
116
117
118
119
120
121 @Deprecated
122 public static boolean willCleanupFastThreadLocals(Thread thread) {
123 return thread instanceof FastThreadLocalThread &&
124 ((FastThreadLocalThread) thread).willCleanupFastThreadLocals();
125 }
126
127
128
129
130 public static boolean currentThreadWillCleanupFastThreadLocals() {
131
132 Thread currentThread = currentThread();
133 if (currentThread instanceof FastThreadLocalThread) {
134 return ((FastThreadLocalThread) currentThread).willCleanupFastThreadLocals();
135 }
136 return isFastThreadLocalVirtualThread();
137 }
138
139
140
141
142 public static boolean currentThreadHasFastThreadLocal() {
143
144 return currentThread() instanceof FastThreadLocalThread || isFastThreadLocalVirtualThread();
145 }
146
147 private static boolean isFastThreadLocalVirtualThread() {
148 return fallbackThreads.get().contains(currentThread().getId());
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163 public static void runWithFastThreadLocal(Runnable runnable) {
164 Thread current = currentThread();
165 if (current instanceof FastThreadLocalThread) {
166 throw new IllegalStateException("Caller is a real FastThreadLocalThread");
167 }
168 long id = current.getId();
169 fallbackThreads.updateAndGet(set -> {
170 if (set.contains(id)) {
171 throw new IllegalStateException("Reentrant call to run()");
172 }
173 return set.add(id);
174 });
175
176 try {
177 runnable.run();
178 } finally {
179 fallbackThreads.getAndUpdate(set -> set.remove(id));
180 FastThreadLocal.removeAll();
181 }
182 }
183
184
185
186
187
188
189
190
191
192
193
194 public boolean permitBlockingCalls() {
195 return false;
196 }
197
198
199
200
201 private static final class FallbackThreadSet {
202 static final FallbackThreadSet EMPTY = new FallbackThreadSet();
203 private static final long EMPTY_VALUE = 0L;
204
205 private final LongLongHashMap map;
206
207 private FallbackThreadSet() {
208 this.map = new LongLongHashMap(EMPTY_VALUE);
209 }
210
211 private FallbackThreadSet(LongLongHashMap map) {
212 this.map = map;
213 }
214
215 public boolean contains(long threadId) {
216 long key = threadId >>> 6;
217 long bit = 1L << (threadId & 63);
218
219 long bitmap = map.get(key);
220 return (bitmap & bit) != 0;
221 }
222
223 public FallbackThreadSet add(long threadId) {
224 long key = threadId >>> 6;
225 long bit = 1L << (threadId & 63);
226
227 LongLongHashMap newMap = new LongLongHashMap(map);
228 long oldBitmap = newMap.get(key);
229 long newBitmap = oldBitmap | bit;
230 newMap.put(key, newBitmap);
231
232 return new FallbackThreadSet(newMap);
233 }
234
235 public FallbackThreadSet remove(long threadId) {
236 long key = threadId >>> 6;
237 long bit = 1L << (threadId & 63);
238
239 long oldBitmap = map.get(key);
240 if ((oldBitmap & bit) == 0) {
241 return this;
242 }
243
244 LongLongHashMap newMap = new LongLongHashMap(map);
245 long newBitmap = oldBitmap & ~bit;
246
247 if (newBitmap != EMPTY_VALUE) {
248 newMap.put(key, newBitmap);
249 } else {
250 newMap.remove(key);
251 }
252
253 return new FallbackThreadSet(newMap);
254 }
255 }
256 }