I've opened https://bz.apache.org/bugzilla/show_bug.cgi?id=59797 to
track this.

Nate, if you let me have your public IP, I'll get you unbanned from BZ
as well.

Mark

On 01/07/2016 15:33, Nate Clark wrote:
> 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
> 


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

Reply via email to