From b47db9842048728b52a8f2828d067a59b5f02a8e Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Mon, 27 Jul 2020 01:02:50 +0200 Subject: [PATCH] Close async tasks properly when shutting down --- .../bukkit/managers/BukkitTaskManager.java | 13 ++-- .../bungee/managers/BungeeTaskManager.java | 13 ++-- .../common/managers/AbstractTaskManager.java | 66 +++++++++++++++---- .../common/tasks/PluginWatcherTask.java | 14 +++- 4 files changed, 83 insertions(+), 23 deletions(-) diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitTaskManager.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitTaskManager.java index 82e043d..405556a 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitTaskManager.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitTaskManager.java @@ -12,12 +12,17 @@ public class BukkitTaskManager extends AbstractTaskManager { } @Override - public void runTask(Runnable runnable) { - addTask(Bukkit.getScheduler().runTask(ServerUtils.getInstance(), runnable)); + protected BukkitTask runTaskImpl(Runnable runnable) { + return Bukkit.getScheduler().runTask(ServerUtils.getInstance(), runnable); } @Override - public void runTaskAsynchronously(Runnable runnable) { - addTask(Bukkit.getScheduler().runTaskAsynchronously(ServerUtils.getInstance(), runnable)); + protected BukkitTask runTaskAsynchronouslyImpl(Runnable runnable) { + return Bukkit.getScheduler().runTaskAsynchronously(ServerUtils.getInstance(), runnable); + } + + @Override + public void cancelTask(BukkitTask task) { + task.cancel(); } } diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeeTaskManager.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeeTaskManager.java index 0888c31..dc06c6c 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeeTaskManager.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeeTaskManager.java @@ -12,12 +12,17 @@ public class BungeeTaskManager extends AbstractTaskManager { } @Override - public void runTask(Runnable runnable) { - runTaskAsynchronously(runnable); + protected ScheduledTask runTaskImpl(Runnable runnable) { + return runTaskAsynchronously(runnable); } @Override - public void runTaskAsynchronously(Runnable runnable) { - ProxyServer.getInstance().getScheduler().runAsync(ServerUtils.getInstance(), runnable); + protected ScheduledTask runTaskAsynchronouslyImpl(Runnable runnable) { + return ProxyServer.getInstance().getScheduler().runAsync(ServerUtils.getInstance(), runnable); + } + + @Override + public void cancelTask(ScheduledTask task) { + task.cancel(); } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractTaskManager.java b/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractTaskManager.java index 2245f5e..a7e2561 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractTaskManager.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractTaskManager.java @@ -12,7 +12,7 @@ public abstract class AbstractTaskManager { private final List serverTasks; private final Consumer taskCloser; - private final Map tasks; + private final Map tasks; /** * Constructs a new TaskManager with a consumer which closes a task. @@ -24,31 +24,56 @@ public abstract class AbstractTaskManager { this.tasks = new HashMap<>(); } - public abstract void runTask(Runnable runnable); + protected abstract T runTaskImpl(Runnable runnable); - public void runTask(String key, AbstractTask task) { - tasks.put(key, task); - runTask(task); + public T runTask(Runnable runnable) { + return addTask(runTaskImpl(runnable)); } - public abstract void runTaskAsynchronously(Runnable runnable); - - public void runTaskAsynchronously(String key, AbstractTask task) { - tasks.put(key, task); - runTaskAsynchronously(task); + /** + * Associates a synchronous task with a key which can be cancelled later by that key. + * @param key The key of the task. + * @param abstractTask The AbstractTask. + * @return The implementation-specific scheduled task. + */ + public T runTask(String key, AbstractTask abstractTask) { + T task = runTask(abstractTask); + tasks.put(key, new RunningTask(task, abstractTask)); + return task; } - public void addTask(T task) { + protected abstract T runTaskAsynchronouslyImpl(Runnable runnable); + + public T runTaskAsynchronously(Runnable runnable) { + return addTask(runTaskAsynchronouslyImpl(runnable)); + } + + /** + * Associates an asynchronous task with a key which can be cancelled later by that key. + * @param key The key of the task. + * @param abstractTask The AbstractTask. + * @return The implementation-specific scheduled task. + */ + public T runTaskAsynchronously(String key, AbstractTask abstractTask) { + T task = runTaskAsynchronously(abstractTask); + tasks.put(key, new RunningTask(task, abstractTask)); + return task; + } + + private T addTask(T task) { serverTasks.add(task); + return task; } + public abstract void cancelTask(T task); + /** * Cancels a single task by key. * @param key The key of the task. * @return Whether or not the task existed. */ public boolean cancelTask(String key) { - AbstractTask task = tasks.remove(key); + RunningTask task = tasks.remove(key); if (task == null) return false; task.cancel(); return true; @@ -58,7 +83,7 @@ public abstract class AbstractTaskManager { * Cancels all tasks. */ public void cancelAllTasks() { - for (AbstractTask task : tasks.values()) { + for (RunningTask task : tasks.values()) { task.cancel(); } tasks.clear(); @@ -68,4 +93,19 @@ public abstract class AbstractTaskManager { } serverTasks.clear(); } + + private final class RunningTask { + private final T task; + private final AbstractTask abstractTask; + + private RunningTask(T task, AbstractTask abstractTask) { + this.task = task; + this.abstractTask = abstractTask; + } + + public void cancel() { + cancelTask(task); + abstractTask.cancel(); + } + } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java index 2e7c345..0eb2cf7 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java @@ -4,6 +4,7 @@ import com.sun.nio.file.SensitivityWatchEventModifier; import java.io.File; import java.io.IOException; +import java.nio.file.ClosedWatchServiceException; import java.nio.file.FileSystems; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; @@ -41,6 +42,8 @@ public class PluginWatcherTask extends AbstractTask { private File file; private final AtomicBoolean run; + private WatchService watchService; + /** * Constructs a new PluginWatcherTask for the specified plugin. * @param pluginName The name of the plugin. @@ -55,6 +58,8 @@ public class PluginWatcherTask extends AbstractTask { @Override public void run() { try (WatchService watchService = FileSystems.getDefault().newWatchService()) { + this.watchService = watchService; + File folder = pluginManager.getPluginsFolder(); folder.toPath().register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH); @@ -76,9 +81,9 @@ public class PluginWatcherTask extends AbstractTask { break; } } - } catch (IOException ex) { + } catch (IOException | InterruptedException ex) { ex.printStackTrace(); - } catch (InterruptedException ignored) { + } catch (ClosedWatchServiceException ignored) { // } } @@ -93,5 +98,10 @@ public class PluginWatcherTask extends AbstractTask { @Override public void cancel() { run.set(false); + try { + watchService.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } } }