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.ObjectUtil;
19 import io.netty.util.internal.SystemPropertyUtil;
20 import io.netty.util.internal.ThreadExecutorMap;
21 import io.netty.util.internal.ThrowableUtil;
22 import io.netty.util.internal.logging.InternalLogger;
23 import io.netty.util.internal.logging.InternalLoggerFactory;
24
25 import org.jetbrains.annotations.Async.Schedule;
26
27 import java.security.AccessController;
28 import java.security.PrivilegedAction;
29 import java.util.Queue;
30 import java.util.concurrent.BlockingQueue;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.LinkedBlockingQueue;
33 import java.util.concurrent.RejectedExecutionException;
34 import java.util.concurrent.ThreadFactory;
35 import java.util.concurrent.TimeUnit;
36 import java.util.concurrent.atomic.AtomicBoolean;
37
38
39
40
41
42
43
44 public final class GlobalEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
45 private static final InternalLogger logger = InternalLoggerFactory.getInstance(GlobalEventExecutor.class);
46
47 private static final long SCHEDULE_QUIET_PERIOD_INTERVAL;
48
49 static {
50 int quietPeriod = SystemPropertyUtil.getInt("io.netty.globalEventExecutor.quietPeriodSeconds", 1);
51 if (quietPeriod <= 0) {
52 quietPeriod = 1;
53 }
54 logger.debug("-Dio.netty.globalEventExecutor.quietPeriodSeconds: {}", quietPeriod);
55
56 SCHEDULE_QUIET_PERIOD_INTERVAL = TimeUnit.SECONDS.toNanos(quietPeriod);
57 }
58
59 public static final GlobalEventExecutor INSTANCE = new GlobalEventExecutor();
60
61 final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
62 final ScheduledFutureTask<Void> quietPeriodTask = new ScheduledFutureTask<Void>(
63 this, Executors.<Void>callable(new Runnable() {
64 @Override
65 public void run() {
66
67 }
68 }, null),
69
70
71 deadlineNanos(getCurrentTimeNanos(), SCHEDULE_QUIET_PERIOD_INTERVAL),
72 -SCHEDULE_QUIET_PERIOD_INTERVAL
73 );
74
75
76
77
78
79 final ThreadFactory threadFactory;
80 private final TaskRunner taskRunner = new TaskRunner();
81 private final AtomicBoolean started = new AtomicBoolean();
82 volatile Thread thread;
83
84 private final Future<?> terminationFuture;
85
86 private GlobalEventExecutor() {
87 scheduleFromEventLoop(quietPeriodTask);
88 threadFactory = ThreadExecutorMap.apply(new DefaultThreadFactory(
89 DefaultThreadFactory.toPoolName(getClass()), false, Thread.NORM_PRIORITY, null), this);
90
91 terminationFuture = new FailedFuture<Object>(this,
92 StacklessUnsupportedOperationException.newInstance(GlobalEventExecutor.class, "terminationFuture"));
93 }
94
95
96
97
98
99
100 Runnable takeTask() {
101 BlockingQueue<Runnable> taskQueue = this.taskQueue;
102 for (;;) {
103 ScheduledFutureTask<?> scheduledTask = peekScheduledTask();
104 if (scheduledTask == null) {
105 Runnable task = null;
106 try {
107 task = taskQueue.take();
108 } catch (InterruptedException e) {
109
110 }
111 return task;
112 } else {
113 long delayNanos = scheduledTask.delayNanos();
114 Runnable task = null;
115 if (delayNanos > 0) {
116 try {
117 task = taskQueue.poll(delayNanos, TimeUnit.NANOSECONDS);
118 } catch (InterruptedException e) {
119
120 return null;
121 }
122 }
123 if (task == null) {
124
125
126
127
128 fetchFromScheduledTaskQueue();
129 task = taskQueue.poll();
130 }
131
132 if (task != null) {
133 return task;
134 }
135 }
136 }
137 }
138
139 private void fetchFromScheduledTaskQueue() {
140 long nanoTime = getCurrentTimeNanos();
141 Runnable scheduledTask = pollScheduledTask(nanoTime);
142 while (scheduledTask != null) {
143 taskQueue.add(scheduledTask);
144 scheduledTask = pollScheduledTask(nanoTime);
145 }
146 }
147
148
149
150
151 public int pendingTasks() {
152 return taskQueue.size();
153 }
154
155
156
157
158
159 private void addTask(Runnable task) {
160 taskQueue.add(ObjectUtil.checkNotNull(task, "task"));
161 }
162
163 @Override
164 public boolean inEventLoop(Thread thread) {
165 return thread == this.thread;
166 }
167
168 @Override
169 public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
170 return terminationFuture();
171 }
172
173 @Override
174 public Future<?> terminationFuture() {
175 return terminationFuture;
176 }
177
178 @Override
179 @Deprecated
180 public void shutdown() {
181 throw new UnsupportedOperationException();
182 }
183
184 @Override
185 public boolean isShuttingDown() {
186 return false;
187 }
188
189 @Override
190 public boolean isShutdown() {
191 return false;
192 }
193
194 @Override
195 public boolean isTerminated() {
196 return false;
197 }
198
199 @Override
200 public boolean awaitTermination(long timeout, TimeUnit unit) {
201 return false;
202 }
203
204
205
206
207
208
209
210
211
212 public boolean awaitInactivity(long timeout, TimeUnit unit) throws InterruptedException {
213 ObjectUtil.checkNotNull(unit, "unit");
214
215 final Thread thread = this.thread;
216 if (thread == null) {
217 throw new IllegalStateException("thread was not started");
218 }
219 thread.join(unit.toMillis(timeout));
220 return !thread.isAlive();
221 }
222
223 @Override
224 public void execute(Runnable task) {
225 execute0(task);
226 }
227
228 private void execute0(@Schedule Runnable task) {
229 addTask(ObjectUtil.checkNotNull(task, "task"));
230 if (!inEventLoop()) {
231 startThread();
232 }
233 }
234
235 private void startThread() {
236 if (started.compareAndSet(false, true)) {
237 final Thread callingThread = Thread.currentThread();
238 ClassLoader parentCCL = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
239 @Override
240 public ClassLoader run() {
241 return callingThread.getContextClassLoader();
242 }
243 });
244
245 setContextClassLoader(callingThread, null);
246 try {
247 final Thread t = threadFactory.newThread(taskRunner);
248
249
250
251
252
253 setContextClassLoader(t, null);
254
255
256
257
258 thread = t;
259 t.start();
260 } finally {
261 setContextClassLoader(callingThread, parentCCL);
262 }
263 }
264 }
265
266 private static void setContextClassLoader(final Thread t, final ClassLoader cl) {
267 AccessController.doPrivileged(new PrivilegedAction<Void>() {
268 @Override
269 public Void run() {
270 t.setContextClassLoader(cl);
271 return null;
272 }
273 });
274 }
275
276 final class TaskRunner implements Runnable {
277 @Override
278 public void run() {
279 for (;;) {
280 Runnable task = takeTask();
281 if (task != null) {
282 try {
283 runTask(task);
284 } catch (Throwable t) {
285 logger.warn("Unexpected exception from the global event executor: ", t);
286 }
287
288 if (task != quietPeriodTask) {
289 continue;
290 }
291 }
292
293 Queue<ScheduledFutureTask<?>> scheduledTaskQueue = GlobalEventExecutor.this.scheduledTaskQueue;
294
295 if (taskQueue.isEmpty() && (scheduledTaskQueue == null || scheduledTaskQueue.size() == 1)) {
296
297
298
299 boolean stopped = started.compareAndSet(true, false);
300 assert stopped;
301
302
303
304
305 if (taskQueue.isEmpty()) {
306
307
308
309
310 break;
311 }
312
313
314 if (!started.compareAndSet(false, true)) {
315
316
317 break;
318 }
319
320
321
322
323 }
324 }
325 }
326 }
327
328 private static final class StacklessUnsupportedOperationException extends UnsupportedOperationException {
329
330 private static final long serialVersionUID = -8060232216137960173L;
331
332 private StacklessUnsupportedOperationException() { }
333
334
335
336
337
338 @Override
339 public Throwable fillInStackTrace() {
340 return this;
341 }
342
343 static StacklessUnsupportedOperationException newInstance(Class<?> clazz, String method) {
344 return ThrowableUtil.unknownStackTrace(new StacklessUnsupportedOperationException(), clazz, method);
345 }
346 }
347 }