use of net.robinfriedli.aiode.concurrent.CommandExecutionQueueManager in project aiode by robinfriedli.
the class CleanDbCommand method runAdmin.
@Override
public void runAdmin() {
Aiode.shutdownListeners();
try {
if (!argumentSet("silent")) {
EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setTitle("Scheduled cleanup");
embedBuilder.setDescription("Aiode is suspending for a few seconds to clean the database.");
sendToActiveGuilds(embedBuilder.build());
}
CommandExecutionQueueManager executionQueueManager = Aiode.get().getExecutionQueueManager();
Command command = this;
Thread cleanupThread = new Thread(() -> {
ThreadContext.Current.install(command);
try {
try {
executionQueueManager.joinAll(60000);
} catch (InterruptedException e) {
return;
}
StaticSessionProvider.consumeSession(this::doClean);
} finally {
Aiode.registerListeners();
}
});
cleanupThread.setName("database-cleanup-thread-" + command.getContext().toString());
Thread.UncaughtExceptionHandler commandExecutionExceptionHandler = Thread.currentThread().getUncaughtExceptionHandler();
if (commandExecutionExceptionHandler != null) {
cleanupThread.setUncaughtExceptionHandler(commandExecutionExceptionHandler);
}
cleanupThread.start();
} catch (Throwable e) {
Aiode.registerListeners();
throw e;
}
}
use of net.robinfriedli.aiode.concurrent.CommandExecutionQueueManager in project aiode by robinfriedli.
the class Aiode method shutdown.
/**
* Shutdown the bot waiting for pending commands and rest actions. Note that #shutdownListeners usually should get
* called first, as all ThreadExecutionQueues will close, meaning the CommandListener will fail. You should also be
* careful to not call this method from within a CommandExecutionThread executed by a ThreadExecutionQueue, as this
* method waits for those threads to finish, causing a deadlock.
*
* @param millisToWait time to wait for pending actions to complete in milliseconds, after this time the bot will
* quit either way
* @param messagesToAwait list of shutdown notification messages that are being send that should first be awaited
*/
public static void shutdown(int millisToWait, @Nullable List<CompletableFuture<Message>> messagesToAwait) {
Aiode aiode = get();
shuttingDown = true;
LOGGER.info("Shutting down");
ShardManager shardManager = aiode.getShardManager();
CommandExecutionQueueManager executionQueueManager = aiode.getExecutionQueueManager();
// use a daemon thread to shutdown the bot after the provided time has elapsed without keeping the application
// running if all live threads terminate earlier
Thread forceShutdownThread = new Thread(() -> {
try {
Thread.sleep(millisToWait);
} catch (InterruptedException e) {
return;
}
System.exit(0);
});
forceShutdownThread.setDaemon(true);
forceShutdownThread.setName("force shutdown thread");
forceShutdownThread.start();
executionQueueManager.closeAll();
executionQueueManager.cancelEnqueued();
try {
LOGGER.info("Waiting for commands to finish");
executionQueueManager.joinAll(0);
if (messagesToAwait != null && !messagesToAwait.isEmpty()) {
for (CompletableFuture<Message> futureMessage : messagesToAwait) {
try {
futureMessage.get();
} catch (InterruptedException e) {
break;
} catch (ExecutionException e) {
continue;
}
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
forceShutdownThread.interrupt();
return;
}
LOGGER.info("Shutting down registered shutdownables");
SHUTDOWNABLES.forEach(shutdownable -> shutdownable.shutdown(millisToWait));
LOGGER.info("Shutting down JDA");
shardManager.shutdown();
LOGGER.info("Shutting down hibernate SessionFactory");
aiode.getSessionFactory().close();
LOGGER.info("Close spring ApplicationContext");
aiode.getSpringBootContext().close();
}
use of net.robinfriedli.aiode.concurrent.CommandExecutionQueueManager in project aiode by robinfriedli.
the class AbortCommand method doRun.
@Override
public void doRun() {
CommandExecutionTask commandExecutionTask = getTask();
Thread abortThread = new Thread(() -> {
if (commandExecutionTask != null) {
try {
commandExecutionTask.await();
} catch (InterruptedException ignored) {
}
}
CommandExecutionQueueManager executionQueueManager = Aiode.get().getExecutionQueueManager();
GuildContext guildContext = getContext().getGuildContext();
ThreadExecutionQueue executionQueue = executionQueueManager.getForGuild(getContext().getGuild());
PooledTrackLoadingExecutor pooledTrackLoadingExecutor = guildContext.getPooledTrackLoadingExecutor();
ReplaceableTrackLoadingExecutor replaceableTrackLoadingExecutor = guildContext.getReplaceableTrackLoadingExecutor();
if (executionQueue.isIdle() && pooledTrackLoadingExecutor.isIdle() && replaceableTrackLoadingExecutor.isIdle()) {
EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setDescription("No commands are currently running");
getMessageService().sendTemporary(embedBuilder, getContext().getChannel());
setFailed(true);
} else {
executionQueue.abortAll();
pooledTrackLoadingExecutor.abortAll();
replaceableTrackLoadingExecutor.abort();
sendSuccess("Sent all currently running commands an interrupt signal and cancelled queued commands.");
}
});
abortThread.setName("aiode abort thread");
abortThread.setUncaughtExceptionHandler(new LoggingUncaughtExceptionHandler());
abortThread.start();
}
use of net.robinfriedli.aiode.concurrent.CommandExecutionQueueManager in project aiode by robinfriedli.
the class ClearAbandonedGuildContextsTask method run.
@Override
protected void run(JobExecutionContext jobExecutionContext) {
Aiode aiode = Aiode.get();
ShardManager shardManager = aiode.getShardManager();
GuildManager guildManager = aiode.getGuildManager();
CommandExecutionQueueManager executionQueueManager = aiode.getExecutionQueueManager();
int removedGuilds = 0;
for (GuildContext guildContext : guildManager.getGuildContexts()) {
Guild guild = guildContext.retrieveGuild();
if (guild == null || shardManager.getGuildById(guild.getIdLong()) == null) {
guildManager.removeGuild(guild);
executionQueueManager.removeGuild(guild);
++removedGuilds;
}
}
if (removedGuilds > 0) {
LoggerFactory.getLogger(getClass()).info("Destroyed context for " + removedGuilds + " missing guilds");
}
}
use of net.robinfriedli.aiode.concurrent.CommandExecutionQueueManager in project aiode by robinfriedli.
the class StartupListener method onReady.
@Override
public void onReady(@NotNull ReadyEvent event) {
STARTUP_TASK_EXECUTOR.execute(() -> {
JDA jda = event.getJDA();
if (discordBotListAPI != null) {
try {
JDA.ShardInfo shardInfo = jda.getShardInfo();
discordBotListAPI.setStats(shardInfo.getShardId(), shardInfo.getShardTotal(), (int) jda.getGuildCache().size());
} catch (Exception e) {
logger.error("Exception setting discordBotListAPI stats", e);
}
}
CommandExecutionQueueManager executionQueueManager = aiode.getExecutionQueueManager();
GuildManager guildManager = aiode.getGuildManager();
StaticSessionProvider.consumeSession(session -> {
// setup current thread session and handle all guilds within one session instead of opening a new session for each
for (Guild guild : jda.getGuilds()) {
executionQueueManager.addGuild(guild);
guildManager.addGuild(guild);
}
});
for (StartupTaskContribution element : startupTaskContributions) {
if (element.getAttribute("runForEachShard").getBool()) {
try {
element.instantiate().runTask(jda);
} catch (Exception e) {
String msg = String.format("Startup task %s has thrown an exception for shard %s", element.getAttribute("implementation").getValue(), jda);
logger.error(msg, e);
}
}
}
});
}
Aggregations