diff --git a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java index 81bc5f01e..5ae18f92f 100644 --- a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java @@ -67,7 +67,7 @@ public abstract class CommonsModule { @Provides @Singleton - static ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer shutdownTaskScheduler) { + static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) { final AtomicInteger threadNumber = new AtomicInteger(1); ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> { Thread t = new Thread(r); @@ -75,7 +75,7 @@ public abstract class CommonsModule { t.setDaemon(true); return t; }); - shutdownTaskScheduler.accept(executorService::shutdown); + shutdownHook.runOnShutdown(executorService::shutdown); return executorService; } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CleanShutdownPerformer.java b/main/commons/src/main/java/org/cryptomator/common/ShutdownHook.java similarity index 54% rename from main/launcher/src/main/java/org/cryptomator/launcher/CleanShutdownPerformer.java rename to main/commons/src/main/java/org/cryptomator/common/ShutdownHook.java index 717fb86a8..866d5edfc 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CleanShutdownPerformer.java +++ b/main/commons/src/main/java/org/cryptomator/common/ShutdownHook.java @@ -3,46 +3,48 @@ * All rights reserved. This program and the accompanying materials * are made available under the terms of the accompanying LICENSE file. *******************************************************************************/ -package org.cryptomator.launcher; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +package org.cryptomator.common; +import com.google.common.util.concurrent.Runnables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; @Singleton -class CleanShutdownPerformer extends Thread { +public class ShutdownHook extends Thread { - private static final Logger LOG = LoggerFactory.getLogger(CleanShutdownPerformer.class); - private final ConcurrentMap tasks = new ConcurrentHashMap<>(); + private static final Logger LOG = LoggerFactory.getLogger(ShutdownHook.class); + private static final Runnable POISON = Runnables.doNothing(); + + private final Queue tasks = new ConcurrentLinkedQueue<>(); @Inject - CleanShutdownPerformer() { + ShutdownHook() { super(null, null, "ShutdownTasks", 0); + Runtime.getRuntime().addShutdownHook(this); + LOG.debug("Registered shutdown hook."); } @Override public void run() { LOG.debug("Running graceful shutdown tasks..."); - tasks.keySet().forEach(r -> { + tasks.add(POISON); + Runnable task; + while ((task = tasks.remove()) != POISON) { try { - r.run(); + task.run(); } catch (RuntimeException e) { LOG.error("Exception while shutting down.", e); } - }); - tasks.clear(); + } } - void scheduleShutdownTask(Runnable task) { - tasks.put(task, Boolean.TRUE); - } - - void registerShutdownHook() { - Runtime.getRuntime().addShutdownHook(this); + public void runOnShutdown(Runnable task) { + tasks.add(task); } + } \ No newline at end of file diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java index a4d861c3e..04eb9448d 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -32,17 +32,15 @@ public class Cryptomator { private final IpcFactory ipcFactory; private final Optional applicationVersion; private final CountDownLatch shutdownLatch; - private final CleanShutdownPerformer shutdownPerformer; private final UiLauncher uiLauncher; @Inject - Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, CleanShutdownPerformer shutdownPerformer, UiLauncher uiLauncher) { + Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, UiLauncher uiLauncher) { this.logConfig = logConfig; this.debugMode = debugMode; this.ipcFactory = ipcFactory; this.applicationVersion = applicationVersion; this.shutdownLatch = shutdownLatch; - this.shutdownPerformer = shutdownPerformer; this.uiLauncher = uiLauncher; } @@ -90,7 +88,6 @@ public class Cryptomator { */ private int runGuiApplication() { try { - shutdownPerformer.registerShutdownHook(); uiLauncher.launch(); shutdownLatch.await(); LOG.info("UI shut down"); diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java index 268d69002..6b37e29b6 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java @@ -7,18 +7,10 @@ import javax.inject.Named; import javax.inject.Singleton; import java.util.Optional; import java.util.concurrent.CountDownLatch; -import java.util.function.Consumer; @Module class CryptomatorModule { - - @Provides - @Singleton - @Named("shutdownTaskScheduler") - Consumer provideShutdownTaskScheduler(CleanShutdownPerformer shutdownPerformer) { - return shutdownPerformer::scheduleShutdownTask; - } - + @Provides @Singleton @Named("shutdownLatch") diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java index 1c73b5959..d32b5ab5e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java @@ -3,9 +3,11 @@ package org.cryptomator.ui.traymenu; import javafx.application.Platform; import javafx.beans.Observable; import javafx.collections.ObservableList; +import org.cryptomator.common.ShutdownHook; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; +import org.cryptomator.common.vaults.Volume; import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.launcher.FxApplicationStarter; import org.cryptomator.ui.preferences.SelectedPreferencesTab; @@ -38,17 +40,17 @@ class TrayMenuController { private final ResourceBundle resourceBundle; private final FxApplicationStarter fxApplicationStarter; private final CountDownLatch shutdownLatch; - private final Settings settings; + private final ShutdownHook shutdownHook; private final ObservableList vaults; private final PopupMenu menu; private final AtomicBoolean allowSuddenTermination; @Inject - TrayMenuController(ResourceBundle resourceBundle, FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch, Settings settings, ObservableList vaults) { + TrayMenuController(ResourceBundle resourceBundle, FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, ObservableList vaults) { this.resourceBundle = resourceBundle; this.fxApplicationStarter = fxApplicationStarter; this.shutdownLatch = shutdownLatch; - this.settings = settings; + this.shutdownHook = shutdownHook; this.vaults = vaults; this.menu = new PopupMenu(); this.allowSuddenTermination = new AtomicBoolean(true); @@ -72,6 +74,7 @@ class TrayMenuController { if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_HANDLER)) { Desktop.getDesktop().setQuitHandler(this::handleQuitRequest); } + shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults); // allow sudden termination if (Desktop.getDesktop().isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) { @@ -175,4 +178,16 @@ class TrayMenuController { } }); } + + private void forceUnmountRemainingVaults() { + for (Vault vault : vaults) { + if (vault.isUnlocked()) { + try { + vault.lock(true); + } catch (Volume.VolumeException e) { + LOG.error("Failed to unmount vault " + vault.getPath(), e); + } + } + } + } }