Author: slaurent
Date: Mon Dec  6 20:49:14 2010
New Revision: 1042786

URL: http://svn.apache.org/viewvc?rev=1042786&view=rev
Log:
bug 49159: Improve ThreadLocal memory leak clean-up 
https://issues.apache.org/bugzilla/show_bug.cgi?id=49159

Various fixes after review by markt : formatting, use string manager, svn 
props...

Modified:
    tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
    tomcat/trunk/java/org/apache/catalina/core/StandardThreadExecutor.java
    
tomcat/trunk/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
   (contents, props changed)
    tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
    tomcat/trunk/java/org/apache/tomcat/util/threads/Constants.java   
(contents, props changed)
    tomcat/trunk/java/org/apache/tomcat/util/threads/LocalStrings.properties   
(props changed)
    tomcat/trunk/java/org/apache/tomcat/util/threads/TaskQueue.java
    tomcat/trunk/java/org/apache/tomcat/util/threads/TaskThread.java   
(contents, props changed)
    tomcat/trunk/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java

Modified: tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties Mon Dec  
6 20:49:14 2010
@@ -238,6 +238,8 @@ standardWrapper.unavailable=Marking serv
 standardWrapper.unloadException=Servlet {0} threw unload() exception
 standardWrapper.unloading=Cannot allocate servlet {0} because it is being 
unloaded
 standardWrapper.waiting=Waiting for {0} instance(s) to be deallocated
+threadLocalLeakPreventionListener.lifecycleEvent.error=Exception processing 
lifecycle event {0}
+threadLocalLeakPreventionListener.containerEvent.error=Exception processing 
container event {0}
 
 defaultInstanceManager.restrictedServletsResource=Restricted servlets property 
file not found
 defaultInstanceManager.privilegedServlet=Servlet of class {0} is privileged 
and cannot be loaded by this web application

Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Mon Dec  6 
20:49:14 2010
@@ -2489,9 +2489,10 @@ public class StandardContext extends Con
         return this.renewThreadsWhenStoppingContext;
     }
 
-    public void setRenewThreadsWhenStoppingContext(boolean 
renewThreadsWhenStoppingContext) {
+    public void setRenewThreadsWhenStoppingContext(
+            boolean renewThreadsWhenStoppingContext) {
         boolean oldRenewThreadsWhenStoppingContext =
-            this.renewThreadsWhenStoppingContext;
+                this.renewThreadsWhenStoppingContext;
         this.renewThreadsWhenStoppingContext = renewThreadsWhenStoppingContext;
         support.firePropertyChange("renewThreadsWhenStoppingContext",
                 oldRenewThreadsWhenStoppingContext,

Modified: tomcat/trunk/java/org/apache/catalina/core/StandardThreadExecutor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardThreadExecutor.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardThreadExecutor.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardThreadExecutor.java Mon 
Dec  6 20:49:14 2010
@@ -88,7 +88,8 @@ public class StandardThreadExecutor exte
      * renewing all threads at the same time, this delay is observed between 2
      * threads being renewed.
      */
-    protected long threadRenewalDelay = 1000L;
+    protected long threadRenewalDelay = 
+        org.apache.tomcat.util.threads.Constants.DEFAULT_THREAD_RENEWAL_DELAY;
     
     private TaskQueue taskqueue = null;
     // ---------------------------------------------- Constructors

Modified: 
tomcat/trunk/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- 
tomcat/trunk/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
 (original)
+++ 
tomcat/trunk/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
 Mon Dec  6 20:49:14 2010
@@ -34,25 +34,34 @@ import org.apache.catalina.connector.Con
 import org.apache.coyote.ProtocolHandler;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
 import org.apache.tomcat.util.threads.ThreadPoolExecutor;
 
 /**
+ * <p>
  * A {...@link LifecycleListener} that triggers the renewal of threads in 
Executor
  * pools when a {...@link Context} is being stopped to avoid thread-local 
related
- * memory leaks.<br/>
+ * memory leaks.
+ * </p>
+ * <p>
  * Note : active threads will be renewed one by one when they come back to the
  * pool after executing their task, see
- * {...@link 
org.apache.tomcat.util.threads.ThreadPoolExecutor}.afterExecute().<br/>
+ * {...@link org.apache.tomcat.util.threads.ThreadPoolExecutor}.afterExecute().
+ * </p>
  * 
  * This listener must be declared in server.xml to be active.
  * 
- * @author slaurent
- * 
  */
 public class ThreadLocalLeakPreventionListener implements LifecycleListener,
-        ContainerListener {
-    private static final Log log = LogFactory
-            .getLog(ThreadLocalLeakPreventionListener.class);
+    ContainerListener {
+    private static final Log log =
+        LogFactory.getLog(ThreadLocalLeakPreventionListener.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
 
     /**
      * Listens for {...@link LifecycleEvent} for the start of the {...@link 
Server} to
@@ -63,7 +72,7 @@ public class ThreadLocalLeakPreventionLi
         try {
             Lifecycle lifecycle = event.getLifecycle();
             if (Lifecycle.AFTER_START_EVENT.equals(event.getType())
-                    && lifecycle instanceof Server) {
+                && lifecycle instanceof Server) {
                 // when the server starts, we register ourself as listener for
                 // all context
                 // as well as container event listener so that we know when new
@@ -73,11 +82,15 @@ public class ThreadLocalLeakPreventionLi
             }
 
             if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())
-                    && lifecycle instanceof Context) {
+                && lifecycle instanceof Context) {
                 stopIdleThreads((Context) lifecycle);
             }
         } catch (Exception e) {
-            log.error("Exception processing event " + event, e);
+            String msg =
+                sm.getString(
+                    "threadLocalLeakPreventionListener.lifecycleEvent.error",
+                    event);
+            log.error(msg, e);
         }
     }
 
@@ -87,13 +100,17 @@ public class ThreadLocalLeakPreventionLi
             String type = event.getType();
             if (Container.ADD_CHILD_EVENT.equals(type)) {
                 processContainerAddChild(event.getContainer(),
-                        (Container) event.getData());
+                    (Container) event.getData());
             } else if (Container.REMOVE_CHILD_EVENT.equals(type)) {
                 processContainerRemoveChild(event.getContainer(),
-                        (Container) event.getData());
+                    (Container) event.getData());
             }
         } catch (Exception e) {
-            log.error("Exception processing event " + event, e);
+            String msg =
+                sm.getString(
+                    "threadLocalLeakPreventionListener.containerEvent.error",
+                    event);
+            log.error(msg, e);
         }
 
     }
@@ -129,43 +146,35 @@ public class ThreadLocalLeakPreventionLi
     protected void processContainerAddChild(Container parent, Container child) 
{
         if (log.isDebugEnabled())
             log.debug("Process addChild[parent=" + parent + ",child=" + child
-                    + "]");
+                + "]");
 
-        try {
-            if (child instanceof Context) {
-                registerContextListener((Context) child);
-            } else if (child instanceof Engine) {
-                registerListenersForEngine((Engine) child);
-            } else if (child instanceof Host) {
-                registerListenersForHost((Host) child);
-            }
-        } catch (Throwable t) {
-            log.error("processContainerAddChild: Throwable", t);
+        if (child instanceof Context) {
+            registerContextListener((Context) child);
+        } else if (child instanceof Engine) {
+            registerListenersForEngine((Engine) child);
+        } else if (child instanceof Host) {
+            registerListenersForHost((Host) child);
         }
 
     }
 
-    protected void processContainerRemoveChild(Container parent, Container 
child) {
+    protected void processContainerRemoveChild(Container parent, 
+        Container child) {
 
         if (log.isDebugEnabled())
             log.debug("Process removeChild[parent=" + parent + ",child="
-                    + child + "]");
+                + child + "]");
 
-        try {
-            if (child instanceof Context) {
-                Context context = (Context) child;
-                context.removeLifecycleListener(this);
-            } else if (child instanceof Host) {
-                Host host = (Host) child;
-                host.removeContainerListener(this);
-            } else if (child instanceof Engine) {
-                Engine engine = (Engine) child;
-                engine.removeContainerListener(this);
-            }
-        } catch (Throwable t) {
-            log.error("processContainerRemoveChild: Throwable", t);
+        if (child instanceof Context) {
+            Context context = (Context) child;
+            context.removeLifecycleListener(this);
+        } else if (child instanceof Host) {
+            Host host = (Host) child;
+            host.removeContainerListener(this);
+        } else if (child instanceof Engine) {
+            Engine engine = (Engine) child;
+            engine.removeContainerListener(this);
         }
-
     }
 
     /**
@@ -177,9 +186,8 @@ public class ThreadLocalLeakPreventionLi
      *            of its parent Service.
      */
     private void stopIdleThreads(Context context) {
-        if (context instanceof StandardContext
-                && !((StandardContext) context)
-                        .getRenewThreadsWhenStoppingContext()) {
+        if (context instanceof StandardContext && 
+            !((StandardContext) context).getRenewThreadsWhenStoppingContext()) 
{
             log.debug("Not renewing threads when the context is stopping, it 
is configured not to do it.");
             return;
         }
@@ -196,10 +204,12 @@ public class ThreadLocalLeakPreventionLi
                 }
 
                 if (executor instanceof ThreadPoolExecutor) {
-                    ThreadPoolExecutor threadPoolExecutor = 
(ThreadPoolExecutor) executor;
+                    ThreadPoolExecutor threadPoolExecutor =
+                        (ThreadPoolExecutor) executor;
                     threadPoolExecutor.contextStopping();
                 } else if (executor instanceof StandardThreadExecutor) {
-                    StandardThreadExecutor stdThreadExecutor = 
(StandardThreadExecutor) executor;
+                    StandardThreadExecutor stdThreadExecutor =
+                        (StandardThreadExecutor) executor;
                     stdThreadExecutor.contextStopping();
                 }
 

Propchange: 
tomcat/trunk/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
tomcat/trunk/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java Mon Dec 
 6 20:49:14 2010
@@ -2365,24 +2365,18 @@ public class WebappClassLoader
         } catch (IllegalAccessException e) {
             
log.warn(sm.getString("webappClassLoader.checkThreadLocalsForLeaksFail",
                     contextName), e);
-        } catch (NoSuchMethodException e) {
-            
log.warn(sm.getString("webappClassLoader.checkThreadLocalsForLeaksFail",
-                    contextName), e);
-        } catch (InvocationTargetException e) {
-            
log.warn(sm.getString("webappClassLoader.checkThreadLocalsForLeaksFail",
-                    contextName), e);
         }       
     }
 
 
-    /*
+    /**
      * Analyzes the given thread local map object. Also pass in the field that
      * points to the internal table to save re-calculating it on every
      * call to this method.
      */
-    private void checkThreadLocalMapForLeaks(Object map, Field 
internalTableField)
-            throws NoSuchMethodException, IllegalAccessException,
-            NoSuchFieldException, InvocationTargetException {
+    private void checkThreadLocalMapForLeaks(Object map,
+            Field internalTableField) throws IllegalAccessException,
+            NoSuchFieldException {
         if (map != null) {
             Object[] table = (Object[]) internalTableField.get(map);
             if (table != null) {

Modified: tomcat/trunk/java/org/apache/tomcat/util/threads/Constants.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/threads/Constants.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/threads/Constants.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/threads/Constants.java Mon Dec  6 
20:49:14 2010
@@ -23,4 +23,5 @@ public final class Constants {
 
     public static final String Package = "org.apache.tomcat.util.threads";
 
+    public static final long DEFAULT_THREAD_RENEWAL_DELAY = 1000L;
 }

Propchange: tomcat/trunk/java/org/apache/tomcat/util/threads/Constants.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: tomcat/trunk/java/org/apache/tomcat/util/threads/Constants.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: 
tomcat/trunk/java/org/apache/tomcat/util/threads/LocalStrings.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
tomcat/trunk/java/org/apache/tomcat/util/threads/LocalStrings.properties
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: tomcat/trunk/java/org/apache/tomcat/util/threads/TaskQueue.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/threads/TaskQueue.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/threads/TaskQueue.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/threads/TaskQueue.java Mon Dec  6 
20:49:14 2010
@@ -81,7 +81,8 @@ public class TaskQueue extends LinkedBlo
 
 
     @Override
-    public Runnable poll(long timeout, TimeUnit unit) throws 
InterruptedException {
+    public Runnable poll(long timeout, TimeUnit unit)
+            throws InterruptedException {
         Runnable runnable = super.poll(timeout, unit);
         if (runnable == null && parent != null) {
             // the poll timed out, it gives an opportunity to stop the current
@@ -90,21 +91,22 @@ public class TaskQueue extends LinkedBlo
         }
         return runnable;
     }
-    
 
     @Override
     public Runnable take() throws InterruptedException {
         if (parent != null && parent.currentThreadShouldBeStopped()) {
-            return poll(parent.getKeepAliveTime(TimeUnit.MILLISECONDS), 
TimeUnit.MILLISECONDS);
-            //yes, this may return null (in case of timeout) which normally 
does not occur with take()
-            //but the ThreadPoolExecutor implementation allows this
+            return poll(parent.getKeepAliveTime(TimeUnit.MILLISECONDS),
+                    TimeUnit.MILLISECONDS);
+            // yes, this may return null (in case of timeout) which normally
+            // does not occur with take()
+            // but the ThreadPoolExecutor implementation allows this
         }
         return super.take();
     }
 
     @Override
     public int remainingCapacity() {
-        if(forcedRemainingCapacity != null) {
+        if (forcedRemainingCapacity != null) {
             // ThreadPoolExecutor.setCorePoolSize checks that
             // remainingCapacity==0 to allow to interrupt idle threads
             // I don't see why, but this hack allows to conform to this

Modified: tomcat/trunk/java/org/apache/tomcat/util/threads/TaskThread.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/threads/TaskThread.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/threads/TaskThread.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/threads/TaskThread.java Mon Dec  6 
20:49:14 2010
@@ -19,8 +19,6 @@ package org.apache.tomcat.util.threads;
 /**
  * A Thread implementation that records the time at which it was created.
  * 
- * @author slaurent
- * 
  */
 public class TaskThread extends Thread {
 

Propchange: tomcat/trunk/java/org/apache/tomcat/util/threads/TaskThread.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: tomcat/trunk/java/org/apache/tomcat/util/threads/TaskThread.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: 
tomcat/trunk/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java?rev=1042786&r1=1042785&r2=1042786&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java 
Mon Dec  6 20:49:14 2010
@@ -65,7 +65,7 @@ public class ThreadPoolExecutor extends 
     /**
      * Delay in ms between 2 threads being renewed. If negative, do not renew 
threads.
      */
-    private long threadRenewalDelay = 1000L;
+    private long threadRenewalDelay = Constants.DEFAULT_THREAD_RENEWAL_DELAY;
 
     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long 
keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, 
RejectedExecutionHandler handler) {
         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 
handler);
@@ -133,9 +133,11 @@ public class ThreadPoolExecutor extends 
     }
     
     protected boolean currentThreadShouldBeStopped() {
-        if (threadRenewalDelay >= 0 && Thread.currentThread() instanceof 
TaskThread) {
+        if (threadRenewalDelay >= 0
+            && Thread.currentThread() instanceof TaskThread) {
             TaskThread currentTaskThread = (TaskThread) Thread.currentThread();
-            if (currentTaskThread.getCreationTime() < 
this.lastContextStoppedTime.longValue()) {
+            if (currentTaskThread.getCreationTime() < 
+                    this.lastContextStoppedTime.longValue()) {
                 return true;
             }
         }
@@ -160,7 +162,8 @@ public class ThreadPoolExecutor extends 
      * thread, at the discretion of the <tt>Executor</tt> implementation.
      * If no threads are available, it will be added to the work queue.
      * If the work queue is full, the system will wait for the specified 
-     * time and it throw a RejectedExecutionException if the queue is still 
full after that.
+     * time and it throw a RejectedExecutionException if the queue is still 
+     * full after that.
      *
      * @param command the runnable task
      * @throws RejectedExecutionException if this task cannot be
@@ -197,7 +200,8 @@ public class ThreadPoolExecutor extends 
 
         // save the current pool parameters to restore them later
         int savedCorePoolSize = this.getCorePoolSize();
-        TaskQueue taskQueue = getQueue() instanceof TaskQueue ? (TaskQueue) 
getQueue() : null;
+        TaskQueue taskQueue =
+                getQueue() instanceof TaskQueue ? (TaskQueue) getQueue() : 
null;
         if (taskQueue != null) {
             // note by slaurent : quite oddly 
threadPoolExecutor.setCorePoolSize
             // checks that queue.remainingCapacity()==0. I did not understand
@@ -210,16 +214,18 @@ public class ThreadPoolExecutor extends 
         this.setCorePoolSize(0);
 
         // wait a little so that idle threads wake and poll the queue again,
-        // this time always with a timeout (queue.poll() instead of 
queue.take())
-        // even if we did not wait enough, TaskQueue.take() takes care of 
timing out
-        // so that we are sure that all threads of the pool are renewed in a 
limited
-        // time, something like (threadKeepAlive + longest request time)
+        // this time always with a timeout (queue.poll() instead of
+        // queue.take())
+        // even if we did not wait enough, TaskQueue.take() takes care of 
timing
+        // out, so that we are sure that all threads of the pool are renewed in
+        // a limited time, something like 
+        // (threadKeepAlive + longest request time)
         try {
             Thread.sleep(200L);
         } catch (InterruptedException e) {
-            //yes, ignore
+            // yes, ignore
         }
-        
+
         if (taskQueue != null) {
             // ok, restore the state of the queue and pool
             taskQueue.setForcedRemainingCapacity(null);



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

Reply via email to