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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]