private void doStop(CompletableFuture<Void> future,
                            @Nullable ExecutorService gracefulShutdownExecutor) {
            // Graceful shutdown is over. Terminate the temporary executor we created at stop0(future).
            if (gracefulShutdownExecutor != null) {
                gracefulShutdownExecutor.shutdownNow();
            }

            // Close all server sockets.
            final Set<Channel> serverChannels = ImmutableSet.copyOf(Server.this.serverChannels);
            ChannelUtil.close(serverChannels).handle((unused1, unused2) -> {
                // All server ports have been closed.
                lock.lock();
                try {
                    activePorts.clear();
                } finally {
                    lock.unlock();
                }

                shutdownServerHandlers().handle((unused3, unused4) -> {
                    // Close all accepted sockets.
                    ChannelUtil.close(connectionLimitingHandler.children()).handle((unused5, unused6) -> {
                        // Shut down the worker group if necessary.
                        final Future<?> workerShutdownFuture;
                        if (config.shutdownWorkerGroupOnStop()) {
                            workerShutdownFuture = config.workerGroup().shutdownGracefully();
                        } else {
                            workerShutdownFuture = ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
                        }

                        workerShutdownFuture.addListener(unused7 -> {
                            final Set<EventLoopGroup> bossGroups =
                                    Server.this.serverChannels.stream()
                                                              .map(ch -> ch.eventLoop().parent())
                                                              .collect(toImmutableSet());

                            // If started to shutdown before initializing a boss group,
                            // complete the future immediately.
                            if (bossGroups.isEmpty()) {
                                finishDoStop(future);
                                return;
                            }

                            // Shut down all boss groups and wait until they are terminated.
                            final AtomicInteger remainingBossGroups = new AtomicInteger(bossGroups.size());
                            bossGroups.forEach(bossGroup -> {
                                bossGroup.shutdownGracefully();
                                bossGroup.terminationFuture().addListener(unused8 -> {
                                    if (remainingBossGroups.decrementAndGet() != 0) {
                                        // There are more boss groups to terminate.
                                        return;
                                    }

                                    // Boss groups have been terminated completely.
                                    finishDoStop(future);
                                });
                            });
                        });

                        return null;
                    });
                    return null;
                });

                return null;
            });
        }
Closes all channels and terminates all event loops.
<ol>
  <li>Closes all server channels so that we don't accept any more incoming connections.</li>
  <li>Closes all accepted channels.</li>
  <li>Shuts down the worker group if necessary.</li>
  <li>Shuts down the boss groups.</li>
</ol>
Note that we terminate the boss groups lastly so that the JVM does not terminate itself
even if other threads are daemon, because boss group threads are always non-daemon.
core/src/main/java/com/linecorp/armeria/server/Server.java