This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/8.5.x by this push:
     new ae448b3461 Rework Native library stability fix.
ae448b3461 is described below

commit ae448b346135f381ff695c16dd4a24bac184a3e5
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Feb 9 12:35:59 2024 +0000

    Rework Native library stability fix.
    
    There are two aspects to this:
    - Don't let the library temerinate while finalize is clearing
      OpenSSLContext instances
    - Don't allow finalize to clear OpenSSLContext instances once the
      library generaiton that created that instance has terminated
---
 .../apache/catalina/core/AprLifecycleListener.java |  5 +-
 java/org/apache/tomcat/jni/Library.java            | 53 ++++++++++++++++------
 .../tomcat/util/net/openssl/OpenSSLContext.java    | 26 +++++++----
 test/org/apache/tomcat/util/net/TesterSupport.java |  1 -
 4 files changed, 57 insertions(+), 28 deletions(-)

diff --git a/java/org/apache/catalina/core/AprLifecycleListener.java 
b/java/org/apache/catalina/core/AprLifecycleListener.java
index ffcc694e6f..604d01502a 100644
--- a/java/org/apache/catalina/core/AprLifecycleListener.java
+++ b/java/org/apache/catalina/core/AprLifecycleListener.java
@@ -175,14 +175,11 @@ public class AprLifecycleListener implements 
LifecycleListener {
     }
 
     private static void terminateAPR() {
-        Library.terminatePrepare();
-        // Need to force GC here as some components do APR clean-up in 
finalize()
-        System.gc();
         aprInitialized = false;
         aprAvailable = false;
         fipsModeActive = false;
         sslInitialized = false; // Well we cleaned the pool in terminate.
-        Library.terminate();
+        Library.threadSafeTerminate();
     }
 
     @SuppressWarnings("deprecation")
diff --git a/java/org/apache/tomcat/jni/Library.java 
b/java/org/apache/tomcat/jni/Library.java
index a08dae4c07..e876c13d73 100644
--- a/java/org/apache/tomcat/jni/Library.java
+++ b/java/org/apache/tomcat/jni/Library.java
@@ -17,6 +17,10 @@
 package org.apache.tomcat.jni;
 
 import java.io.File;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 public final class Library {
 
@@ -30,7 +34,8 @@ public final class Library {
      */
     private static Library _instance = null;
 
-    private static volatile boolean initialized = false;
+    private static final AtomicLong generation = new AtomicLong(0);
+    private static final ReadWriteLock cleanUpLock = new 
ReentrantReadWriteLock();
 
     private Library() throws Exception {
         boolean loaded = false;
@@ -81,21 +86,25 @@ public final class Library {
      */
     private static native boolean initialize();
     /**
-     * Signal that Tomcat Native is about to be shutdown.
-     * <p>
-     * The main purpose of this flag is to allow instances that manage their 
own APR root pools to determine if those
-     * pools need to be explicitly cleaned up or if they will be / have been 
cleaned up by the call to
-     * {@link #terminate()}. The code needs to avoid multiple attempts to 
clean up these pools else the Native code may
-     * crash.
+     * Allows for thread safe termination when other threads may be attempting 
clean-up concurrently with the current
+     * thread. Waits for any threads currently holding the clean-up lock to 
release the lock and then calls
+     * {@link #terminate()}.
      */
-    public static void terminatePrepare() {
-        initialized = false;
+    public static void threadSafeTerminate() {
+        cleanUpLock.writeLock().lock();
+        try {
+            terminate();
+        } finally {
+            generation.incrementAndGet();
+            cleanUpLock.writeLock().unlock();
+        }
     }
     /**
      * Destroys Tomcat Native's global APR pool. This has to be the last call 
to TCN library. This will destroy any APR
      * root pools that have not been explicitly destroyed.
      * <p>
-     * Callers of this method should call {@link #terminatePrepare()} before 
calling this method.
+     * This method should only be used if the caller is certain that all other 
threads have finished using the native
+     * library.
      */
     public static native void terminate();
     /* Internal function for loading APR Features */
@@ -263,13 +272,29 @@ public final class Library {
                 throw new UnsatisfiedLinkError("Missing threading support from 
APR");
             }
         }
-        initialized = initialize();
-        return initialized;
+        return initialize();
+    }
+
+
+    public static boolean tryCleanUpLock(long cleanupGeneration) {
+        try {
+            boolean result = cleanUpLock.readLock().tryLock(0, 
TimeUnit.SECONDS);
+            if (result &&  generation.get() == cleanupGeneration) {
+                return true;
+            }
+            cleanUpLock.readLock().unlock();
+        } catch (InterruptedException e) {
+            // Treated the same way as not getting the lock
+        }
+        return false;
     }
 
+    public static long getGeneration() {
+        return generation.get();
+    }
 
-    public static boolean isInitialized() {
-        return initialized;
+    public static void returnCleanUpLock() {
+        cleanUpLock.readLock().unlock();
     }
 
     /**
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java 
b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
index b2878a8beb..06767e186d 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
@@ -87,6 +87,7 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
     private final SSLHostConfigCertificate certificate;
     private final List<String> negotiableProtocols;
     private final long aprPool;
+    private final long aprGeneration;
     private final AtomicInteger aprPoolDestroyed = new AtomicInteger(0);
     // OpenSSLConfCmd context
     protected final long cctx;
@@ -104,6 +105,7 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
         this.sslHostConfig = certificate.getSSLHostConfig();
         this.certificate = certificate;
         aprPool = Pool.create(0);
+        aprGeneration = Library.getGeneration();
         boolean success = false;
         try {
             // Create OpenSSLConfCmd context if used
@@ -190,15 +192,21 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
     @Override
     public synchronized void destroy() {
         // Guard against multiple destroyPools() calls triggered by 
construction exception and finalize() later
-        if (aprPoolDestroyed.compareAndSet(0, 1) && Library.isInitialized()) {
-            if (ctx != 0) {
-                SSLContext.free(ctx);
-            }
-            if (cctx != 0) {
-                SSLConf.free(cctx);
-            }
-            if (aprPool != 0) {
-                Pool.destroy(aprPool);
+        if (aprPoolDestroyed.compareAndSet(0, 1)) {
+            if (Library.tryCleanUpLock(aprGeneration)) {
+                try {
+                    if (ctx != 0) {
+                        SSLContext.free(ctx);
+                    }
+                    if (cctx != 0) {
+                        SSLConf.free(cctx);
+                    }
+                    if (aprPool != 0) {
+                        Pool.destroy(aprPool);
+                    }
+                } finally {
+                    Library.returnCleanUpLock();
+                }
             }
         }
     }
diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java 
b/test/org/apache/tomcat/util/net/TesterSupport.java
index c0f908ffc9..568ea1ece8 100644
--- a/test/org/apache/tomcat/util/net/TesterSupport.java
+++ b/test/org/apache/tomcat/util/net/TesterSupport.java
@@ -101,7 +101,6 @@ public final class TesterSupport {
             Library.initialize(null);
             available = true;
             version = SSL.version();
-            Library.terminatePrepare();
             Library.terminate();
         } catch (Exception | LibraryNotFoundError ex) {
             // Ignore


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to