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 ScheduledFutureTask scheduledTask;
142 while ((scheduledTask = (ScheduledFutureTask) pollScheduledTask(nanoTime)) != null) {
143 if (scheduledTask.isCancelled()) {
144 continue;
145 }
146 taskQueue.add(scheduledTask);
147 }
148 }
149
150
151
152
153 public int pendingTasks() {
154 return taskQueue.size();
155 }
156
157
158
159
160
161 private void addTask(Runnable task) {
162 taskQueue.add(ObjectUtil.checkNotNull(task, "task"));
163 }
164
165 @Override
166 public boolean inEventLoop(Thread thread) {
167 return thread == this.thread;
168 }
169
170 @Override
171 public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
172 return terminationFuture();
173 }
174
175 @Override
176 public Future<?> terminationFuture() {
177 return terminationFuture;
178 }
179
180 @Override
181 @Deprecated
182 public void shutdown() {
183 throw new UnsupportedOperationException();
184 }
185
186 @Override
187 public boolean isShuttingDown() {
188 return false;
189 }
190
191 @Override
192 public boolean isShutdown() {
193 return false;
194 }
195
196 @Override
197 public boolean isTerminated() {
198 return false;
199 }
200
201 @Override
202 public boolean awaitTermination(long timeout, TimeUnit unit) {
203 return false;
204 }
205
206
207
208
209
210
211
212
213
214 public boolean awaitInactivity(long timeout, TimeUnit unit) throws InterruptedException {
215 ObjectUtil.checkNotNull(unit, "unit");
216
217 final Thread thread = this.thread;
218 if (thread == null) {
219 throw new IllegalStateException("thread was not started");
220 }
221 thread.join(unit.toMillis(timeout));
222 return !thread.isAlive();
223 }
224
225 @Override
226 public void execute(Runnable task) {
227 execute0(task);
228 }
229
230 private void execute0(@Schedule Runnable task) {
231 addTask(ObjectUtil.checkNotNull(task, "task"));
232 if (!inEventLoop()) {
233 startThread();
234 }
235 }
236
237 private void startThread() {
238 if (started.compareAndSet(false, true)) {
239 final Thread callingThread = Thread.currentThread();
240 ClassLoader parentCCL = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
241 @Override
242 public ClassLoader run() {
243 return callingThread.getContextClassLoader();
244 }
245 });
246
247 setContextClassLoader(callingThread, null);
248 try {
249 final Thread t = threadFactory.newThread(taskRunner);
250
251
252
253
254
255 setContextClassLoader(t, null);
256
257
258
259
260 thread = t;
261 t.start();
262 } finally {
263 setContextClassLoader(callingThread, parentCCL);
264 }
265 }
266 }
267
268 private static void setContextClassLoader(final Thread t, final ClassLoader cl) {
269 AccessController.doPrivileged(new PrivilegedAction<Void>() {
270 @Override
271 public Void run() {
272 t.setContextClassLoader(cl);
273 return null;
274 }
275 });
276 }
277
278 final class TaskRunner implements Runnable {
279 @Override
280 public void run() {
281 for (;;) {
282 Runnable task = takeTask();
283 if (task != null) {
284 try {
285 runTask(task);
286 } catch (Throwable t) {
287 logger.warn("Unexpected exception from the global event executor: ", t);
288 }
289
290 if (task != quietPeriodTask) {
291 continue;
292 }
293 }
294
295 Queue<ScheduledFutureTask<?>> scheduledTaskQueue = GlobalEventExecutor.this.scheduledTaskQueue;
296
297 if (taskQueue.isEmpty() && (scheduledTaskQueue == null || scheduledTaskQueue.size() == 1)) {
298
299
300
301 boolean stopped = started.compareAndSet(true, false);
302 assert stopped;
303
304
305
306
307 if (taskQueue.isEmpty()) {
308
309
310
311
312 break;
313 }
314
315
316 if (!started.compareAndSet(false, true)) {
317
318
319 break;
320 }
321
322
323
324
325 }
326 }
327 }
328 }
329
330 private static final class StacklessUnsupportedOperationException extends UnsupportedOperationException {
331
332 private static final long serialVersionUID = -8060232216137960173L;
333
334 private StacklessUnsupportedOperationException() { }
335
336
337
338
339
340 @Override
341 public Throwable fillInStackTrace() {
342 return this;
343 }
344
345 static StacklessUnsupportedOperationException newInstance(Class<?> clazz, String method) {
346 return ThrowableUtil.unknownStackTrace(new StacklessUnsupportedOperationException(), clazz, method);
347 }
348 }
349 }