View Javadoc
1   /*
2    * Copyright 2012 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    *   https://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  package io.netty.util.concurrent;
17  
18  import io.netty.util.internal.ObjectUtil;
19  import io.netty.util.internal.PlatformDependent;
20  import io.netty.util.internal.SystemPropertyUtil;
21  import io.netty.util.internal.ThreadExecutorMap;
22  import io.netty.util.internal.logging.InternalLogger;
23  import io.netty.util.internal.logging.InternalLoggerFactory;
24  import org.jetbrains.annotations.Async.Schedule;
25  
26  import java.lang.Thread.State;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.LinkedHashSet;
30  import java.util.List;
31  import java.util.Queue;
32  import java.util.Set;
33  import java.util.concurrent.BlockingQueue;
34  import java.util.concurrent.Callable;
35  import java.util.concurrent.CountDownLatch;
36  import java.util.concurrent.ExecutionException;
37  import java.util.concurrent.Executor;
38  import java.util.concurrent.LinkedBlockingQueue;
39  import java.util.concurrent.RejectedExecutionException;
40  import java.util.concurrent.ThreadFactory;
41  import java.util.concurrent.TimeUnit;
42  import java.util.concurrent.TimeoutException;
43  import java.util.concurrent.atomic.AtomicInteger;
44  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
45  import java.util.concurrent.atomic.AtomicLong;
46  import java.util.concurrent.atomic.AtomicLongFieldUpdater;
47  import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
48  import java.util.concurrent.locks.Lock;
49  import java.util.concurrent.locks.ReentrantLock;
50  
51  /**
52   * Abstract base class for {@link OrderedEventExecutor}'s that execute all its submitted tasks in a single thread.
53   *
54   */
55  public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
56  
57      static final int DEFAULT_MAX_PENDING_EXECUTOR_TASKS = Math.max(16,
58              SystemPropertyUtil.getInt("io.netty.eventexecutor.maxPendingTasks", Integer.MAX_VALUE));
59  
60      private static final InternalLogger logger =
61              InternalLoggerFactory.getInstance(SingleThreadEventExecutor.class);
62  
63      private static final int ST_NOT_STARTED = 1;
64      private static final int ST_SUSPENDING = 2;
65      private static final int ST_SUSPENDED = 3;
66      private static final int ST_STARTED = 4;
67      private static final int ST_SHUTTING_DOWN = 5;
68      private static final int ST_SHUTDOWN = 6;
69      private static final int ST_TERMINATED = 7;
70  
71      private static final Runnable NOOP_TASK = new Runnable() {
72          @Override
73          public void run() {
74              // Do nothing.
75          }
76      };
77  
78      private static final AtomicIntegerFieldUpdater<SingleThreadEventExecutor> STATE_UPDATER =
79              AtomicIntegerFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "state");
80      private static final AtomicReferenceFieldUpdater<SingleThreadEventExecutor, ThreadProperties> PROPERTIES_UPDATER =
81              AtomicReferenceFieldUpdater.newUpdater(
82                      SingleThreadEventExecutor.class, ThreadProperties.class, "threadProperties");
83      private static final AtomicLongFieldUpdater<SingleThreadEventExecutor> ACCUMULATED_ACTIVE_TIME_NANOS_UPDATER =
84              AtomicLongFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "accumulatedActiveTimeNanos");
85      private static final AtomicIntegerFieldUpdater<SingleThreadEventExecutor> CONSECUTIVE_IDLE_CYCLES_UPDATER =
86              AtomicIntegerFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "consecutiveIdleCycles");
87      private static final AtomicIntegerFieldUpdater<SingleThreadEventExecutor> CONSECUTIVE_BUSY_CYCLES_UPDATER =
88              AtomicIntegerFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "consecutiveBusyCycles");
89      private final Queue<Runnable> taskQueue;
90  
91      private volatile Thread thread;
92      @SuppressWarnings("unused")
93      private volatile ThreadProperties threadProperties;
94      private final Executor executor;
95      private volatile boolean interrupted;
96  
97      private final Lock processingLock = new ReentrantLock();
98      private final CountDownLatch threadLock = new CountDownLatch(1);
99      private final Set<Runnable> shutdownHooks = new LinkedHashSet<Runnable>();
100     private final boolean addTaskWakesUp;
101     private final int maxPendingTasks;
102     private final RejectedExecutionHandler rejectedExecutionHandler;
103     private final boolean supportSuspension;
104 
105     // A running total of nanoseconds this executor has spent in an "active" state.
106     private volatile long accumulatedActiveTimeNanos;
107     // Timestamp of the last recorded activity (tasks + I/O).
108     private volatile long lastActivityTimeNanos;
109     /**
110      * Tracks the number of consecutive monitor cycles this executor's
111      * utilization has been below the scale-down threshold.
112      */
113     private volatile int consecutiveIdleCycles;
114 
115     /**
116      * Tracks the number of consecutive monitor cycles this executor's
117      * utilization has been above the scale-up threshold.
118      */
119     private volatile int consecutiveBusyCycles;
120     private long lastExecutionTime;
121 
122     @SuppressWarnings({ "FieldMayBeFinal", "unused" })
123     private volatile int state = ST_NOT_STARTED;
124 
125     private volatile long gracefulShutdownQuietPeriod;
126     private volatile long gracefulShutdownTimeout;
127     private long gracefulShutdownStartTime;
128 
129     private final Promise<?> terminationFuture = new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE);
130 
131     /**
132      * Create a new instance
133      *
134      * @param parent            the {@link EventExecutorGroup} which is the parent of this instance and belongs to it
135      * @param threadFactory     the {@link ThreadFactory} which will be used for the used {@link Thread}
136      * @param addTaskWakesUp    {@code true} if and only if invocation of {@link #addTask(Runnable)} will wake up the
137      *                          executor thread
138      */
139     protected SingleThreadEventExecutor(
140             EventExecutorGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp) {
141         this(parent, new ThreadPerTaskExecutor(threadFactory), addTaskWakesUp);
142     }
143 
144     /**
145      * Create a new instance
146      *
147      * @param parent            the {@link EventExecutorGroup} which is the parent of this instance and belongs to it
148      * @param threadFactory     the {@link ThreadFactory} which will be used for the used {@link Thread}
149      * @param addTaskWakesUp    {@code true} if and only if invocation of {@link #addTask(Runnable)} will wake up the
150      *                          executor thread
151      * @param maxPendingTasks   the maximum number of pending tasks before new tasks will be rejected.
152      * @param rejectedHandler   the {@link RejectedExecutionHandler} to use.
153      */
154     protected SingleThreadEventExecutor(
155             EventExecutorGroup parent, ThreadFactory threadFactory,
156             boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedHandler) {
157         this(parent, new ThreadPerTaskExecutor(threadFactory), addTaskWakesUp, maxPendingTasks, rejectedHandler);
158     }
159 
160     /**
161      * Create a new instance
162      *
163      * @param parent            the {@link EventExecutorGroup} which is the parent of this instance and belongs to it
164      * @param threadFactory     the {@link ThreadFactory} which will be used for the used {@link Thread}
165      * @param addTaskWakesUp    {@code true} if and only if invocation of {@link #addTask(Runnable)} will wake up the
166      *                          executor thread
167      * @param supportSuspension {@code true} if suspension of this {@link SingleThreadEventExecutor} is supported.
168      * @param maxPendingTasks   the maximum number of pending tasks before new tasks will be rejected.
169      * @param rejectedHandler   the {@link RejectedExecutionHandler} to use.
170      */
171     protected SingleThreadEventExecutor(
172             EventExecutorGroup parent, ThreadFactory threadFactory,
173             boolean addTaskWakesUp, boolean supportSuspension,
174             int maxPendingTasks, RejectedExecutionHandler rejectedHandler) {
175         this(parent, new ThreadPerTaskExecutor(threadFactory), addTaskWakesUp, supportSuspension,
176                 maxPendingTasks, rejectedHandler);
177     }
178 
179     /**
180      * Create a new instance
181      *
182      * @param parent            the {@link EventExecutorGroup} which is the parent of this instance and belongs to it
183      * @param executor          the {@link Executor} which will be used for executing
184      * @param addTaskWakesUp    {@code true} if and only if invocation of {@link #addTask(Runnable)} will wake up the
185      *                          executor thread
186      */
187     protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor, boolean addTaskWakesUp) {
188         this(parent, executor, addTaskWakesUp, DEFAULT_MAX_PENDING_EXECUTOR_TASKS, RejectedExecutionHandlers.reject());
189     }
190 
191     /**
192      * Create a new instance
193      *
194      * @param parent            the {@link EventExecutorGroup} which is the parent of this instance and belongs to it
195      * @param executor          the {@link Executor} which will be used for executing
196      * @param addTaskWakesUp    {@code true} if and only if invocation of {@link #addTask(Runnable)} will wake up the
197      *                          executor thread
198      * @param maxPendingTasks   the maximum number of pending tasks before new tasks will be rejected.
199      * @param rejectedHandler   the {@link RejectedExecutionHandler} to use.
200      */
201     protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
202                                         boolean addTaskWakesUp, int maxPendingTasks,
203                                         RejectedExecutionHandler rejectedHandler) {
204         this(parent, executor, addTaskWakesUp, false, maxPendingTasks, rejectedHandler);
205     }
206 
207     /**
208      * Create a new instance
209      *
210      * @param parent            the {@link EventExecutorGroup} which is the parent of this instance and belongs to it
211      * @param executor          the {@link Executor} which will be used for executing
212      * @param addTaskWakesUp    {@code true} if and only if invocation of {@link #addTask(Runnable)} will wake up the
213      *                          executor thread
214      * @param supportSuspension {@code true} if suspension of this {@link SingleThreadEventExecutor} is supported.
215      * @param maxPendingTasks   the maximum number of pending tasks before new tasks will be rejected.
216      * @param rejectedHandler   the {@link RejectedExecutionHandler} to use.
217      */
218     protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
219                                         boolean addTaskWakesUp, boolean supportSuspension,
220                                         int maxPendingTasks, RejectedExecutionHandler rejectedHandler) {
221         super(parent);
222         this.addTaskWakesUp = addTaskWakesUp;
223         this.supportSuspension = supportSuspension;
224         this.maxPendingTasks = Math.max(16, maxPendingTasks);
225         this.executor = ThreadExecutorMap.apply(executor, this);
226         taskQueue = newTaskQueue(this.maxPendingTasks);
227         rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
228         lastActivityTimeNanos = ticker().nanoTime();
229     }
230 
231     protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
232                                         boolean addTaskWakesUp, Queue<Runnable> taskQueue,
233                                         RejectedExecutionHandler rejectedHandler) {
234         this(parent, executor, addTaskWakesUp, false, taskQueue, rejectedHandler);
235     }
236 
237     protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
238                                         boolean addTaskWakesUp, boolean supportSuspension,
239                                         Queue<Runnable> taskQueue, RejectedExecutionHandler rejectedHandler) {
240         super(parent);
241         this.addTaskWakesUp = addTaskWakesUp;
242         this.supportSuspension = supportSuspension;
243         this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
244         this.executor = ThreadExecutorMap.apply(executor, this);
245         this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
246         this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
247     }
248 
249     /**
250      * @deprecated Please use and override {@link #newTaskQueue(int)}.
251      */
252     @Deprecated
253     protected Queue<Runnable> newTaskQueue() {
254         return newTaskQueue(maxPendingTasks);
255     }
256 
257     /**
258      * Create a new {@link Queue} which will holds the tasks to execute. This default implementation will return a
259      * {@link LinkedBlockingQueue} but if your sub-class of {@link SingleThreadEventExecutor} will not do any blocking
260      * calls on the this {@link Queue} it may make sense to {@code @Override} this and return some more performant
261      * implementation that does not support blocking operations at all.
262      */
263     protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
264         return new LinkedBlockingQueue<Runnable>(maxPendingTasks);
265     }
266 
267     /**
268      * Interrupt the current running {@link Thread}.
269      */
270     protected void interruptThread() {
271         Thread currentThread = thread;
272         if (currentThread == null) {
273             interrupted = true;
274         } else {
275             currentThread.interrupt();
276         }
277     }
278 
279     /**
280      * @see Queue#poll()
281      */
282     protected Runnable pollTask() {
283         assert inEventLoop();
284         return pollTaskFrom(taskQueue);
285     }
286 
287     protected static Runnable pollTaskFrom(Queue<Runnable> taskQueue) {
288         for (;;) {
289             Runnable task = taskQueue.poll();
290             if (task != WAKEUP_TASK) {
291                 return task;
292             }
293         }
294     }
295 
296     /**
297      * Take the next {@link Runnable} from the task queue and so will block if no task is currently present.
298      * <p>
299      * Be aware that this method will throw an {@link UnsupportedOperationException} if the task queue, which was
300      * created via {@link #newTaskQueue()}, does not implement {@link BlockingQueue}.
301      * </p>
302      *
303      * @return {@code null} if the executor thread has been interrupted or waken up.
304      */
305     protected Runnable takeTask() {
306         assert inEventLoop();
307         if (!(taskQueue instanceof BlockingQueue)) {
308             throw new UnsupportedOperationException();
309         }
310 
311         BlockingQueue<Runnable> taskQueue = (BlockingQueue<Runnable>) this.taskQueue;
312         for (;;) {
313             ScheduledFutureTask<?> scheduledTask = peekScheduledTask();
314             if (scheduledTask == null) {
315                 Runnable task = null;
316                 try {
317                     task = taskQueue.take();
318                     if (task == WAKEUP_TASK) {
319                         task = null;
320                     }
321                 } catch (InterruptedException e) {
322                     // Ignore
323                 }
324                 return task;
325             } else {
326                 long delayNanos = scheduledTask.delayNanos();
327                 Runnable task = null;
328                 if (delayNanos > 0) {
329                     try {
330                         task = taskQueue.poll(delayNanos, TimeUnit.NANOSECONDS);
331                     } catch (InterruptedException e) {
332                         // Waken up.
333                         return null;
334                     }
335                 }
336                 if (task == null) {
337                     // We need to fetch the scheduled tasks now as otherwise there may be a chance that
338                     // scheduled tasks are never executed if there is always one task in the taskQueue.
339                     // This is for example true for the read task of OIO Transport
340                     // See https://github.com/netty/netty/issues/1614
341                     fetchFromScheduledTaskQueue();
342                     task = taskQueue.poll();
343                 }
344 
345                 if (task != null) {
346                     if (task == WAKEUP_TASK) {
347                         return null;
348                     }
349                     return task;
350                 }
351             }
352         }
353     }
354 
355     private boolean fetchFromScheduledTaskQueue() {
356         return fetchFromScheduledTaskQueue(taskQueue);
357     }
358 
359     /**
360      * @return {@code true} if at least one scheduled task was executed.
361      */
362     private boolean executeExpiredScheduledTasks() {
363         if (scheduledTaskQueue == null || scheduledTaskQueue.isEmpty()) {
364             return false;
365         }
366         long nanoTime = getCurrentTimeNanos();
367         Runnable scheduledTask = pollScheduledTask(nanoTime);
368         if (scheduledTask == null) {
369             return false;
370         }
371         do {
372             safeExecute(scheduledTask);
373         } while ((scheduledTask = pollScheduledTask(nanoTime)) != null);
374         return true;
375     }
376 
377     /**
378      * @see Queue#peek()
379      */
380     protected Runnable peekTask() {
381         assert inEventLoop();
382         return taskQueue.peek();
383     }
384 
385     /**
386      * @see Queue#isEmpty()
387      */
388     protected boolean hasTasks() {
389         assert inEventLoop();
390         return !taskQueue.isEmpty();
391     }
392 
393     /**
394      * Return the number of tasks that are pending for processing.
395      */
396     public int pendingTasks() {
397         return taskQueue.size();
398     }
399 
400     /**
401      * Add a task to the task queue, or throws a {@link RejectedExecutionException} if this instance was shutdown
402      * before.
403      */
404     protected void addTask(Runnable task) {
405         ObjectUtil.checkNotNull(task, "task");
406         if (!offerTask(task)) {
407             reject(task);
408         }
409     }
410 
411     final boolean offerTask(Runnable task) {
412         if (isShutdown()) {
413             reject();
414         }
415         return taskQueue.offer(task);
416     }
417 
418     /**
419      * @see Queue#remove(Object)
420      */
421     protected boolean removeTask(Runnable task) {
422         return taskQueue.remove(ObjectUtil.checkNotNull(task, "task"));
423     }
424 
425     /**
426      * Poll all tasks from the task queue and run them via {@link Runnable#run()} method.
427      *
428      * @return {@code true} if and only if at least one task was run
429      */
430     protected boolean runAllTasks() {
431         assert inEventLoop();
432         boolean fetchedAll;
433         boolean ranAtLeastOne = false;
434 
435         do {
436             fetchedAll = fetchFromScheduledTaskQueue(taskQueue);
437             if (runAllTasksFrom(taskQueue)) {
438                 ranAtLeastOne = true;
439             }
440         } while (!fetchedAll); // keep on processing until we fetched all scheduled tasks.
441 
442         if (ranAtLeastOne) {
443             lastExecutionTime = getCurrentTimeNanos();
444         }
445         afterRunningAllTasks();
446         return ranAtLeastOne;
447     }
448 
449     /**
450      * Execute all expired scheduled tasks and all current tasks in the executor queue until both queues are empty,
451      * or {@code maxDrainAttempts} has been exceeded.
452      * @param maxDrainAttempts The maximum amount of times this method attempts to drain from queues. This is to prevent
453      *                         continuous task execution and scheduling from preventing the EventExecutor thread to
454      *                         make progress and return to the selector mechanism to process inbound I/O events.
455      * @return {@code true} if at least one task was run.
456      */
457     protected final boolean runScheduledAndExecutorTasks(final int maxDrainAttempts) {
458         assert inEventLoop();
459         boolean ranAtLeastOneTask;
460         int drainAttempt = 0;
461         do {
462             // We must run the taskQueue tasks first, because the scheduled tasks from outside the EventLoop are queued
463             // here because the taskQueue is thread safe and the scheduledTaskQueue is not thread safe.
464             ranAtLeastOneTask = runExistingTasksFrom(taskQueue) | executeExpiredScheduledTasks();
465         } while (ranAtLeastOneTask && ++drainAttempt < maxDrainAttempts);
466 
467         if (drainAttempt > 0) {
468             lastExecutionTime = getCurrentTimeNanos();
469         }
470         afterRunningAllTasks();
471 
472         return drainAttempt > 0;
473     }
474 
475     /**
476      * Runs all tasks from the passed {@code taskQueue}.
477      *
478      * @param taskQueue To poll and execute all tasks.
479      *
480      * @return {@code true} if at least one task was executed.
481      */
482     protected final boolean runAllTasksFrom(Queue<Runnable> taskQueue) {
483         Runnable task = pollTaskFrom(taskQueue);
484         if (task == null) {
485             return false;
486         }
487         for (;;) {
488             safeExecute(task);
489             task = pollTaskFrom(taskQueue);
490             if (task == null) {
491                 return true;
492             }
493         }
494     }
495 
496     /**
497      * What ever tasks are present in {@code taskQueue} when this method is invoked will be {@link Runnable#run()}.
498      * @param taskQueue the task queue to drain.
499      * @return {@code true} if at least {@link Runnable#run()} was called.
500      */
501     private boolean runExistingTasksFrom(Queue<Runnable> taskQueue) {
502         Runnable task = pollTaskFrom(taskQueue);
503         if (task == null) {
504             return false;
505         }
506         int remaining = Math.min(maxPendingTasks, taskQueue.size());
507         safeExecute(task);
508         // Use taskQueue.poll() directly rather than pollTaskFrom() since the latter may
509         // silently consume more than one item from the queue (skips over WAKEUP_TASK instances)
510         while (remaining-- > 0 && (task = taskQueue.poll()) != null) {
511             safeExecute(task);
512         }
513         return true;
514     }
515 
516     /**
517      * Poll all tasks from the task queue and run them via {@link Runnable#run()} method.  This method stops running
518      * the tasks in the task queue and returns if it ran longer than {@code timeoutNanos}.
519      */
520     @SuppressWarnings("NonAtomicOperationOnVolatileField")
521     protected boolean runAllTasks(long timeoutNanos) {
522         fetchFromScheduledTaskQueue(taskQueue);
523         Runnable task = pollTask();
524         if (task == null) {
525             afterRunningAllTasks();
526             return false;
527         }
528 
529         final long deadline = timeoutNanos > 0 ? getCurrentTimeNanos() + timeoutNanos : 0;
530         long runTasks = 0;
531         long lastExecutionTime;
532 
533         long workStartTime = ticker().nanoTime();
534         for (;;) {
535             safeExecute(task);
536 
537             runTasks ++;
538 
539             // Check timeout every 64 tasks because nanoTime() is relatively expensive.
540             // XXX: Hard-coded value - will make it configurable if it is really a problem.
541             if ((runTasks & 0x3F) == 0) {
542                 lastExecutionTime = getCurrentTimeNanos();
543                 if (lastExecutionTime >= deadline) {
544                     break;
545                 }
546             }
547 
548             task = pollTask();
549             if (task == null) {
550                 lastExecutionTime = getCurrentTimeNanos();
551                 break;
552             }
553         }
554 
555         long workEndTime = ticker().nanoTime();
556         accumulatedActiveTimeNanos += workEndTime - workStartTime;
557         lastActivityTimeNanos = workEndTime;
558 
559         afterRunningAllTasks();
560         this.lastExecutionTime = lastExecutionTime;
561         return true;
562     }
563 
564     /**
565      * Invoked before returning from {@link #runAllTasks()} and {@link #runAllTasks(long)}.
566      */
567     protected void afterRunningAllTasks() { }
568 
569     /**
570      * Returns the amount of time left until the scheduled task with the closest dead line is executed.
571      */
572     protected long delayNanos(long currentTimeNanos) {
573         currentTimeNanos -= ticker().initialNanoTime();
574 
575         ScheduledFutureTask<?> scheduledTask = peekScheduledTask();
576         if (scheduledTask == null) {
577             return SCHEDULE_PURGE_INTERVAL;
578         }
579 
580         return scheduledTask.delayNanos(currentTimeNanos);
581     }
582 
583     /**
584      * Returns the absolute point in time (relative to {@link #getCurrentTimeNanos()}) at which the next
585      * closest scheduled task should run.
586      */
587     protected long deadlineNanos() {
588         ScheduledFutureTask<?> scheduledTask = peekScheduledTask();
589         if (scheduledTask == null) {
590             return getCurrentTimeNanos() + SCHEDULE_PURGE_INTERVAL;
591         }
592         return scheduledTask.deadlineNanos();
593     }
594 
595     /**
596      * Updates the internal timestamp that tells when a submitted task was executed most recently.
597      * {@link #runAllTasks()} and {@link #runAllTasks(long)} updates this timestamp automatically, and thus there's
598      * usually no need to call this method.  However, if you take the tasks manually using {@link #takeTask()} or
599      * {@link #pollTask()}, you have to call this method at the end of task execution loop for accurate quiet period
600      * checks.
601      */
602     protected void updateLastExecutionTime() {
603         long now = getCurrentTimeNanos();
604         lastExecutionTime = now;
605         lastActivityTimeNanos = now;
606     }
607 
608     /**
609      * Returns the number of registered channels for auto-scaling related decisions.
610      * This is intended to be used by {@link MultithreadEventExecutorGroup} for dynamic scaling.
611      *
612      * @return The number of registered channels, or {@code -1} if not applicable.
613      */
614     protected int getNumOfRegisteredChannels() {
615         return -1;
616     }
617 
618     /**
619      * Adds the given duration to the total active time for the current measurement window.
620      * <p>
621      * <strong>Note:</strong> This method is not thread-safe and must only be called from the
622      * {@link #inEventLoop() event loop thread}.
623      *
624      * @param nanos The active time in nanoseconds to add.
625      */
626     @SuppressWarnings("NonAtomicOperationOnVolatileField")
627     protected void reportActiveIoTime(long nanos) {
628         assert inEventLoop();
629         if (nanos > 0) {
630             accumulatedActiveTimeNanos += nanos;
631             lastActivityTimeNanos = ticker().nanoTime();
632         }
633     }
634 
635     /**
636      * Returns the accumulated active time since the last call and resets the counter.
637      */
638     protected long getAndResetAccumulatedActiveTimeNanos() {
639         return ACCUMULATED_ACTIVE_TIME_NANOS_UPDATER.getAndSet(this, 0);
640     }
641 
642     /**
643      * Returns the timestamp of the last known activity (tasks + I/O).
644      */
645     protected long getLastActivityTimeNanos() {
646         return lastActivityTimeNanos;
647     }
648 
649     /**
650      * Atomically increments the counter for consecutive monitor cycles where utilization was below the
651      * scale-down threshold. This is used by the auto-scaling monitor to track sustained idleness.
652      *
653      * @return The number of consecutive idle cycles before the increment.
654      */
655     protected int getAndIncrementIdleCycles() {
656         return CONSECUTIVE_IDLE_CYCLES_UPDATER.getAndIncrement(this);
657     }
658 
659     /**
660      * Resets the counter for consecutive idle cycles to zero. This is typically called when the
661      * executor's utilization is no longer considered idle, breaking the streak.
662      */
663     protected void resetIdleCycles() {
664         CONSECUTIVE_IDLE_CYCLES_UPDATER.set(this, 0);
665     }
666 
667     /**
668      * Atomically increments the counter for consecutive monitor cycles where utilization was above the
669      * scale-up threshold. This is used by the auto-scaling monitor to track a sustained high load.
670      *
671      * @return The number of consecutive busy cycles before the increment.
672      */
673     protected int getAndIncrementBusyCycles() {
674         return CONSECUTIVE_BUSY_CYCLES_UPDATER.getAndIncrement(this);
675     }
676 
677     /**
678      * Resets the counter for consecutive busy cycles to zero. This is typically called when the
679      * executor's utilization is no longer considered busy, breaking the streak.
680      */
681     protected void resetBusyCycles() {
682         CONSECUTIVE_BUSY_CYCLES_UPDATER.set(this, 0);
683     }
684 
685     /**
686      * Returns {@code true} if this {@link SingleThreadEventExecutor} supports suspension.
687      */
688     protected boolean isSuspensionSupported() {
689         return supportSuspension;
690     }
691 
692     /**
693      * Runs the task-processing loop until {@link #confirmShutdown()} returns {@code true}.
694      *
695      * <p>Implementations <strong>must not let a {@link Throwable} thrown by a task escape this
696      * method</strong>: any uncaught {@link Throwable} terminates the executor (logged at {@code WARN}
697      * and surfaced via {@link #terminationFuture()}), at which point every {@code Channel}
698      * registered with this executor stops processing I/O and new task submissions are rejected.
699      * The supplied helpers - {@link #runAllTasks()}, {@link #runAllTasks(long)}, and
700      * {@link #safeExecute(Runnable)} - catch {@code Throwable} for you; custom loops built on
701      * {@link #pollTask()} or {@link #takeTask()} are responsible for wrapping each task
702      * invocation accordingly.
703      */
704     protected abstract void run();
705 
706     /**
707      * Do nothing, sub-classes may override
708      */
709     protected void cleanup() {
710         // NOOP
711     }
712 
713     protected void wakeup(boolean inEventLoop) {
714         if (!inEventLoop) {
715             // Use offer as we actually only need this to unblock the thread and if offer fails we do not care as there
716             // is already something in the queue.
717             taskQueue.offer(WAKEUP_TASK);
718         }
719     }
720 
721     @Override
722     public boolean inEventLoop(Thread thread) {
723         return thread == this.thread;
724     }
725 
726     /**
727      * Add a {@link Runnable} which will be executed on shutdown of this instance
728      */
729     public void addShutdownHook(final Runnable task) {
730         if (inEventLoop()) {
731             shutdownHooks.add(task);
732         } else {
733             execute(new Runnable() {
734                 @Override
735                 public void run() {
736                     shutdownHooks.add(task);
737                 }
738             });
739         }
740     }
741 
742     /**
743      * Remove a previous added {@link Runnable} as a shutdown hook
744      */
745     public void removeShutdownHook(final Runnable task) {
746         if (inEventLoop()) {
747             shutdownHooks.remove(task);
748         } else {
749             execute(new Runnable() {
750                 @Override
751                 public void run() {
752                     shutdownHooks.remove(task);
753                 }
754             });
755         }
756     }
757 
758     private boolean runShutdownHooks() {
759         boolean ran = false;
760         // Note shutdown hooks can add / remove shutdown hooks.
761         while (!shutdownHooks.isEmpty()) {
762             List<Runnable> copy = new ArrayList<Runnable>(shutdownHooks);
763             shutdownHooks.clear();
764             for (Runnable task: copy) {
765                 try {
766                     runTask(task);
767                 } catch (Throwable t) {
768                     logger.warn("Shutdown hook raised an exception.", t);
769                 } finally {
770                     ran = true;
771                 }
772             }
773         }
774 
775         if (ran) {
776             lastExecutionTime = getCurrentTimeNanos();
777         }
778 
779         return ran;
780     }
781 
782     private void shutdown0(long quietPeriod, long timeout, int shutdownState) {
783         if (isShuttingDown()) {
784             return;
785         }
786 
787         boolean inEventLoop = inEventLoop();
788         boolean wakeup;
789         int oldState;
790         for (;;) {
791             if (isShuttingDown()) {
792                 return;
793             }
794             int newState;
795             wakeup = true;
796             oldState = state;
797             if (inEventLoop) {
798                 newState = shutdownState;
799             } else {
800                 switch (oldState) {
801                     case ST_NOT_STARTED:
802                     case ST_STARTED:
803                     case ST_SUSPENDING:
804                     case ST_SUSPENDED:
805                         newState = shutdownState;
806                         break;
807                     default:
808                         newState = oldState;
809                         wakeup = false;
810                 }
811             }
812             if (STATE_UPDATER.compareAndSet(this, oldState, newState)) {
813                 break;
814             }
815         }
816         if (quietPeriod != -1) {
817             gracefulShutdownQuietPeriod = quietPeriod;
818         }
819         if (timeout != -1) {
820             gracefulShutdownTimeout = timeout;
821         }
822 
823         if (ensureThreadStarted(oldState)) {
824             return;
825         }
826 
827         if (wakeup) {
828             taskQueue.offer(WAKEUP_TASK);
829             if (!addTaskWakesUp) {
830                 wakeup(inEventLoop);
831             }
832         }
833     }
834 
835     @Override
836     public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
837         ObjectUtil.checkPositiveOrZero(quietPeriod, "quietPeriod");
838         if (timeout < quietPeriod) {
839             throw new IllegalArgumentException(
840                     "timeout: " + timeout + " (expected >= quietPeriod (" + quietPeriod + "))");
841         }
842         ObjectUtil.checkNotNull(unit, "unit");
843 
844         shutdown0(unit.toNanos(quietPeriod), unit.toNanos(timeout), ST_SHUTTING_DOWN);
845         return terminationFuture();
846     }
847 
848     @Override
849     public Future<?> terminationFuture() {
850         return terminationFuture;
851     }
852 
853     @Override
854     @Deprecated
855     public void shutdown() {
856         shutdown0(-1, -1, ST_SHUTDOWN);
857     }
858 
859     @Override
860     public boolean isShuttingDown() {
861         return state >= ST_SHUTTING_DOWN;
862     }
863 
864     @Override
865     public boolean isShutdown() {
866         return state >= ST_SHUTDOWN;
867     }
868 
869     @Override
870     public boolean isTerminated() {
871         return state == ST_TERMINATED;
872     }
873 
874     @Override
875     public boolean isSuspended() {
876         int currentState = state;
877         return currentState == ST_SUSPENDED || currentState == ST_SUSPENDING;
878     }
879 
880     @Override
881     public boolean trySuspend() {
882         if (supportSuspension) {
883             if (STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_SUSPENDING)) {
884                 wakeup(inEventLoop());
885                 return true;
886             } else if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_SUSPENDED)) {
887                 return true;
888             }
889             int currentState = state;
890             return currentState == ST_SUSPENDED || currentState == ST_SUSPENDING;
891         }
892         return false;
893     }
894 
895     /**
896      * Returns {@code true} if this {@link SingleThreadEventExecutor} can be suspended at the moment, {@code false}
897      * otherwise.
898      *
899      * @return  if suspension is possible at the moment.
900      */
901     protected boolean canSuspend() {
902         return canSuspend(state);
903     }
904 
905     /**
906      * Returns {@code true} if this {@link SingleThreadEventExecutor} can be suspended at the moment, {@code false}
907      * otherwise.
908      *
909      * Subclasses might override this method to add extra checks.
910      *
911      * @param   state   the current internal state of the {@link SingleThreadEventExecutor}.
912      * @return          if suspension is possible at the moment.
913      */
914     protected boolean canSuspend(int state) {
915         assert inEventLoop();
916         return supportSuspension && (state == ST_SUSPENDED || state == ST_SUSPENDING)
917                 && !hasTasks() && nextScheduledTaskDeadlineNanos() == -1;
918     }
919 
920     /**
921      * Confirm that the shutdown if the instance should be done now!
922      */
923     protected boolean confirmShutdown() {
924         if (!isShuttingDown()) {
925             return false;
926         }
927 
928         if (!inEventLoop()) {
929             throw new IllegalStateException("must be invoked from an event loop");
930         }
931 
932         cancelScheduledTasks();
933 
934         if (gracefulShutdownStartTime == 0) {
935             gracefulShutdownStartTime = getCurrentTimeNanos();
936         }
937 
938         if (runAllTasks() || runShutdownHooks()) {
939             if (isShutdown()) {
940                 // Executor shut down - no new tasks anymore.
941                 return true;
942             }
943 
944             // There were tasks in the queue. Wait a little bit more until no tasks are queued for the quiet period or
945             // terminate if the quiet period is 0.
946             // See https://github.com/netty/netty/issues/4241
947             if (gracefulShutdownQuietPeriod == 0) {
948                 return true;
949             }
950             taskQueue.offer(WAKEUP_TASK);
951             return false;
952         }
953 
954         final long nanoTime = getCurrentTimeNanos();
955 
956         if (isShutdown() || nanoTime - gracefulShutdownStartTime > gracefulShutdownTimeout) {
957             return true;
958         }
959 
960         if (nanoTime - lastExecutionTime <= gracefulShutdownQuietPeriod) {
961             // Check if any tasks were added to the queue every 100ms.
962             // TODO: Change the behavior of takeTask() so that it returns on timeout.
963             taskQueue.offer(WAKEUP_TASK);
964             try {
965                 Thread.sleep(100);
966             } catch (InterruptedException e) {
967                 // Ignore
968             }
969 
970             return false;
971         }
972 
973         // No tasks were added for last quiet period - hopefully safe to shut down.
974         // (Hopefully because we really cannot make a guarantee that there will be no execute() calls by a user.)
975         return true;
976     }
977 
978     @Override
979     public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
980         ObjectUtil.checkNotNull(unit, "unit");
981         if (inEventLoop()) {
982             throw new IllegalStateException("cannot await termination of the current thread");
983         }
984 
985         threadLock.await(timeout, unit);
986 
987         return isTerminated();
988     }
989 
990     @Override
991     public void execute(Runnable task) {
992         execute0(task);
993     }
994 
995     @Override
996     public void lazyExecute(Runnable task) {
997         lazyExecute0(task);
998     }
999 
1000     private void execute0(@Schedule Runnable task) {
1001         ObjectUtil.checkNotNull(task, "task");
1002         execute(task, wakesUpForTask(task));
1003     }
1004 
1005     private void lazyExecute0(@Schedule Runnable task) {
1006         execute(ObjectUtil.checkNotNull(task, "task"), false);
1007     }
1008 
1009     @Override
1010     void scheduleRemoveScheduled(final ScheduledFutureTask<?> task) {
1011         ObjectUtil.checkNotNull(task, "task");
1012         int currentState = state;
1013         if (supportSuspension && (currentState == ST_SUSPENDED || currentState == ST_SUSPENDING)) {
1014             // In the case of scheduling for removal we need to also ensure we will recover the "suspend" state
1015             // after it if it was set before. Otherwise we will always end up "unsuspending" things on cancellation
1016             // which is not optimal. This also covers the case where the executor is still ST_SUSPENDING (suspend
1017             // was requested but not confirmed yet): if the removal task races with doStartThread() re-engaging
1018             // the thread as ST_STARTED (see the ST_SUSPENDED/ST_STARTED CAS dance below), nobody would otherwise
1019             // ever re-request suspension and the thread would keep running forever waiting for new tasks.
1020             execute(new Runnable() {
1021                 @Override
1022                 public void run() {
1023                     task.run();
1024                     if (canSuspend(ST_SUSPENDED)) {
1025                         // Try suspending again to recover the state before we submitted the new task that will
1026                         // handle cancellation itself.
1027                         trySuspend();
1028                     }
1029                 }
1030             }, true);
1031         } else {
1032             // task will remove itself from scheduled task queue when it runs
1033             execute(task, false);
1034         }
1035     }
1036 
1037     private void execute(Runnable task, boolean immediate) {
1038         boolean inEventLoop = inEventLoop();
1039         addTask(task);
1040         if (!inEventLoop) {
1041             startThread();
1042             if (isShutdown()) {
1043                 boolean reject = false;
1044                 try {
1045                     if (removeTask(task)) {
1046                         reject = true;
1047                     }
1048                 } catch (UnsupportedOperationException e) {
1049                     // The task queue does not support removal so the best thing we can do is to just move on and
1050                     // hope we will be able to pick-up the task before its completely terminated.
1051                     // In worst case we will log on termination.
1052                 }
1053                 if (reject) {
1054                     reject();
1055                 }
1056             }
1057         }
1058 
1059         if (!addTaskWakesUp && immediate) {
1060             wakeup(inEventLoop);
1061         }
1062     }
1063 
1064     @Override
1065     public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
1066         throwIfInEventLoop("invokeAny");
1067         return super.invokeAny(tasks);
1068     }
1069 
1070     @Override
1071     public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
1072             throws InterruptedException, ExecutionException, TimeoutException {
1073         throwIfInEventLoop("invokeAny");
1074         return super.invokeAny(tasks, timeout, unit);
1075     }
1076 
1077     @Override
1078     public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
1079             throws InterruptedException {
1080         throwIfInEventLoop("invokeAll");
1081         return super.invokeAll(tasks);
1082     }
1083 
1084     @Override
1085     public <T> List<java.util.concurrent.Future<T>> invokeAll(
1086             Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
1087         throwIfInEventLoop("invokeAll");
1088         return super.invokeAll(tasks, timeout, unit);
1089     }
1090 
1091     private void throwIfInEventLoop(String method) {
1092         if (inEventLoop()) {
1093             throw new RejectedExecutionException("Calling " + method + " from within the EventLoop is not allowed");
1094         }
1095     }
1096 
1097     /**
1098      * Returns the {@link ThreadProperties} of the {@link Thread} that powers the {@link SingleThreadEventExecutor}.
1099      * If the {@link SingleThreadEventExecutor} is not started yet, this operation will start it and block until
1100      * it is fully started.
1101      */
1102     public final ThreadProperties threadProperties() {
1103         ThreadProperties threadProperties = this.threadProperties;
1104         if (threadProperties == null) {
1105             Thread thread = this.thread;
1106             if (thread == null) {
1107                 assert !inEventLoop();
1108                 submit(NOOP_TASK).syncUninterruptibly();
1109                 thread = this.thread;
1110                 assert thread != null;
1111             }
1112 
1113             threadProperties = new DefaultThreadProperties(thread);
1114             if (!PROPERTIES_UPDATER.compareAndSet(this, null, threadProperties)) {
1115                 threadProperties = this.threadProperties;
1116             }
1117         }
1118 
1119         return threadProperties;
1120     }
1121 
1122     /**
1123      * @deprecated override {@link SingleThreadEventExecutor#wakesUpForTask} to re-create this behaviour
1124      */
1125     @Deprecated
1126     protected interface NonWakeupRunnable extends LazyRunnable { }
1127 
1128     /**
1129      * Can be overridden to control which tasks require waking the {@link EventExecutor} thread
1130      * if it is waiting so that they can be run immediately.
1131      */
1132     protected boolean wakesUpForTask(Runnable task) {
1133         return true;
1134     }
1135 
1136     protected static void reject() {
1137         throw new RejectedExecutionException("event executor terminated");
1138     }
1139 
1140     /**
1141      * Offers the task to the associated {@link RejectedExecutionHandler}.
1142      *
1143      * @param task to reject.
1144      */
1145     protected final void reject(Runnable task) {
1146         rejectedExecutionHandler.rejected(task, this);
1147     }
1148 
1149     // ScheduledExecutorService implementation
1150 
1151     private static final long SCHEDULE_PURGE_INTERVAL = TimeUnit.SECONDS.toNanos(1);
1152 
1153     private void startThread() {
1154         int currentState = state;
1155         while (currentState == ST_NOT_STARTED || currentState == ST_SUSPENDED) {
1156             if (STATE_UPDATER.compareAndSet(this, currentState, ST_STARTED)) {
1157                 resetIdleCycles();
1158                 resetBusyCycles();
1159                 boolean success = false;
1160                 try {
1161                     doStartThread();
1162                     success = true;
1163                 } finally {
1164                     if (!success) {
1165                         STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
1166                     }
1167                 }
1168                 break;
1169             }
1170             // The state changed after we read it. If trySuspend() moved it from ST_NOT_STARTED to ST_SUSPENDED no
1171             // thread was started, so we need to try again. Otherwise the task that was just added might never run.
1172             // This may deliberately un-suspend an executor that trySuspend() has just suspended, which is the same
1173             // thing execute() does for an executor that is already suspended. We only loop again if another thread
1174             // changed the state in between, so this can't spin.
1175             currentState = state;
1176         }
1177     }
1178 
1179     private boolean ensureThreadStarted(int oldState) {
1180         if (oldState == ST_NOT_STARTED || oldState == ST_SUSPENDED) {
1181             try {
1182                 doStartThread();
1183             } catch (Throwable cause) {
1184                 STATE_UPDATER.set(this, ST_TERMINATED);
1185                 terminationFuture.tryFailure(cause);
1186 
1187                 if (!(cause instanceof Exception)) {
1188                     // Also rethrow as it may be an OOME for example
1189                     PlatformDependent.throwException(cause);
1190                 }
1191                 return true;
1192             }
1193         }
1194         return false;
1195     }
1196 
1197     private void doStartThread() {
1198         executor.execute(new Runnable() {
1199             @Override
1200             public void run() {
1201                 processingLock.lock();
1202                 assert thread == null;
1203                 thread = Thread.currentThread();
1204                 if (interrupted) {
1205                     thread.interrupt();
1206                     interrupted = false;
1207                 }
1208                 boolean success = false;
1209                 Throwable unexpectedException = null;
1210                 updateLastExecutionTime();
1211                 boolean suspend = false;
1212                 try {
1213                     for (;;) {
1214                         SingleThreadEventExecutor.this.run();
1215                         success = true;
1216 
1217                         int currentState = state;
1218                         if (canSuspend(currentState)) {
1219                             // currentState might already be ST_SUSPENDED here (we can loop back around with the
1220                             // state still ST_SUSPENDED via the ST_SUSPENDING/ST_SUSPENDED branch below), so we must
1221                             // CAS from currentState and not hardcode ST_SUSPENDING as the expected value, or the CAS
1222                             // would spuriously "fail" forever and livelock this thread instead of finishing suspend.
1223                             if (!STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this,
1224                                     currentState, ST_SUSPENDED)) {
1225                                 // Try again as the CAS failed.
1226                                 continue;
1227                             }
1228 
1229                             if (!canSuspend(ST_SUSPENDED) && STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this,
1230                                         ST_SUSPENDED, ST_STARTED)) {
1231                                 // Seems like there was something added to the task queue again in the meantime but we
1232                                 // were able to re-engage this thread as the event loop thread.
1233                                 continue;
1234                             }
1235                             suspend = true;
1236                         } else if (currentState == ST_SUSPENDING || currentState == ST_SUSPENDED) {
1237                             // We were trying to suspend (or just did) but something raced in - e.g. a scheduled
1238                             // task being cancelled and re-submitted itself for removal via scheduleRemoveScheduled()
1239                             // - so we can no longer suspend right now. No shutdown was requested, so this must not
1240                             // be treated as if run() returned without confirming shutdown; just go around the loop
1241                             // again so run() can pick up whatever raced in.
1242                             continue;
1243                         }
1244                         break;
1245                     }
1246                 } catch (Throwable t) {
1247                     unexpectedException = t;
1248                     logger.warn("Unexpected exception from an event executor: ", t);
1249                 } finally {
1250                     boolean shutdown = !suspend;
1251                     if (shutdown) {
1252                         for (;;) {
1253                             // We are re-fetching the state as it might have been shutdown in the meantime.
1254                             int oldState = state;
1255                             if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
1256                                     SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
1257                                 break;
1258                             }
1259                         }
1260                         if (success && gracefulShutdownStartTime == 0) {
1261                             // Check if confirmShutdown() was called at the end of the loop.
1262                             if (logger.isErrorEnabled()) {
1263                                 logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
1264                                         SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must " +
1265                                         "be called before run() implementation terminates.");
1266                             }
1267                         }
1268                     }
1269 
1270                     try {
1271                         if (shutdown) {
1272                             // Run all remaining tasks and shutdown hooks. At this point the event loop
1273                             // is in ST_SHUTTING_DOWN state still accepting tasks which is needed for
1274                             // graceful shutdown with quietPeriod.
1275                             for (;;) {
1276                                 if (confirmShutdown()) {
1277                                     break;
1278                                 }
1279                             }
1280 
1281                             // Now we want to make sure no more tasks can be added from this point. This is
1282                             // achieved by switching the state. Any new tasks beyond this point will be rejected.
1283                             for (;;) {
1284                                 int currentState = state;
1285                                 if (currentState >= ST_SHUTDOWN || STATE_UPDATER.compareAndSet(
1286                                         SingleThreadEventExecutor.this, currentState, ST_SHUTDOWN)) {
1287                                     break;
1288                                 }
1289                             }
1290 
1291                             // We have the final set of tasks in the queue now, no more can be added, run all remaining.
1292                             // No need to loop here, this is the final pass.
1293                             confirmShutdown();
1294                         }
1295                     } finally {
1296                         try {
1297                             if (shutdown) {
1298                                 try {
1299                                     cleanup();
1300                                 } finally {
1301                                     // Lets remove all FastThreadLocals for the Thread as we are about to terminate and
1302                                     // notify the future. The user may block on the future and once it unblocks the JVM
1303                                     // may terminate and start unloading classes.
1304                                     // See https://github.com/netty/netty/issues/6596.
1305                                     FastThreadLocal.removeAll();
1306 
1307                                     STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
1308                                     threadLock.countDown();
1309                                     int numUserTasks = drainTasks();
1310                                     if (numUserTasks > 0 && logger.isWarnEnabled()) {
1311                                         logger.warn("An event executor terminated with " +
1312                                                 "non-empty task queue (" + numUserTasks + ')');
1313                                     }
1314                                     if (unexpectedException == null) {
1315                                         terminationFuture.setSuccess(null);
1316                                     } else {
1317                                         terminationFuture.setFailure(unexpectedException);
1318                                     }
1319                                 }
1320                             } else {
1321                                 // Lets remove all FastThreadLocals for the Thread as we are about to terminate it.
1322                                 FastThreadLocal.removeAll();
1323 
1324                                 // Reset the stored threadProperties in case of suspension.
1325                                 threadProperties = null;
1326                             }
1327                         } finally {
1328                             thread = null;
1329                             // Let the next thread take over if needed.
1330                             processingLock.unlock();
1331                         }
1332                     }
1333                 }
1334             }
1335         });
1336     }
1337 
1338     final int drainTasks() {
1339         int numTasks = 0;
1340         for (;;) {
1341             Runnable runnable = taskQueue.poll();
1342             if (runnable == null) {
1343                 break;
1344             }
1345             // WAKEUP_TASK should be just discarded as these are added internally.
1346             // The important bit is that we not have any user tasks left.
1347             if (WAKEUP_TASK != runnable) {
1348                 numTasks++;
1349             }
1350         }
1351         return numTasks;
1352     }
1353 
1354     private static final class DefaultThreadProperties implements ThreadProperties {
1355         private final Thread t;
1356 
1357         DefaultThreadProperties(Thread t) {
1358             this.t = t;
1359         }
1360 
1361         @Override
1362         public State state() {
1363             return t.getState();
1364         }
1365 
1366         @Override
1367         public int priority() {
1368             return t.getPriority();
1369         }
1370 
1371         @Override
1372         public boolean isInterrupted() {
1373             return t.isInterrupted();
1374         }
1375 
1376         @Override
1377         public boolean isDaemon() {
1378             return t.isDaemon();
1379         }
1380 
1381         @Override
1382         public String name() {
1383             return t.getName();
1384         }
1385 
1386         @Override
1387         public long id() {
1388             return t.getId();
1389         }
1390 
1391         @Override
1392         public StackTraceElement[] stackTrace() {
1393             return t.getStackTrace();
1394         }
1395 
1396         @Override
1397         public boolean isAlive() {
1398             return t.isAlive();
1399         }
1400     }
1401 }