فهرست منبع

Added catching executors

JaniruTEC 2 سال پیش
والد
کامیت
0040c8a5f8

+ 99 - 0
src/main/java/org/cryptomator/common/CatchingExecutors.java

@@ -0,0 +1,99 @@
+package org.cryptomator.common;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javafx.application.Platform;
+import javafx.concurrent.Task;
+import java.util.Objects;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+//Inspired by: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html#afterExecute(java.lang.Runnable,java.lang.Throwable)
+public final class CatchingExecutors {
+
+	private static final Logger LOG = LoggerFactory.getLogger(CatchingExecutors.class);
+
+	private CatchingExecutors() { /* NO-OP */ }
+
+	public static class CatchingScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
+
+		public CatchingScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
+			super(corePoolSize, threadFactory);
+		}
+
+		@Override
+		protected void afterExecute(Runnable runnable, Throwable throwable) {
+			super.afterExecute(runnable, throwable);
+			afterExecute0(runnable, throwable);
+		}
+	}
+
+	public static class CatchingThreadPoolExecutor extends ThreadPoolExecutor {
+
+		public CatchingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
+			super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+		}
+
+		@Override
+		protected void afterExecute(Runnable runnable, Throwable throwable) {
+			super.afterExecute(runnable, throwable);
+			afterExecute0(runnable, throwable);
+		}
+	}
+
+	private static void afterExecute0(Runnable runnable, Throwable throwable) {
+		if (throwable == null) {
+			if (runnable instanceof Task<?>) {
+				afterExecuteTask(Thread.currentThread(), (Task<?>) runnable);
+				return;
+			}
+			throwable = getThrowable(runnable);
+		}
+
+		if (throwable != null) {
+			callHandler(Thread.currentThread(), throwable);
+		}
+		//Errors in this method are delegated to the UncaughtExceptionHandler of the current thread
+	}
+
+	private static void callHandler(Thread thread, Throwable throwable) {
+		Objects.requireNonNullElseGet(thread.getUncaughtExceptionHandler(), CatchingExecutors::fallbackHandler).uncaughtException(thread, throwable);
+	}
+
+	private static Thread.UncaughtExceptionHandler fallbackHandler() {
+		return (thread, throwable) -> LOG.error("FALLBACK: Uncaught exception in " + thread.getName(), throwable);
+	}
+
+	private static void afterExecuteTask(Thread caller, Task<?> task) {
+		Platform.runLater(() -> {
+			if (task.getOnFailed() == null) {
+				callHandler(caller, task.getException());
+			}
+		});
+	}
+
+	private static Throwable getThrowable(Runnable runnable) {
+		assert !(runnable instanceof Task<?>);
+
+		if (runnable instanceof Future<?> && ((Future<?>) runnable).isDone()) {
+			try {
+				((Future<?>) runnable).get();
+			} catch (CancellationException ce) {
+				return ce;
+			} catch (ExecutionException ee) {
+				return ee.getCause();
+			} catch (InterruptedException ie) {
+				//Ignore/Reset
+				Thread.currentThread().interrupt();
+			}
+		}
+		return null;
+	}
+}

+ 2 - 7
src/main/java/org/cryptomator/common/CommonsModule.java

@@ -12,9 +12,7 @@ import org.apache.commons.lang3.SystemUtils;
 import org.cryptomator.common.keychain.KeychainModule;
 import org.cryptomator.common.settings.Settings;
 import org.cryptomator.common.settings.SettingsProvider;
-import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.common.vaults.VaultComponent;
-import org.cryptomator.common.vaults.VaultListManager;
 import org.cryptomator.common.vaults.VaultListModule;
 import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
 import org.cryptomator.frontend.webdav.WebDavServer;
@@ -25,16 +23,13 @@ import javax.inject.Named;
 import javax.inject.Singleton;
 import javafx.beans.binding.Binding;
 import javafx.beans.binding.Bindings;
-import javafx.collections.ObservableList;
 import java.net.InetSocketAddress;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.Comparator;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -93,7 +88,7 @@ public abstract class CommonsModule {
 	@Singleton
 	static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
 		final AtomicInteger threadNumber = new AtomicInteger(1);
-		ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
+		ScheduledExecutorService executorService = new CatchingExecutors.CatchingScheduledThreadPoolExecutor(NUM_SCHEDULER_THREADS, r -> {
 			String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
 			Thread t = new Thread(r);
 			t.setName(name);
@@ -110,7 +105,7 @@ public abstract class CommonsModule {
 	@Singleton
 	static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
 		final AtomicInteger threadNumber = new AtomicInteger(1);
-		ExecutorService executorService = new ThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
+		ExecutorService executorService = new CatchingExecutors.CatchingThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
 			String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
 			Thread t = new Thread(r);
 			t.setName(name);