From 77e30e246d179e5b84ad6079a7f7f1d28b5fff20 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Fri, 21 Jan 2022 09:09:32 -0500
Subject: [PATCH] Util: Fix leak of SimpleTimer2 shutdown task

---
 core/java/src/net/i2p/I2PAppContext.java     |  7 ++++++
 core/java/src/net/i2p/util/SimpleTimer2.java | 23 +++++++++++++++++---
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java
index 99bbd53c72..7df62046c8 100644
--- a/core/java/src/net/i2p/I2PAppContext.java
+++ b/core/java/src/net/i2p/I2PAppContext.java
@@ -906,6 +906,13 @@ public class I2PAppContext {
     public void addShutdownTask(Runnable task) {
         _shutdownTasks.add(task);
     }
+
+    /**
+     *  @since 0.9.53
+     */
+    public void removeShutdownTask(Runnable task) {
+        _shutdownTasks.remove(task);
+    }
     
     /**
      *  @return an unmodifiable Set
diff --git a/core/java/src/net/i2p/util/SimpleTimer2.java b/core/java/src/net/i2p/util/SimpleTimer2.java
index b6c6deaef7..f2723dbb73 100644
--- a/core/java/src/net/i2p/util/SimpleTimer2.java
+++ b/core/java/src/net/i2p/util/SimpleTimer2.java
@@ -41,6 +41,8 @@ public class SimpleTimer2 {
     private final String _name;
     private final AtomicInteger _count = new AtomicInteger();
     private final int _threads;
+    private final I2PAppContext _context;
+    private final Runnable _shutdown;
 
     /**
      *  To be instantiated by the context.
@@ -64,14 +66,15 @@ public class SimpleTimer2 {
      *  @since 0.9
      */
     protected SimpleTimer2(I2PAppContext context, String name, boolean prestartAllThreads) {
+        _context = context;
         _name = name;
         long maxMemory = SystemVersion.getMaxMemory();
         _threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
         _executor = new CustomScheduledThreadPoolExecutor(_threads, new CustomThreadFactory());
         if (prestartAllThreads)
             _executor.prestartAllCoreThreads();
-        // don't bother saving ref to remove hook if somebody else calls stop
-        context.addShutdownTask(new Shutdown());
+        _shutdown = new Shutdown();
+        context.addShutdownTask(_shutdown);
     }
     
     /**
@@ -79,7 +82,7 @@ public class SimpleTimer2 {
      */
     private class Shutdown implements Runnable {
         public void run() {
-            stop();
+            stop(false);
         }
     }
 
@@ -89,6 +92,20 @@ public class SimpleTimer2 {
      * Cannot be restarted.
      */
     public void stop() {
+        stop(true);
+    }
+
+    /**
+     * Stops the SimpleTimer.
+     * Subsequent executions should not throw a RejectedExecutionException.
+     * Cannot be restarted.
+     *
+     * @param removeTask true to unregister the shutdown hook
+     * @since 0.9.53
+     */
+    private void stop(boolean removeTask) {
+        if (removeTask)
+            _context.removeShutdownTask(_shutdown);
         _executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
         _executor.shutdownNow();
     }
-- 
GitLab