On Tue, Jun 28, 2016 at 12:37 PM, Rainer Jung <rainer.j...@kippdata.de> wrote:
> Am 28.06.2016 um 18:06 schrieb therealnewo...@gmail.com:
>>
>> On Tue, Jun 28, 2016 at 11:51 AM, Rainer Jung <rainer.j...@kippdata.de>
>> wrote:
>>>
>>> Am 28.06.2016 um 16:07 schrieb Mark Thomas:
>>>>
>>>>
>>>> On 28/06/2016 12:28, Mark Thomas wrote:
>>>>>
>>>>>
>>>>> On 28/06/2016 11:34, Rainer Jung wrote:
>>>>>>
>>>>>>
>>>>>> Am 28.06.2016 um 11:15 schrieb Mark Thomas:
>>>>>
>>>>>
>>>>>
>>>>> <snip />
>>>>>
>>>>>>> Index: src/ssl.c
>>>>>>> ===================================================================
>>>>>>> --- src/ssl.c    (revision 1750259)
>>>>>>> +++ src/ssl.c    (working copy)
>>>>>>> @@ -420,6 +420,10 @@
>>>>>>>      return psaptr->PSATOLD;
>>>>>>>  #elif defined(WIN32)
>>>>>>>      return (unsigned long)GetCurrentThreadId();
>>>>>>> +#elif defined(DARWIN)
>>>>>>> +    uint64_t tid;
>>>>>>> +    pthread_threadid_np(NULL, &tid);
>>>>>>> +    return (unsigned long)tid;
>>>>>>>  #else
>>>>>>>      return (unsigned long)(apr_os_thread_current());
>>>>>>>  #endif
>>>>>>>
>>>>>>>
>>>>>>> I want to do some similar testing for Linux before adding what I
>>>>>>> suspect
>>>>>>> will be a very similar block using gettid().
>>>>>>
>>>>>>
>>>>>>
>>>>>> We could either add something to configure.in. Untested:
>>>>>>
>>>>>> Index: native/configure.in
>>>>>> ===================================================================
>>>>>> --- native/configure.in (revision 1750462)
>>>>>> +++ native/configure.in (working copy)
>>>>>> @@ -218,6 +218,9 @@
>>>>>>      *-solaris2*)
>>>>>>          APR_ADDTO(TCNATIVE_LIBS, -lkstat)
>>>>>>          ;;
>>>>>> +    *linux*)
>>>>>> +        APR_ADDTO(CFLAGS, -DTCNATIVE_LINUX)
>>>>>> +        ;;
>>>>>>      *)
>>>>>>          ;;
>>>>>>  esac
>>>>>>
>>>>>>
>>>>>> and then use a
>>>>>>
>>>>>> #ifdef TCNATIVE_LINUX
>>>>>>
>>>>>> or we copy some other more direct linux check from e.g. APR:
>>>>>>
>>>>>> #ifdef __linux__
>>>>>>
>>>>>> The latter looks simpler, but the version above is based on all the
>>>>>> logic put into config.guess.
>>>>>
>>>>>
>>>>>
>>>>> I'd go with the __linux__ option as that is consistent with what we
>>>>> already use in os/unix/system.c
>>>>>
>>>>> I'm not against the change to configure.in, I just think we should be
>>>>> consistent with how we do this throughout the code base.
>>>>
>>>>
>>>>
>>>> I've confirmed that the same problem occurs with hash bucket selection
>>>> on linux and that switching to gettid() fixes that problem.
>>>>
>>>> I'm going to go ahead with the 1.2.8 release shortly. We can continue to
>>>> refine this as necessary and have a more complete fix in 1.2.9.
>>>
>>>
>>>
>>> I did a quick check on Solaris. apr_os_thread_current() uses pthread_self
>>> on
>>> Solaris like on Linux (actually on any Unix type OS), but unlike Linux
>>> where
>>> this returns a address which is either 32 or 64 bit aligned depending on
>>> address size, on Solaris you get an increasing number starting with 1 for
>>> the first thread and incremented by one for each following thread. Thread
>>> IDs do not get reused in the same process, even if the thread finished,
>>> but
>>> thread IDs are common between different processes, because they always
>>> start
>>> with 1. So Solaris should be fine as-is.
>>
>>
>> Does the value have a cap? If not then Solaris will just continue to
>> use more and more memory as threads are created over the lifetime of
>> the server.
>
>
> No cap. I think everyone agrees we are still looking at cleaning up at
> thread death as a further improvement. Whether there's a problem without it
> depends on the executor size and whether the executor actually destroys and
> recreates threads (thus using more IDs than the max thread count).
>

Mark,

The change which was added to 1.2.8 works great on 1.1.34. I also
hacked in calling ERR_remove_thread_state on thread exit. It is
similar to my original change but only does do it on a threads exit,
but as Remy pointed out earlier this will only work if all IO
operations occur on one of the original threads from the thread pool.

-nate

Here is the hack just for completeness.

diff --git a/java/org/apache/catalina/core/StandardThreadExecutor.java
b/java/org/apache/catalina/core/StandardThreadExecutor.java
index a30f29c..80f429d 100644
--- a/java/org/apache/catalina/core/StandardThreadExecutor.java
+++ b/java/org/apache/catalina/core/StandardThreadExecutor.java
@@ -18,6 +18,7 @@
 package org.apache.catalina.core;

 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;

 import org.apache.catalina.Executor;
@@ -27,10 +28,11 @@ import org.apache.catalina.util.LifecycleMBeanBase;
 import org.apache.tomcat.util.threads.ResizableExecutor;
 import org.apache.tomcat.util.threads.TaskQueue;
 import org.apache.tomcat.util.threads.TaskThreadFactory;
+import org.apache.tomcat.util.threads.ThreadCreator;
 import org.apache.tomcat.util.threads.ThreadPoolExecutor;

 public class StandardThreadExecutor extends LifecycleMBeanBase
-        implements Executor, ResizableExecutor {
+        implements Executor, ResizableExecutor, ThreadCreator {

     // ---------------------------------------------- Properties
     /**
@@ -319,6 +321,10 @@ public class StandardThreadExecutor extends
LifecycleMBeanBase
         return false;
     }

+    @Override
+    public ThreadFactory getThreadFactory() {
+        return executor.getThreadFactory();
+    }

     @Override
     protected String getDomainInternal() {
diff --git a/java/org/apache/tomcat/jni/SSL.java
b/java/org/apache/tomcat/jni/SSL.java
index b726a54..fb9fc16 100644
--- a/java/org/apache/tomcat/jni/SSL.java
+++ b/java/org/apache/tomcat/jni/SSL.java
@@ -388,4 +388,10 @@ public final class SSL {
      * @return true if all SSL_OP_* are supported by OpenSSL library.
      */
     public static native boolean hasOp(int op);
+
+    /**
+     * Cleanup thread specific data structures. This needs to be
called after a thread
+     * has completed ssl operations.
+     */
+    public static native void threadCleanup();
 }
diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java
b/java/org/apache/tomcat/util/net/AprEndpoint.java
index 74d813f..2edf673 100644
--- a/java/org/apache/tomcat/util/net/AprEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;

 import org.apache.juli.logging.Log;
@@ -50,6 +51,8 @@ import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.net.AbstractEndpoint.Acceptor.AcceptorState;
 import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
 import org.apache.tomcat.util.security.PrivilegedSetTccl;
+import org.apache.tomcat.util.threads.TaskThreadFactory;
+import org.apache.tomcat.util.threads.ThreadCreator;


 /**
@@ -670,6 +673,14 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
                 createExecutor();
             }

+            Executor executor = getExecutor();
+            if (isSSLEnabled() && executor instanceof ThreadCreator) {
+                ThreadFactory tf = ((ThreadCreator)
executor).getThreadFactory();
+                if (tf instanceof TaskThreadFactory) {
+                    ((TaskThreadFactory) tf).addCleanupTask(SSL_CLEANUP);
+                }
+            }
+
             initializeConnectionLatch();

             // Start poller thread
@@ -1024,6 +1035,13 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
         return log;
     }

+    private static final Runnable SSL_CLEANUP = new Runnable() {
+        @Override
+        public void run() {
+            SSL.threadCleanup();
+        }
+    };
+
     // --------------------------------------------------- Acceptor Inner Class
     /**
      * The background thread that listens for incoming TCP/IP connections and
diff --git a/java/org/apache/tomcat/util/threads/TaskThreadFactory.java
b/java/org/apache/tomcat/util/threads/TaskThreadFactory.java
index af63f11..12a4eb7 100644
--- a/java/org/apache/tomcat/util/threads/TaskThreadFactory.java
+++ b/java/org/apache/tomcat/util/threads/TaskThreadFactory.java
@@ -16,6 +16,8 @@
  */
 package org.apache.tomcat.util.threads;

+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
 /**
@@ -29,6 +31,8 @@ public class TaskThreadFactory implements ThreadFactory {
     private final String namePrefix;
     private final boolean daemon;
     private final int threadPriority;
+    private final Collection<Runnable> cleanupTasks = new
CopyOnWriteArrayList<Runnable>();
+
     public TaskThreadFactory(String namePrefix, boolean daemon, int priority) {
         SecurityManager s = System.getSecurityManager();
         group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
@@ -38,11 +42,36 @@ public class TaskThreadFactory implements ThreadFactory {
     }

     @Override
-    public Thread newThread(Runnable r) {
-        TaskThread t = new TaskThread(group, r, namePrefix +
threadNumber.getAndIncrement());
+    public Thread newThread(final Runnable r) {
+        Runnable wrapped = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    r.run();
+                } finally {
+                    for (Runnable task : cleanupTasks) {
+                        task.run();
+                    }
+                }
+            }
+        };
+        TaskThread t = new TaskThread(group, wrapped, namePrefix +
threadNumber.getAndIncrement());
         t.setDaemon(daemon);
         t.setPriority(threadPriority);
         return t;
     }

+    public void addCleanupTask(Runnable task) {
+        synchronized (cleanupTasks) {
+            if (!cleanupTasks.contains(task)) {
+                cleanupTasks.add(task);
+            }
+        }
+    }
+
+    public boolean removeCleanupTask(Runnable task) {
+        synchronized (cleanupTasks) {
+            return cleanupTasks.remove(task);
+        }
+    }
 }
diff --git a/java/org/apache/tomcat/util/threads/ThreadCreator.java
b/java/org/apache/tomcat/util/threads/ThreadCreator.java
new file mode 100644
index 0000000..94270b5
--- /dev/null
+++ b/java/org/apache/tomcat/util/threads/ThreadCreator.java
@@ -0,0 +1,17 @@
+/*
+ * ========================================================================
+ *
+ * Copyright (c) by Hitachi Data Systems, 2016. All rights reserved.
+ *
+ * ========================================================================
+ */
+package org.apache.tomcat.util.threads;
+
+import java.util.concurrent.ThreadFactory;
+
+public interface ThreadCreator {
+    /**
+     * @return The {@link ThreadFactory} used by this instance
+     */
+    ThreadFactory getThreadFactory();
+}
diff --git a/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
b/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
index bc84ad3..1479783 100644
--- a/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
+++ b/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
@@ -34,7 +34,7 @@ import org.apache.tomcat.util.res.StringManager;
  * @author fhanik
  *
  */
-public class ThreadPoolExecutor extends
java.util.concurrent.ThreadPoolExecutor {
+public class ThreadPoolExecutor extends
java.util.concurrent.ThreadPoolExecutor implements ThreadCreator {
     /**
      * The string manager for this package.
      */

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

Reply via email to