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.logging.InternalLogger;
20 import io.netty.util.internal.logging.InternalLoggerFactory;
21
22 import java.util.Arrays;
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<long[]> fallbackThreads = new AtomicReference<>(null);
36
37
38 private final boolean cleanupFastThreadLocals;
39
40 private InternalThreadLocalMap threadLocalMap;
41
42 public FastThreadLocalThread() {
43 cleanupFastThreadLocals = false;
44 }
45
46 public FastThreadLocalThread(Runnable target) {
47 super(FastThreadLocalRunnable.wrap(target));
48 cleanupFastThreadLocals = true;
49 }
50
51 public FastThreadLocalThread(ThreadGroup group, Runnable target) {
52 super(group, FastThreadLocalRunnable.wrap(target));
53 cleanupFastThreadLocals = true;
54 }
55
56 public FastThreadLocalThread(String name) {
57 super(name);
58 cleanupFastThreadLocals = false;
59 }
60
61 public FastThreadLocalThread(ThreadGroup group, String name) {
62 super(group, name);
63 cleanupFastThreadLocals = false;
64 }
65
66 public FastThreadLocalThread(Runnable target, String name) {
67 super(FastThreadLocalRunnable.wrap(target), name);
68 cleanupFastThreadLocals = true;
69 }
70
71 public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
72 super(group, FastThreadLocalRunnable.wrap(target), name);
73 cleanupFastThreadLocals = true;
74 }
75
76 public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) {
77 super(group, FastThreadLocalRunnable.wrap(target), name, stackSize);
78 cleanupFastThreadLocals = true;
79 }
80
81
82
83
84
85 public final InternalThreadLocalMap threadLocalMap() {
86 if (this != Thread.currentThread() && logger.isWarnEnabled()) {
87 logger.warn(new RuntimeException("It's not thread-safe to get 'threadLocalMap' " +
88 "which doesn't belong to the caller thread"));
89 }
90 return threadLocalMap;
91 }
92
93
94
95
96
97 public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
98 if (this != Thread.currentThread() && logger.isWarnEnabled()) {
99 logger.warn(new RuntimeException("It's not thread-safe to set 'threadLocalMap' " +
100 "which doesn't belong to the caller thread"));
101 }
102 this.threadLocalMap = threadLocalMap;
103 }
104
105
106
107
108
109
110 @Deprecated
111 public boolean willCleanupFastThreadLocals() {
112 return cleanupFastThreadLocals;
113 }
114
115
116
117
118
119
120 @Deprecated
121 public static boolean willCleanupFastThreadLocals(Thread thread) {
122 return thread instanceof FastThreadLocalThread &&
123 ((FastThreadLocalThread) thread).willCleanupFastThreadLocals();
124 }
125
126
127
128
129 public static boolean currentThreadWillCleanupFastThreadLocals() {
130
131 Thread currentThread = currentThread();
132 if (currentThread instanceof FastThreadLocalThread) {
133 return ((FastThreadLocalThread) currentThread).willCleanupFastThreadLocals();
134 }
135 return isFastThreadLocalVirtualThread();
136 }
137
138
139
140
141 public static boolean currentThreadHasFastThreadLocal() {
142
143 return currentThread() instanceof FastThreadLocalThread || isFastThreadLocalVirtualThread();
144 }
145
146 private static boolean isFastThreadLocalVirtualThread() {
147 long[] arr = fallbackThreads.get();
148 if (arr == null) {
149 return false;
150 }
151 return Arrays.binarySearch(arr, Thread.currentThread().getId()) >= 0;
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166 public static void runWithFastThreadLocal(Runnable runnable) {
167 Thread current = currentThread();
168 if (current instanceof FastThreadLocalThread) {
169 throw new IllegalStateException("Caller is a real FastThreadLocalThread");
170 }
171 long id = current.getId();
172 fallbackThreads.updateAndGet(arr -> {
173 if (arr == null) {
174 return new long[] { id };
175 }
176 int index = Arrays.binarySearch(arr, id);
177 if (index >= 0) {
178 throw new IllegalStateException("Reentrant call to run()");
179 }
180 index = ~index;
181 long[] next = new long[arr.length + 1];
182 System.arraycopy(arr, 0, next, 0, index);
183 next[index] = id;
184 System.arraycopy(arr, index, next, index + 1, arr.length - index);
185 return next;
186 });
187 try {
188 runnable.run();
189 } finally {
190 fallbackThreads.getAndUpdate(arr -> {
191 if (arr == null || (arr.length == 1 && arr[0] == id)) {
192 return null;
193 }
194 int index = Arrays.binarySearch(arr, id);
195 if (index < 0) {
196 return arr;
197 }
198 long[] next = new long[arr.length - 1];
199 System.arraycopy(arr, 0, next, 0, index);
200 System.arraycopy(arr, index + 1, next, index, arr.length - index - 1);
201 return next;
202 });
203 FastThreadLocal.removeAll();
204 }
205 }
206
207
208
209
210
211
212
213
214
215
216
217 public boolean permitBlockingCalls() {
218 return false;
219 }
220 }