Skip to content

Commit a451254

Browse files
kopporcalixtus
andauthored
parallel-shutdown (#13493)
* Initial executor-based shutdown * Add tracing on successful close * Streamline shutdown * Fix checkstyle * Fix naming Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --------- Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com>
1 parent 8732e40 commit a451254

File tree

4 files changed

+100
-57
lines changed

4 files changed

+100
-57
lines changed

jabgui/src/main/java/org/jabref/gui/JabRefGUI.java

Lines changed: 84 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.List;
44
import java.util.Optional;
5+
import java.util.concurrent.ExecutorService;
6+
import java.util.concurrent.Executors;
57

68
import javax.swing.undo.UndoManager;
79

@@ -418,53 +420,90 @@ public void startBackgroundTasks() {
418420

419421
@Override
420422
public void stop() {
421-
LOGGER.trace("Closing AI service");
422-
try {
423-
aiService.close();
424-
} catch (Exception e) {
425-
LOGGER.error("Unable to close AI service", e);
423+
LOGGER.trace("Stopping JabRef GUI");
424+
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
425+
LOGGER.trace("Stopping JabRef GUI using a virtual thread executor");
426+
427+
// Shutdown everything in parallel to prevent causing non-shutdown of something in case of issues
428+
executor.submit(() -> {
429+
LOGGER.trace("Closing citations and relations search service");
430+
citationsAndRelationsSearchService.close();
431+
LOGGER.trace("Citations and relations search service closed");
432+
});
433+
434+
executor.submit(() -> {
435+
LOGGER.trace("Closing AI service");
436+
try {
437+
aiService.close();
438+
} catch (Exception e) {
439+
LOGGER.error("Unable to close AI service", e);
440+
}
441+
LOGGER.trace("AI service closed");
442+
});
443+
444+
executor.submit(() -> {
445+
LOGGER.trace("Closing OpenOffice connection");
446+
OOBibBaseConnect.closeOfficeConnection();
447+
LOGGER.trace("OpenOffice connection closed");
448+
});
449+
450+
executor.submit(() -> {
451+
LOGGER.trace("Shutting down remote server manager");
452+
remoteListenerServerManager.stop();
453+
LOGGER.trace("RemoteListenerServerManager shut down");
454+
});
455+
456+
executor.submit(() -> {
457+
LOGGER.trace("Shutting down http server manager");
458+
httpServerManager.stop();
459+
LOGGER.trace("HttpServerManager shut down");
460+
});
461+
462+
executor.submit(() -> {
463+
LOGGER.trace("Stopping background tasks");
464+
Unirest.shutDown();
465+
LOGGER.trace("Unirest shut down");
466+
});
467+
468+
// region All threading related shutdowns
469+
executor.submit(() -> {
470+
LOGGER.trace("Shutting down taskExecutor");
471+
if (taskExecutor != null) {
472+
taskExecutor.shutdown();
473+
}
474+
LOGGER.trace("TaskExecutor shut down");
475+
});
476+
477+
executor.submit(() -> {
478+
LOGGER.trace("Shutting down fileUpdateMonitor");
479+
fileUpdateMonitor.shutdown();
480+
LOGGER.trace("FileUpdateMonitor shut down");
481+
});
482+
483+
executor.submit(() -> {
484+
LOGGER.trace("Shutting down directoryMonitor");
485+
DirectoryMonitor directoryMonitor = Injector.instantiateModelOrService(DirectoryMonitor.class);
486+
directoryMonitor.shutdown();
487+
LOGGER.trace("DirectoryMonitor shut down");
488+
});
489+
490+
executor.submit(() -> {
491+
LOGGER.trace("Shutting down postgreServer");
492+
PostgreServer postgreServer = Injector.instantiateModelOrService(PostgreServer.class);
493+
postgreServer.shutdown();
494+
LOGGER.trace("PostgreServer shut down");
495+
});
496+
497+
executor.submit(() -> {
498+
LOGGER.trace("Shutting down HeadlessExecutorService");
499+
HeadlessExecutorService.INSTANCE.shutdownEverything();
500+
LOGGER.trace("HeadlessExecutorService shut down");
501+
});
502+
// endregion
503+
504+
HeadlessExecutorService.gracefullyShutdown("HeadlessExecutorService", executor, 30);
426505
}
427506

428-
LOGGER.trace("Closing OpenOffice connection");
429-
OOBibBaseConnect.closeOfficeConnection();
430-
431-
LOGGER.trace("Shutting down remote server manager");
432-
remoteListenerServerManager.stop();
433-
434-
LOGGER.trace("Shutting down http server manager");
435-
httpServerManager.stop();
436-
437-
LOGGER.trace("Stopping background tasks");
438-
stopBackgroundTasks();
439-
440-
LOGGER.trace("Shutting down thread pools");
441-
shutdownThreadPools();
442-
443-
LOGGER.trace("Closing citations and relations search service");
444-
citationsAndRelationsSearchService.close();
445-
446507
LOGGER.trace("Finished stop");
447508
}
448-
449-
public void stopBackgroundTasks() {
450-
Unirest.shutDown();
451-
}
452-
453-
public static void shutdownThreadPools() {
454-
LOGGER.trace("Shutting down taskExecutor");
455-
if (taskExecutor != null) {
456-
taskExecutor.shutdown();
457-
}
458-
LOGGER.trace("Shutting down fileUpdateMonitor");
459-
fileUpdateMonitor.shutdown();
460-
LOGGER.trace("Shutting down directoryMonitor");
461-
DirectoryMonitor directoryMonitor = Injector.instantiateModelOrService(DirectoryMonitor.class);
462-
directoryMonitor.shutdown();
463-
LOGGER.trace("Shutting down postgreServer");
464-
PostgreServer postgreServer = Injector.instantiateModelOrService(PostgreServer.class);
465-
postgreServer.shutdown();
466-
LOGGER.trace("Shutting down HeadlessExecutorService");
467-
HeadlessExecutorService.INSTANCE.shutdownEverything();
468-
LOGGER.trace("Finished shutdownThreadPools");
469-
}
470509
}

jabgui/src/main/java/org/jabref/gui/util/UiTaskExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void shutdown() {
152152
}
153153
executor.shutdownNow();
154154
scheduledExecutor.shutdownNow();
155-
throttlers.forEach((throttler, aVoid) -> throttler.shutdown());
155+
throttlers.forEach((throttler, _) -> throttler.shutdown());
156156
}
157157

158158
@Override

jablib/src/main/java/org/jabref/logic/util/DelayTaskThrottler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public void cancel() {
8080
* Shuts everything down. Upon termination, this method returns.
8181
*/
8282
public void shutdown() {
83-
HeadlessExecutorService.gracefullyShutdown(executor);
83+
LOGGER.trace("Gracefully shutting down DelayTaskThrottler");
84+
HeadlessExecutorService.gracefullyShutdown("ScheduledThreadPoolExecutor of DelayTaskThrottler", executor, 15);
8485
}
8586
}

jablib/src/main/java/org/jabref/logic/util/HeadlessExecutorService.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ public class HeadlessExecutorService implements Executor {
2929

3030
private static final Logger LOGGER = LoggerFactory.getLogger(HeadlessExecutorService.class);
3131

32+
private static final String EXECUTOR_NAME = "JabRef CachedThreadPool";
33+
private static final String LOW_PRIORITY_EXECUTOR_NAME = "JabRef LowPriorityCachedThreadPool";
34+
3235
private final ExecutorService executorService = Executors.newCachedThreadPool(r -> {
3336
Thread thread = new Thread(r);
34-
thread.setName("JabRef CachedThreadPool");
37+
thread.setName(EXECUTOR_NAME);
3538
thread.setUncaughtExceptionHandler(new FallbackExceptionHandler());
3639
return thread;
3740
});
3841

3942
private final ExecutorService lowPriorityExecutorService = Executors.newCachedThreadPool(r -> {
4043
Thread thread = new Thread(r);
41-
thread.setName("JabRef LowPriorityCachedThreadPool");
44+
thread.setName(LOW_PRIORITY_EXECUTOR_NAME);
4245
thread.setUncaughtExceptionHandler(new FallbackExceptionHandler());
4346
return thread;
4447
});
@@ -128,10 +131,10 @@ public void submit(TimerTask timerTask, long millisecondsDelay) {
128131
*/
129132
public void shutdownEverything() {
130133
LOGGER.trace("Gracefully shut down executor service");
131-
gracefullyShutdown(this.executorService);
134+
gracefullyShutdown(EXECUTOR_NAME, this.executorService, 15);
132135

133136
LOGGER.trace("Gracefully shut down low priority executor service");
134-
gracefullyShutdown(this.lowPriorityExecutorService);
137+
gracefullyShutdown(LOW_PRIORITY_EXECUTOR_NAME, this.lowPriorityExecutorService, 15);
135138

136139
LOGGER.trace("Canceling timer");
137140
timer.cancel();
@@ -166,18 +169,18 @@ public void run() {
166169
* Shuts down the provided executor service by first trying a normal shutdown, then waiting for the shutdown and then forcibly shutting it down.
167170
* Returns if the status of the shut down is known.
168171
*/
169-
public static void gracefullyShutdown(ExecutorService executorService) {
172+
public static void gracefullyShutdown(String name, ExecutorService executorService, int timeoutInSeconds) {
170173
try {
171174
// This is non-blocking. See https://stackoverflow.com/a/57383461/873282.
172175
executorService.shutdown();
173-
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
174-
LOGGER.debug("One minute passed, {} still not completed. Trying forced shutdown.", executorService.toString());
176+
if (!executorService.awaitTermination(timeoutInSeconds, TimeUnit.SECONDS)) {
177+
LOGGER.debug("{} seconds passed, {} still not completed. Trying forced shutdown.", timeoutInSeconds, name);
175178
// those threads will be interrupted in their current task
176179
executorService.shutdownNow();
177-
if (executorService.awaitTermination(60, TimeUnit.SECONDS)) {
178-
LOGGER.debug("One minute passed again - forced shutdown of {} worked.", executorService.toString());
180+
if (executorService.awaitTermination(timeoutInSeconds, TimeUnit.SECONDS)) {
181+
LOGGER.debug("{} seconds passed again - forced shutdown of {} worked.", timeoutInSeconds, name);
179182
} else {
180-
LOGGER.error("{} did not terminate", executorService.toString());
183+
LOGGER.error("{} did not terminate", name);
181184
}
182185
}
183186
} catch (InterruptedException ie) {

0 commit comments

Comments
 (0)