X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fworkers%2FAlignCalcManager2.java;h=eba241a7e64a50411f08eb6fa37925927e9f350c;hb=49ab19e8189569edf0bc1f4ba8dac14e67f4ca36;hp=0057500ae492b09a4ca55fdac537893d7a79e289;hpb=c2b931a5a0ea81bdc93db1f0cf632b07717c5066;p=jalview.git diff --git a/src/jalview/workers/AlignCalcManager2.java b/src/jalview/workers/AlignCalcManager2.java index 0057500..eba241a 100644 --- a/src/jalview/workers/AlignCalcManager2.java +++ b/src/jalview/workers/AlignCalcManager2.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -22,6 +23,7 @@ import jalview.api.AlignCalcManagerI2; import jalview.api.AlignCalcWorkerI; import jalview.api.PollableAlignCalcWorkerI; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.datamodel.AlignmentAnnotation; public class AlignCalcManager2 implements AlignCalcManagerI2 @@ -29,59 +31,67 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 private abstract class WorkerManager { protected volatile boolean enabled = true; - - protected final AlignCalcWorkerI worker; - + + protected AlignCalcWorkerI worker; + WorkerManager(AlignCalcWorkerI worker) { this.worker = worker; } - - AlignCalcWorkerI getWorker() + + protected AlignCalcWorkerI getWorker() { return worker; } - + boolean isEnabled() { return enabled; } - + void setEnabled(boolean enabled) { this.enabled = enabled; } - + synchronized void restart() { if (!isEnabled()) { return; } + if (!isRegistered()) + { + setEnabled(false); + } if (isWorking()) { cancel(); } submit(); } - + + protected boolean isRegistered() + { + return registered.containsKey(getWorker()); + } + abstract boolean isWorking(); - + protected abstract void submit(); - - abstract void cancel(); + + abstract void cancel(); } - - + private class SimpleWorkerManager extends WorkerManager { private Future task = null; - + SimpleWorkerManager(AlignCalcWorkerI worker) { super(worker); } - + @Override boolean isWorking() { @@ -94,31 +104,33 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 if (task != null && !(task.isDone() || task.isCancelled())) { throw new IllegalStateException( - "Cannot submit new task if the prevoius one is still running"); + "Cannot submit new task if the prevoius one is still running"); } - Cache.log.debug(format("Worker %s queued", - worker.getClass().getName())); + Console.debug( + format("Worker %s queued", getWorker().getClass().getName())); task = executor.submit(() -> { try { - Cache.log.debug(format("Worker %s started", - worker.getClass().getName())); - worker.run(); - Cache.log.debug(format("Worker %s finished", - worker.getClass().getName())); - } - catch (InterruptedException e) + Console.debug(format("Worker %s started", + getWorker().getClass().getName())); + getWorker().run(); + Console.debug(format("Worker %s finished", + getWorker().getClass().getName())); + } catch (InterruptedException e) { - Cache.log.debug(format("Worker %s interrupted", - worker.getClass().getName())); - } - catch (Throwable th) + Console.debug(format("Worker %s interrupted", + getWorker().getClass().getName())); + } catch (Throwable th) { - Cache.log.debug(format("Worker %s failed", - worker.getClass().getName()), th); - } - finally + Console.debug(format("Worker %s failed", + getWorker().getClass().getName()), th); + } finally { + if (!isRegistered()) + { + // delete worker reference so garbage collector can remove it + worker = null; + } } }); } @@ -130,45 +142,50 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { return; } - Cache.log.debug(format("Cancelling worker %s", - worker.getClass().getName())); + Console.debug(format("Cancelling worker %s", + getWorker().getClass().getName())); task.cancel(true); } } - - + private class PollableWorkerManager extends WorkerManager { - private final PollableAlignCalcWorkerI worker; private Future task = null; - + PollableWorkerManager(PollableAlignCalcWorkerI worker) { super(worker); - this.worker = worker; } - + + @Override + protected PollableAlignCalcWorkerI getWorker() + { + return (PollableAlignCalcWorkerI) super.getWorker(); + } + @Override boolean isWorking() { return task != null && !task.isDone(); } - + protected void submit() { if (task != null && !(task.isDone() || task.isCancelled())) { throw new IllegalStateException( - "Cannot submit new task if the prevoius one is still running"); + "Cannot submit new task if the prevoius one is still running"); } - Cache.log.debug(format("Worker %s queued", - worker.getClass().getName())); + Console.debug( + format("Worker %s queued", getWorker().getClass().getName())); final var runnable = new Runnable() { private boolean started = false; + private boolean completed = false; + Future future = null; - + @Override public void run() { @@ -176,31 +193,34 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { if (!started) { - Cache.log.debug(format("Worker %s started", - worker.getClass().getName())); - worker.startUp(); + Console.debug(format("Worker %s started", + getWorker().getClass().getName())); + getWorker().startUp(); started = true; } else if (!completed) { - Cache.log.debug(format("Polling worker %s", - worker.getClass().getName())); - if (worker.poll()) + Console.debug(format("Polling worker %s", + getWorker().getClass().getName())); + if (getWorker().poll()) { - Cache.log.debug(format("Worker %s finished", - worker.getClass().getName())); + Console.debug(format("Worker %s finished", + getWorker().getClass().getName())); completed = true; } } } catch (Throwable th) { - Cache.log.debug(format("Worker %s failed", - worker.getClass().getName()), th); + Console.debug(format("Worker %s failed", + getWorker().getClass().getName()), th); completed = true; } if (completed) { - Cache.log.debug(format("Finalizing completed worker %s", + final var worker = getWorker(); + if (!isRegistered()) + PollableWorkerManager.super.worker = null; + Console.debug(format("Finalizing completed worker %s", worker.getClass().getName())); worker.done(); // almost impossible, but the future may be null at this point @@ -209,45 +229,62 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 } } }; - runnable.future = task = executor.scheduleWithFixedDelay( - runnable, 10, 1000, TimeUnit.MILLISECONDS); + runnable.future = task = executor.scheduleWithFixedDelay(runnable, 10, + 1000, TimeUnit.MILLISECONDS); } - + synchronized protected void cancel() { if (!isWorking()) { return; } - Cache.log.debug(format("Cancelling worker %s", - worker.getClass().getName())); + Console.debug(format("Cancelling worker %s", + getWorker().getClass().getName())); task.cancel(false); executor.submit(() -> { - worker.cancel(); - Cache.log.debug(format("Finalizing cancelled worker %s", - worker.getClass().getName())); - worker.done(); + final var worker = getWorker(); + if (!isRegistered()) + PollableWorkerManager.super.worker = null; + if (worker != null) + { + worker.cancel(); + Console.debug(format("Finalizing cancelled worker %s", + worker.getClass().getName())); + worker.done(); + } }); } } - - - private final ScheduledExecutorService executor = - Executors.newSingleThreadScheduledExecutor(); - private final Map registered = - synchronizedMap(new HashMap<>()); - - private final List listeners = - new CopyOnWriteArrayList<>(); - - + + private final ScheduledExecutorService executor = Executors + .newSingleThreadScheduledExecutor(); + + private final Map registered = synchronizedMap( + new HashMap<>()); + + private final Map oneshot = synchronizedMap( + new WeakHashMap<>()); + + private final List listeners = new CopyOnWriteArrayList<>(); + + private WorkerManager createManager(AlignCalcWorkerI worker) + { + if (worker instanceof PollableAlignCalcWorkerI) + { + return new PollableWorkerManager((PollableAlignCalcWorkerI) worker); + } + else + { + return new SimpleWorkerManager(worker); + } + } + @Override public void registerWorker(AlignCalcWorkerI worker) { Objects.requireNonNull(worker); - WorkerManager manager = (worker instanceof PollableAlignCalcWorkerI) ? - new PollableWorkerManager((PollableAlignCalcWorkerI) worker) : - new SimpleWorkerManager(worker); + WorkerManager manager = createManager(worker); registered.putIfAbsent(worker, manager); startWorker(worker); } @@ -255,7 +292,9 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 @Override public List getWorkers() { - return List.copyOf(registered.keySet()); + List result = new ArrayList<>(registered.size()); + result.addAll(registered.keySet()); + return result; } @Override @@ -276,7 +315,10 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 @Override public void removeWorker(AlignCalcWorkerI worker) { - registered.remove(worker); + if (worker.isDeletable()) + { + registered.remove(worker); + } } @Override @@ -286,7 +328,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { for (var worker : getWorkers()) { - if (worker.involves(annot) && worker.isDeletable()) + if (worker.involves(annot)) { removeWorker(worker); } @@ -339,14 +381,13 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 @Override public boolean isWorking(AlignCalcWorkerI worker) { - if (!registered.containsKey(worker)) - { + var manager = registered.get(worker); + if (manager == null) + manager = oneshot.get(worker); + if (manager == null) return false; - } else - { - return registered.get(worker).isWorking(); - } + return manager.isWorking(); } @Override @@ -355,13 +396,14 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 synchronized (registered) { for (var entry : registered.entrySet()) - { - if (entry.getKey().involves(annot) && - entry.getValue().isWorking()) - { + if (entry.getKey().involves(annot) && entry.getValue().isWorking()) + return true; + } + synchronized (oneshot) + { + for (var entry : registered.entrySet()) + if (entry.getKey().involves(annot) && entry.getValue().isWorking()) return true; - } - } } return false; } @@ -372,12 +414,14 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 synchronized (registered) { for (var manager : registered.values()) - { if (manager.isWorking()) - { return true; - } - } + } + synchronized (oneshot) + { + for (var manager : oneshot.values()) + if (manager.isWorking()) + return true; } return false; } @@ -387,9 +431,11 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { Objects.requireNonNull(worker); var manager = registered.get(worker); - if (manager == null) + if (manager == null) { - throw new NoSuchElementException(); + Console.warn("Starting unregistered worker " + worker); + manager = createManager(worker); + oneshot.put(worker, manager); } manager.restart(); } @@ -408,16 +454,18 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 @Override public void cancelWorker(AlignCalcWorkerI worker) - { + { Objects.requireNonNull(worker); var manager = registered.get(worker); - if (manager == null) + if (manager == null) + manager = oneshot.get(worker); + if (manager == null) { throw new NoSuchElementException(); } manager.cancel(); } - + private void notifyQueued(AlignCalcWorkerI worker) { for (AlignCalcListener listener : listeners)