Wait for thread executors to shut down

This commit is contained in:
KingRainbow44 2023-06-01 18:17:48 -04:00
parent 9dd514a73b
commit 8692405363
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
5 changed files with 75 additions and 35 deletions

View File

@ -1,14 +1,11 @@
package emu.grasscutter; package emu.grasscutter;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
import ch.qos.logback.classic.*; import ch.qos.logback.classic.*;
import emu.grasscutter.auth.*; import emu.grasscutter.auth.*;
import emu.grasscutter.command.*; import emu.grasscutter.command.*;
import emu.grasscutter.config.ConfigContainer; import emu.grasscutter.config.ConfigContainer;
import emu.grasscutter.data.ResourceLoader; import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.database.DatabaseManager; import emu.grasscutter.database.*;
import emu.grasscutter.plugin.PluginManager; import emu.grasscutter.plugin.PluginManager;
import emu.grasscutter.plugin.api.ServerHelper; import emu.grasscutter.plugin.api.ServerHelper;
import emu.grasscutter.server.dispatch.DispatchServer; import emu.grasscutter.server.dispatch.DispatchServer;
@ -21,16 +18,20 @@ import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.*; import emu.grasscutter.utils.*;
import emu.grasscutter.utils.lang.Language; import emu.grasscutter.utils.lang.Language;
import io.netty.util.concurrent.FastThreadLocalThread; import io.netty.util.concurrent.FastThreadLocalThread;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import javax.annotation.Nullable;
import lombok.*; import lombok.*;
import org.jline.reader.*; import org.jline.reader.*;
import org.jline.terminal.*; import org.jline.terminal.*;
import org.reflections.Reflections; import org.reflections.Reflections;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
public final class Grasscutter { public final class Grasscutter {
public static final File configFile = new File("./config.json"); public static final File configFile = new File("./config.json");
public static final Reflections reflector = new Reflections("emu.grasscutter"); public static final Reflections reflector = new Reflections("emu.grasscutter");
@ -183,6 +184,22 @@ public final class Grasscutter {
private static void onShutdown() { private static void onShutdown() {
// Disable all plugins. // Disable all plugins.
if (pluginManager != null) pluginManager.disablePlugins(); if (pluginManager != null) pluginManager.disablePlugins();
try {
// Wait for Grasscutter's thread pool to finish.
var executor = Grasscutter.getThreadPool();
executor.shutdown();
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
// Wait for database operations to finish.
var dbExecutor = DatabaseHelper.getEventExecutor();
dbExecutor.shutdown();
if (!dbExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
dbExecutor.shutdownNow();
}
} catch (InterruptedException ignored) { }
} }
/* /*

View File

@ -1,12 +1,8 @@
package emu.grasscutter.database; package emu.grasscutter.database;
import static com.mongodb.client.model.Filters.eq; import dev.morphia.query.*;
import dev.morphia.query.FindOptions;
import dev.morphia.query.Sort;
import dev.morphia.query.experimental.filters.Filters; import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.GameConstants; import emu.grasscutter.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.achievement.Achievements; import emu.grasscutter.game.achievement.Achievements;
import emu.grasscutter.game.activity.PlayerActivityData; import emu.grasscutter.game.activity.PlayerActivityData;
@ -23,12 +19,16 @@ import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.world.SceneGroupInstance; import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.utils.objects.Returnable; import emu.grasscutter.utils.objects.Returnable;
import io.netty.util.concurrent.FastThreadLocalThread; import io.netty.util.concurrent.FastThreadLocalThread;
import lombok.Getter;
import java.util.List; import java.util.List;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.stream.Stream; import java.util.stream.Stream;
import static com.mongodb.client.model.Filters.eq;
public final class DatabaseHelper { public final class DatabaseHelper {
private static final ExecutorService eventExecutor = @Getter private static final ExecutorService eventExecutor =
new ThreadPoolExecutor( new ThreadPoolExecutor(
6, 6,
6, 6,

View File

@ -1,8 +1,5 @@
package emu.grasscutter.server.game; package emu.grasscutter.server.game;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.lang.Language.translate;
import emu.grasscutter.*; import emu.grasscutter.*;
import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
@ -32,14 +29,19 @@ import emu.grasscutter.server.event.internal.*;
import emu.grasscutter.server.event.types.ServerEvent; import emu.grasscutter.server.event.types.ServerEvent;
import emu.grasscutter.server.scheduler.ServerTaskScheduler; import emu.grasscutter.server.scheduler.ServerTaskScheduler;
import emu.grasscutter.task.TaskMap; import emu.grasscutter.task.TaskMap;
import java.net.*; import emu.grasscutter.utils.Utils;
import java.time.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import kcp.highway.*; import kcp.highway.*;
import lombok.*; import lombok.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.net.*;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.lang.Language.translate;
@Getter @Getter
public final class GameServer extends KcpServer implements Iterable<Player> { public final class GameServer extends KcpServer implements Iterable<Player> {
// Game server base // Game server base
@ -326,16 +328,27 @@ public final class GameServer extends KcpServer implements Iterable<Player> {
} }
public void onServerShutdown() { public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); var event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call(); event.call();
this.getPlayers() this.getPlayers()
.forEach( .forEach(
(uid, player) -> { (uid, player) -> player.getSession().close());
player.getSession().close();
});
this.getWorlds().forEach(World::save); this.getWorlds().forEach(World::save);
Utils.sleep(1000L); // Wait 1 second for operations to finish.
try {
var threadPool = GameSessionManager.getLogicThread();
// Shutdown network thread.
threadPool.shutdownGracefully();
// Wait for the network thread to finish.
if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
Grasscutter.getLogger().error("Logic thread did not terminate!");
}
} catch (InterruptedException ignored) { }
} }
@NotNull @Override @NotNull @Override

View File

@ -2,16 +2,16 @@ package emu.grasscutter.server.game;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.*;
import io.netty.buffer.Unpooled;
import io.netty.channel.DefaultEventLoop; import io.netty.channel.DefaultEventLoop;
import kcp.highway.*;
import lombok.Getter;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import kcp.highway.KcpListener;
import kcp.highway.Ukcp;
public class GameSessionManager { public class GameSessionManager {
private static final DefaultEventLoop logicThread = new DefaultEventLoop(); @Getter private static final DefaultEventLoop logicThread = new DefaultEventLoop();
private static final ConcurrentHashMap<Ukcp, GameSession> sessions = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<Ukcp, GameSession> sessions = new ConcurrentHashMap<>();
private static final KcpListener listener = private static final KcpListener listener =
new KcpListener() { new KcpListener() {

View File

@ -504,9 +504,7 @@ public final class Utils {
* @return A list of all fields in the class. * @return A list of all fields in the class.
*/ */
public static List<Field> getAllFields(Class<?> type) { public static List<Field> getAllFields(Class<?> type) {
var fields = new LinkedList<>( var fields = new LinkedList<>(Arrays.asList(type.getDeclaredFields()));
Arrays.asList(type.getDeclaredFields())
);
// Check for superclasses. // Check for superclasses.
if (type.getSuperclass() != null) { if (type.getSuperclass() != null) {
@ -515,4 +513,16 @@ public final class Utils {
return fields; return fields;
} }
/**
* Sleeps the current thread without an exception.
*
* @param millis The amount of milliseconds to sleep.
*/
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ignored) {
}
}
} }