From 66e8b9ebe1e4807efba5293fd75bf8bc17234439 Mon Sep 17 00:00:00 2001 From: Mateusz Waronwy Date: Mon, 26 Oct 2020 13:20:00 +0100 Subject: [PATCH] JAL-3690 - introduce shutdown method that cleans up manager's resources. --- src/jalview/api/AlignCalcManagerI2.java | 6 ++ src/jalview/bin/Jalview.java | 4 + src/jalview/viewmodel/AlignmentViewport.java | 1 + src/jalview/workers/AlignCalcManager2.java | 150 ++++++++++++++++++-------- 4 files changed, 119 insertions(+), 42 deletions(-) diff --git a/src/jalview/api/AlignCalcManagerI2.java b/src/jalview/api/AlignCalcManagerI2.java index 756e402..22b4832 100644 --- a/src/jalview/api/AlignCalcManagerI2.java +++ b/src/jalview/api/AlignCalcManagerI2.java @@ -111,4 +111,10 @@ public interface AlignCalcManagerI2 * Remove previously registered worker listener. */ void removeAlignCalcListener(AlignCalcListener listener); + + /** + * Stops the manager from running new jobs and cleans-up all + * resources such as threads and thread pools. + */ + void shutdown(); } diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index 08b4a1b..f5b092d 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -774,6 +774,10 @@ public class Jalview System.out.println("Unknown arg: " + aparser.nextValue()); } } + if (headless) + { + af.getViewport().getCalcManager().shutdown(); + } } AlignFrame startUpAlframe = null; // We'll only open the default file if the desktop is visible. diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 77210ee..4311d14 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -1000,6 +1000,7 @@ public abstract class AlignmentViewport hconservation = null; hcomplementConsensus = null; gapcounts = null; + calculator.shutdown(); calculator = null; residueShading = null; // may hold a reference to Consensus changeSupport = null; diff --git a/src/jalview/workers/AlignCalcManager2.java b/src/jalview/workers/AlignCalcManager2.java index e154f0f..b914d65 100644 --- a/src/jalview/workers/AlignCalcManager2.java +++ b/src/jalview/workers/AlignCalcManager2.java @@ -11,7 +11,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; -import java.util.stream.Collectors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; import jalview.api.AlignCalcListener; import jalview.api.AlignCalcManagerI2; @@ -36,12 +37,15 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 public AlignCalcTask(AlignCalcWorkerI worker) { - super(new Callable() { - public Void call() throws Exception { - Cache.log.debug(format("Worker %s started%n", worker.getClass().getName())); - notifyStarted(worker); - worker.run(); - return null; + super(new Callable() + { + public Void call() throws Exception + { + Cache.log.debug(format("Worker %s started%n", + worker.getClass().getName())); + notifyStarted(worker); + worker.run(); + return null; } }); this.worker = worker; @@ -61,54 +65,95 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { get(); success = true; - } - catch (CancellationException e) { - Cache.log.debug(format("Worker %s cancelled%n", getWorker().getClass().getName())); + } catch (CancellationException e) + { + Cache.log.debug(format("Worker %s cancelled%n", + getWorker().getClass().getName())); notifyCancelled(worker); - } - catch (ExecutionException e) + } catch (ExecutionException e) { exception = e.getCause(); - if (exception instanceof OutOfMemoryError) { + if (exception instanceof OutOfMemoryError) + { disableWorker(getWorker()); } } catch (Throwable e) { exception = e; - } - finally { + } finally + { inProgress.remove(getWorker()); tasks.remove(this); } if (success) { - Cache.log.debug(format("Worker %s finished%n", getWorker().getClass().getName())); + Cache.log.debug(format("Worker %s finished%n", + getWorker().getClass().getName())); notifyCompleted(worker); } - else if (exception != null){ - Cache.log.warn(format("Worker %s failed%n", getWorker().getClass().getName())); + else if (exception != null) + { + Cache.log.warn(format("Worker %s failed%n", + getWorker().getClass().getName())); exception.printStackTrace(); notifyExceptional(worker, exception); } } } + private static class CalcManagerThreadFactory implements ThreadFactory + { + private static final AtomicInteger threadNumber = new AtomicInteger(1); + + private final ThreadGroup group; + + private static final String namePrefix = "AlignCalcManager-pool-thread-"; + + CalcManagerThreadFactory() + { + var securityManager = System.getSecurityManager(); + if (securityManager != null) + { + group = securityManager.getThreadGroup(); + } + else + { + group = Thread.currentThread().getThreadGroup(); + } + } + + @Override + public Thread newThread(Runnable r) + { + Thread t = new Thread(group, r, + namePrefix + threadNumber.getAndIncrement(), 0); + t.setDaemon(false); + t.setPriority(Thread.NORM_PRIORITY); + return t; + } + } + // main executor for running workers one-by-one - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - + private final ExecutorService executor = Executors + .newSingleThreadExecutor(new CalcManagerThreadFactory()); + private final List listeners = new CopyOnWriteArrayList(); // list of all registered workers (other collections are subsets of this) - private final List registered = synchronizedList(new ArrayList<>()); + private final List registered = synchronizedList( + new ArrayList<>()); // list of tasks holding queued and running workers - private final List tasks = synchronizedList(new ArrayList<>()); - + private final List tasks = synchronizedList( + new ArrayList<>()); + // the collection of currently running workers - private final Set inProgress = synchronizedSet(new HashSet<>()); + private final Set inProgress = synchronizedSet( + new HashSet<>()); // the collection of workers that will not be started - private final Set disabled = synchronizedSet(new HashSet<>()); + private final Set disabled = synchronizedSet( + new HashSet<>()); /* * Register the worker with this manager and scheduler for execution. @@ -130,19 +175,25 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { return unmodifiableList(new ArrayList<>(registered)); } - + @Override public List getWorkersOfClass( Class cls) { synchronized (registered) { - return registered.stream() - .filter(worker -> worker.getClass().equals(cls)) - .collect(Collectors.toUnmodifiableList()); + List collected = new ArrayList<>(); + for (var worker : registered) + { + if (worker.getClass().equals(cls)) + { + collected.add(worker); + } + } + return unmodifiableList(collected); } } - + @Override public void removeWorker(AlignCalcWorkerI worker) { @@ -219,18 +270,19 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 notifyQueued(worker); executor.execute(newTask); } - + @Override public void cancelWorker(AlignCalcWorkerI worker) { - if (isWorking(worker)) + if (isWorking(worker)) { - synchronized (tasks) + synchronized (tasks) { Optional oldTask = tasks.stream() - .filter(task -> task.getWorker().equals(worker)) - .findFirst(); - if (oldTask.isPresent()) { + .filter(task -> task.getWorker().equals(worker)) + .findFirst(); + if (oldTask.isPresent()) + { oldTask.get().cancel(true); } } @@ -254,7 +306,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { return !inProgress.isEmpty(); } - + @Override public boolean isWorkingWithAnnotation(AlignmentAnnotation annot) { @@ -291,7 +343,8 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { for (AlignCalcListener listener : listeners) { - try { + try + { listener.workerCompleted(worker); } catch (RuntimeException e) { @@ -299,12 +352,13 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 } } } - + private void notifyCancelled(AlignCalcWorkerI worker) { for (AlignCalcListener listener : listeners) { - try { + try + { listener.workerCancelled(worker); } catch (RuntimeException e) { @@ -318,7 +372,8 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { for (AlignCalcListener listener : listeners) { - try { + try + { listener.workerExceptional(worker, throwable); } catch (RuntimeException e) { @@ -332,11 +387,22 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { listeners.add(listener); } - + @Override public void removeAlignCalcListener(AlignCalcListener listener) { listeners.remove(listener); } + @Override + public void shutdown() + { + executor.shutdownNow(); + listeners.clear(); + registered.clear(); + tasks.clear(); + inProgress.clear(); + disabled.clear(); + } + } -- 1.7.10.2