You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hivemind.apache.org by hl...@apache.org on 2005/12/18 01:14:15 UTC

svn commit: r357405 - in /jakarta/hivemind/branches/maven2-dec12-2005: ./ framework/src/main/java/org/apache/hivemind/impl/ framework/src/main/java/org/apache/hivemind/impl/servicemodel/ framework/src/main/java/org/apache/hivemind/service/ framework/sr...

Author: hlship
Date: Sat Dec 17 16:13:59 2005
New Revision: 357405

URL: http://svn.apache.org/viewcvs?rev=357405&view=rev
Log:
HIVEMIND-162: Performance bottleneck with threaded services
HIVEMIND-161: ThreadLocal object is never removed in ThreadEventNotifierImpl and holds the classloader
  

Added:
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TheadedServiceModelTest.java
      - copied, changed from r355764, jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadedModel.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadEventNotifierTest.java
      - copied, changed from r355764, jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadEventNotifier.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadLocalStorageTest.java
      - copied, changed from r355764, jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadLocalStorage.java
Removed:
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadEventNotifier.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadLocalStorage.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadedModel.java
Modified:
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/InvokeFactoryServiceConstructor.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/PooledServiceModel.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/ThreadedServiceModel.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/ThreadEventNotifier.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ServiceMessages.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadEventNotifierImpl.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadLocalStorageImpl.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/META-INF/hivemodule.xml
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/org/apache/hivemind/service/impl/ServiceStrings.properties
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/StringHolder.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/impl/RegistryShutdownStringHolderImpl.java
    jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/org/apache/hivemind/impl/TestServiceModelThreading.java
    jakarta/hivemind/branches/maven2-dec12-2005/status.xml

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/InvokeFactoryServiceConstructor.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/InvokeFactoryServiceConstructor.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/InvokeFactoryServiceConstructor.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/InvokeFactoryServiceConstructor.java Sat Dec 17 16:13:59 2005
@@ -50,10 +50,29 @@
     /** The parameters converted to objects as per the factory's parameter schema. */
     private List _convertedParameters;
 
-    // TODO: Should this method be synchronized?
-
     public Object constructCoreServiceImplementation()
     {
+        setupFactoryAndParameters();
+
+        try
+        {
+            ServiceImplementationFactoryParameters factoryParameters = new ServiceImplementationFactoryParametersImpl(
+                    _serviceExtensionPoint, _contributingModule, _convertedParameters);
+
+            return _factory.createCoreServiceImplementation(factoryParameters);
+        }
+        catch (Exception ex)
+        {
+            throw new ApplicationRuntimeException(ex.getMessage(), getLocation(), ex);
+        }
+    }
+
+    // A lot of changes to synchronization and service construction occured between 1.1 and 1.2;
+    // this method was split off and made synchronized ... otherwise, it was possible for the
+    // pooled or threaded services to get into a potential race condition through this code.
+
+    private synchronized void setupFactoryAndParameters()
+    {
         if (_factory == null)
         {
             ServicePoint factoryPoint = _contributingModule.getServicePoint(_factoryServiceId);
@@ -74,18 +93,6 @@
             _convertedParameters = processor.getElements();
 
             checkParameterCounts(errorLog, expected);
-        }
-
-        try
-        {
-            ServiceImplementationFactoryParameters factoryParameters = new ServiceImplementationFactoryParametersImpl(
-                    _serviceExtensionPoint, _contributingModule, _convertedParameters);
-
-            return _factory.createCoreServiceImplementation(factoryParameters);
-        }
-        catch (Exception ex)
-        {
-            throw new ApplicationRuntimeException(ex.getMessage(), getLocation(), ex);
         }
     }
 

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/PooledServiceModel.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/PooledServiceModel.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/PooledServiceModel.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/PooledServiceModel.java Sat Dec 17 16:13:59 2005
@@ -41,13 +41,13 @@
      */
     protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
 
-    private Object _serviceProxy;
+    private final Object _serviceProxy;
 
-    private ThreadEventNotifier _notifier;
+    private final ThreadEventNotifier _notifier;
 
-    private ThreadLocal _activeService;
+    private final ThreadLocal _activeService = new ThreadLocal();
 
-    private List _servicePool;
+    private final List _servicePool = new ArrayList();
 
     /** @since 1.1 */
 
@@ -120,22 +120,18 @@
         super(servicePoint);
 
         _serviceInterface = servicePoint.getServiceInterface();
-    }
 
-    public synchronized Object getService()
-    {
-        if (_notifier == null)
-        {
-            Module module = getServicePoint().getModule();
+        Module module = getServicePoint().getModule();
 
-            _notifier = (ThreadEventNotifier) module.getService(
-                    HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
-                    ThreadEventNotifier.class);
-        }
+        _notifier = (ThreadEventNotifier) module.getService(
+                HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
+                ThreadEventNotifier.class);
 
-        if (_serviceProxy == null)
-            _serviceProxy = constructServiceProxy();
+        _serviceProxy = constructServiceProxy();
+    }
 
+    public Object getService()
+    {
         return _serviceProxy;
     }
 
@@ -165,11 +161,8 @@
         return outerProxy;
     }
 
-    public synchronized Object getServiceImplementationForCurrentThread()
+    public Object getServiceImplementationForCurrentThread()
     {
-        if (_activeService == null)
-            _activeService = new ThreadLocal();
-
         PooledService pooled = (PooledService) _activeService.get();
 
         if (pooled == null)
@@ -197,7 +190,7 @@
 
     private synchronized PooledService getServiceFromPool()
     {
-        int count = _servicePool == null ? 0 : _servicePool.size();
+        int count = _servicePool.size();
 
         if (count == 0)
             return null;
@@ -207,13 +200,10 @@
 
     private synchronized void returnServiceToPool(PooledService pooled)
     {
-        if (_servicePool == null)
-            _servicePool = new ArrayList();
-
         _servicePool.add(pooled);
     }
 
-    private synchronized PooledService constructPooledService()
+    private PooledService constructPooledService()
     {
         try
         {
@@ -238,8 +228,6 @@
 
     private void unbindPooledServiceFromCurrentThread(PooledService pooled)
     {
-        _notifier.removeThreadCleanupListener(pooled);
-
         _activeService.set(null);
 
         pooled.passivate();

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/ThreadedServiceModel.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/ThreadedServiceModel.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/ThreadedServiceModel.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/impl/servicemodel/ThreadedServiceModel.java Sat Dec 17 16:13:59 2005
@@ -39,9 +39,14 @@
      */
     protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
 
-    private Object _serviceProxy;
+    private final Object _serviceProxy;
 
-    private ThreadEventNotifier _notifier;
+    private final ThreadEventNotifier _notifier;
+
+    /**
+     * Used to store the active service for the current thread.
+     */
+    private final ThreadLocal _activeService = new ThreadLocal();
 
     /** @since 1.1 */
 
@@ -52,12 +57,20 @@
         super(servicePoint);
 
         _serviceInterface = servicePoint.getServiceInterface();
+
+        Module module = getServicePoint().getModule();
+
+        _notifier = (ThreadEventNotifier) module.getService(
+                HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
+                ThreadEventNotifier.class);
+
+        _serviceProxy = createServiceProxy();
     }
 
     class CleanupListener implements ThreadCleanupListener
     {
         // The core itself
-        private Object _core;
+        private final Object _core;
 
         CleanupListener(Object core)
         {
@@ -66,9 +79,6 @@
 
         public void threadDidCleanup()
         {
-            // Orhpan this object
-            _notifier.removeThreadCleanupListener(this);
-
             unbindServiceFromCurrentThread();
 
             if (_core instanceof Discardable)
@@ -81,30 +91,12 @@
     }
 
     /**
-     * Used to store the active service for the current thread.
-     */
-    private ThreadLocal _activeService;
-
-    /**
      * Always returns the service proxy.
      */
-    public synchronized Object getService()
+    public Object getService()
     {
-        // _activeService will be null on first invocation; a good
-        // time to create it, the proxy, and find the notifier.
-
-        if (_activeService == null)
-        {
-            _activeService = new ThreadLocal();
-
-            Module module = getServicePoint().getModule();
-
-            _notifier = (ThreadEventNotifier) module.getService(
-                    HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
-                    ThreadEventNotifier.class);
-
-            _serviceProxy = createServiceProxy();
-        }
+        // In 1.1 and earlier, we would lazily create the _serviceProxy here; but that meant the
+        // method had to be synchronized, which created a choke point.
 
         // The result is an interceptor stack, where the final (most deeply nested) object
         // is the serviceProxy. The serviceProxy obtains the instance for the current thread

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/ThreadEventNotifier.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/ThreadEventNotifier.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/ThreadEventNotifier.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/ThreadEventNotifier.java Sat Dec 17 16:13:59 2005
@@ -15,27 +15,34 @@
 package org.apache.hivemind.service;
 
 /**
- * Service which acts as a dispatch hub for events about the lifecycle of
- * the current thread.
- *
+ * Service which acts as a dispatch hub for events about the lifecycle of the current thread.
+ * <p>
+ * Note: prior to release 1.2, the ThreadEventNotifier implementation would retain the listeners
+ * after {@link #fireThreadCleanup()}, which could allow certain threads to retain a reference to a
+ * listener, and thus that listener's class loader, even after the an application redeployment,
+ * resulting in a massive memory leak. Starting with release 1.2, all listeners are discarded by
+ * {@link #fireThreadCleanup()}.
+ * 
  * @author Howard Lewis Ship
  */
 public interface ThreadEventNotifier
 {
     /**
-     * Adds the listener.
+     * Adds the listener. The notifier retains the listener until {@link #fireThreadCleanup()} is
+     * invoked, at which point is discarded.
      */
     public void addThreadCleanupListener(ThreadCleanupListener listener);
 
     /**
-     * Removes the listener, if it has been previously added.  If the listener
-     * has been added multiple times, only one instance is removed.
+     * Removes the listener, if it has been previously added. If the listener has been added
+     * multiple times, only one instance is removed. Note that this method is rarely used, because
+     * all listeners are automatically removed by {@link #fireThreadCleanup()}.
      */
     public void removeThreadCleanupListener(ThreadCleanupListener listener);
 
     /**
-     * Invokes {@link ThreadCleanupListener#threadDidCleanup()} on all
-     * listeners.
+     * Invokes {@link ThreadCleanupListener#threadDidCleanup()} on all listeners, and discards the
+     * list of listeners.
      */
     public void fireThreadCleanup();
 }

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ServiceMessages.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ServiceMessages.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ServiceMessages.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ServiceMessages.java Sat Dec 17 16:13:59 2005
@@ -163,4 +163,11 @@
     {
         return _formatter.format("unable-to-create-interface", name, cause);
     }
+
+    /** @since 1.2 */
+
+    static String threadCleanupException(Throwable cause)
+    {
+        return _formatter.format("thread-cleanup-exception", cause);
+    }
 }

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadEventNotifierImpl.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadEventNotifierImpl.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadEventNotifierImpl.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadEventNotifierImpl.java Sat Dec 17 16:13:59 2005
@@ -16,19 +16,38 @@
 
 import java.util.Iterator;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.hivemind.service.ThreadCleanupListener;
 import org.apache.hivemind.service.ThreadEventNotifier;
+import org.apache.hivemind.util.Defense;
 import org.apache.hivemind.util.EventListenerList;
 
 /**
- * Implementation of {@link org.apache.hivemind.service.ThreadEventNotifier},
- * available as service <code>hivemind.ThreadEventNotifier</code>.
- *
+ * Implementation of {@link org.apache.hivemind.service.ThreadEventNotifier}, available as service
+ * <code>hivemind.ThreadEventNotifier</code>.
+ * 
  * @author Howard Lewis Ship
  */
 public class ThreadEventNotifierImpl implements ThreadEventNotifier
 {
-    private ThreadLocal _storage = new ThreadLocal();
+    private static final Log DEFAULT_LOG = LogFactory.getLog(ThreadEventNotifier.class);
+
+    private final Log _log;
+
+    private final ThreadLocal _storage = new ThreadLocal();
+
+    public ThreadEventNotifierImpl()
+    {
+        this(DEFAULT_LOG);
+    }
+
+    public ThreadEventNotifierImpl(Log log)
+    {
+        Defense.notNull(log, "log");
+
+        _log = log;
+    }
 
     public void addThreadCleanupListener(ThreadCleanupListener listener)
     {
@@ -45,7 +64,7 @@
 
     public void removeThreadCleanupListener(ThreadCleanupListener listener)
     {
-		EventListenerList list = (EventListenerList) _storage.get();
+        EventListenerList list = (EventListenerList) _storage.get();
 
         if (list != null)
             list.removeListener(listener);
@@ -57,24 +76,35 @@
         // are free to unregister as listeners from threadDidCleanup() and
         // we need to avoid concurrent modification errors.
 
-		EventListenerList list = (EventListenerList) _storage.get();
+        EventListenerList list = (EventListenerList) _storage.get();
 
         if (list == null)
             return;
 
         Iterator i = list.getListeners();
 
+        // Discard the list of listeners as early as possible to ensure that
+        // they can in no way be retained, even if this thread aborts abnormally.
+
+        _storage.set(null);
+
         while (i.hasNext())
         {
             ThreadCleanupListener listener = (ThreadCleanupListener) i.next();
 
-			// Each listener may decide to remove itself; that's OK,
-			// EventListenerList handles that kind of concurrent modification
-			// well.
-			
-            listener.threadDidCleanup();
+            // Each listener may decide to remove itself; that's OK,
+            // EventListenerList handles that kind of concurrent modification
+            // well.
+
+            try
+            {
+                listener.threadDidCleanup();
+            }
+            catch (RuntimeException ex)
+            {
+                _log.warn(ServiceMessages.threadCleanupException(ex), ex);
+            }
         }
-
     }
 
 }

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadLocalStorageImpl.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadLocalStorageImpl.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadLocalStorageImpl.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/java/org/apache/hivemind/service/impl/ThreadLocalStorageImpl.java Sat Dec 17 16:13:59 2005
@@ -17,103 +17,34 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.hivemind.service.ThreadCleanupListener;
-import org.apache.hivemind.service.ThreadEventNotifier;
 import org.apache.hivemind.service.ThreadLocalStorage;
 
 /**
  * Implementation of {@link org.apache.hivemind.service.ThreadLocalStorage}.
+ * <p>
+ * Starting with release 1.2, this implementation was simplified, and its service model changed to
+ * threaded.
  * 
  * @author Howard Lewis Ship, Harish Krishnaswamy
  */
-public class ThreadLocalStorageImpl implements ThreadLocalStorage, ThreadCleanupListener
+public class ThreadLocalStorageImpl implements ThreadLocalStorage
 {
-    private static final String INITIALIZED_KEY =
-        "$org.apache.hivemind.service.impl.ThreadLocalStorageImpl#initialized$";
+    // Created anew for each instance of TLS in each thread.
 
-    private CleanableThreadLocal _local = new CleanableThreadLocal();
-    private ThreadEventNotifier _notifier;
-
-    private static class CleanableThreadLocal extends ThreadLocal
-    {
-        /**
-         * <p>
-         * Intializes the variable with a HashMap containing a single Boolean flag to denote the
-         * initialization of the variable. The Boolean flag will be used to determine when to
-         * register the listener with {@link ThreadEventNotifier}.
-         * <p>
-         * The registration cannot be done from here because it may get lost once the caller method (
-         * {@link ThreadLocal#get()}or {@link ThreadLocal#set(java.lang.Object)} completes, if
-         * this was the first ThreadLocal variable access for the Thread.
-         */
-        protected Object initialValue()
-        {
-            // NOTE: This is a workaround to circumvent the ThreadLocal behavior.
-            // It would be easier if the implementation of ThreadLocal.get() checked for
-            // the existence of the thread local map, after initialValue() is evaluated,
-            // and used it instead of creating a new map always after initialization (possibly
-            // overwriting any variables created from within ThreadLocal.initialValue()).
-
-            Map map = new HashMap();
-            map.put(INITIALIZED_KEY, Boolean.TRUE);
-
-            return map;
-        }
-    }
-
-    /**
-     * Gets the thread local variable and registers the listener with {@link ThreadEventNotifier}
-     * if the thread local variable has been initialized. The registration cannot be done from
-     * within {@link CleanableThreadLocal#initialValue()} because the notifier's thread local
-     * variable will be overwritten and the listeners for the thread will be lost.
-     */
-    private Map getThreadLocalVariable()
-    {
-        Map map = (Map) _local.get();
-
-        if (Boolean.TRUE.equals(map.get(INITIALIZED_KEY)) && _notifier != null)
-        {
-            _notifier.addThreadCleanupListener(this);
-
-            map.remove(INITIALIZED_KEY);
-        }
-
-        return map;
-    }
+    private final Map _map = new HashMap();
 
     public Object get(String key)
     {
-        Map map = getThreadLocalVariable();
-
-        return map.get(key);
+        return _map.get(key);
     }
 
     public void put(String key, Object value)
     {
-        Map map = getThreadLocalVariable();
-
-        map.put(key, value);
+        _map.put(key, value);
     }
 
     public void clear()
     {
-        Map map = (Map) _local.get();
-
-        if (map != null)
-            map.clear();
-    }
-
-    public void setNotifier(ThreadEventNotifier notifier)
-    {
-        _notifier = notifier;
+        _map.clear();
     }
-
-    /**
-     * Invokes {@link #clear()}.
-     */
-    public void threadDidCleanup()
-    {
-        clear();
-    }
-
 }

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/META-INF/hivemodule.xml
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/META-INF/hivemodule.xml?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/META-INF/hivemodule.xml (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/META-INF/hivemodule.xml Sat Dec 17 16:13:59 2005
@@ -486,12 +486,10 @@
     Service which manages a thread-local map of data items. 
     This can be used for temporary storage of information when local variables can't be used. 
     All stored items are released when the thread is cleaned up.
+    Note: this service should be considered deprecated; use the threaded service model
+    instead.
       
-    <invoke-factory>
-      <construct class="service.impl.ThreadLocalStorageImpl">
-        <set-service property="notifier" service-id="ThreadEventNotifier"/>
-      </construct>
-    </invoke-factory>
+    <create-instance model="threaded" class="service.impl.ThreadLocalStorageImpl"/>
   </service-point>
   
   <configuration-point id="ServiceModels">

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/org/apache/hivemind/service/impl/ServiceStrings.properties
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/org/apache/hivemind/service/impl/ServiceStrings.properties?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/org/apache/hivemind/service/impl/ServiceStrings.properties (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/main/resources/org/apache/hivemind/service/impl/ServiceStrings.properties Sat Dec 17 16:13:59 2005
@@ -35,4 +35,5 @@
 invalid-service-property-locator=''{0}'' is not a valid locator for use with the service-property object provider. It should be the id of a service, a comma, and the name of a property provided by that service.
 failure-building-service=Error building service {0}: {1}
 autowire-property-failure=Unable to autowire property {0} of service {1}: {2}
-unable-to-find-autowire-constructor=Unable to find constructor applicable for autowiring. Use explicit constructor parameters.
\ No newline at end of file
+unable-to-find-autowire-constructor=Unable to find constructor applicable for autowiring. Use explicit constructor parameters.
+thread-cleanup-exception=Thread cleanup exception: {0}
\ No newline at end of file

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/StringHolder.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/StringHolder.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/StringHolder.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/StringHolder.java Sat Dec 17 16:13:59 2005
@@ -15,7 +15,7 @@
 package hivemind.test.services;
 
 /**
- * Used as part of {@link hivemind.test.services.TestThreadedModel}. 
+ * Used as part of {@link hivemind.test.services.TheadedServiceModelTest}. 
  *
  * @author Howard Lewis Ship
  */

Copied: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TheadedServiceModelTest.java (from r355764, jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadedModel.java)
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TheadedServiceModelTest.java?p2=jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TheadedServiceModelTest.java&p1=jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadedModel.java&r1=355764&r2=357405&rev=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadedModel.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TheadedServiceModelTest.java Sat Dec 17 16:13:59 2005
@@ -25,7 +25,7 @@
  * 
  * @author Howard Lewis Ship
  */
-public class TestThreadedModel extends FrameworkTestCase
+public class TheadedServiceModelTest extends FrameworkTestCase
 {
     /**
      * Runnable that executes in another thread to ensure that the data really is seperate.

Copied: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadEventNotifierTest.java (from r355764, jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadEventNotifier.java)
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadEventNotifierTest.java?p2=jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadEventNotifierTest.java&p1=jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadEventNotifier.java&r1=355764&r2=357405&rev=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadEventNotifier.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadEventNotifierTest.java Sat Dec 17 16:13:59 2005
@@ -14,6 +14,7 @@
 
 package hivemind.test.services;
 
+import org.apache.commons.logging.Log;
 import org.apache.hivemind.service.ThreadCleanupListener;
 import org.apache.hivemind.service.ThreadEventNotifier;
 import org.apache.hivemind.service.impl.ThreadEventNotifierImpl;
@@ -22,10 +23,10 @@
 
 /**
  * Tests for {@link org.apache.hivemind.service.impl.ThreadEventNotifierImpl}.
- *
+ * 
  * @author Howard Lewis Ship
  */
-public class TestThreadEventNotifier extends FrameworkTestCase
+public class ThreadEventNotifierTest extends FrameworkTestCase
 {
     static class Listener implements ThreadCleanupListener
     {
@@ -38,24 +39,6 @@
 
     }
 
-    static class ConcurMod implements ThreadCleanupListener
-    {
-        boolean _cleanup;
-        ThreadEventNotifier _notifier;
-
-        ConcurMod(ThreadEventNotifier notifier)
-        {
-            _notifier = notifier;
-        }
-
-        public void threadDidCleanup()
-        {
-            _cleanup = true;
-            _notifier.removeThreadCleanupListener(this);
-        }
-
-    }
-
     public void testAdd()
     {
         ThreadEventNotifier n = new ThreadEventNotifierImpl();
@@ -84,33 +67,51 @@
         assertEquals(true, l2._cleanup);
     }
 
-    public void testConcur()
+    public void testNotifierClearsListenerListAfterFire()
     {
         ThreadEventNotifier n = new ThreadEventNotifierImpl();
 
-        Listener l1 = new Listener();
-        ConcurMod l2 = new ConcurMod(n);
-        Listener l3 = new Listener();
+        Listener l = new Listener();
 
-        n.addThreadCleanupListener(l1);
-        n.addThreadCleanupListener(l2);
-        n.addThreadCleanupListener(l3);
+        n.addThreadCleanupListener(l);
 
         n.fireThreadCleanup();
 
-        assertEquals(true, l1._cleanup);
-        assertEquals(true, l2._cleanup);
-        assertEquals(true, l3._cleanup);
+        assertEquals(true, l._cleanup);
 
-        l1._cleanup = false;
-        l2._cleanup = false;
-        l3._cleanup = false;
+        l._cleanup = false;
 
         n.fireThreadCleanup();
 
-        assertEquals(true, l1._cleanup);
-        assertEquals(false, l2._cleanup);
-        assertEquals(true, l3._cleanup);
+        // Don't expect a notification, because the notifier's list is gone
+
+        assertEquals(false, l._cleanup);
     }
 
+    public void testListenerThrowsException()
+    {
+        Log log = (Log) newMock(Log.class);
+
+        final RuntimeException re = new RuntimeException("Listener Failure");
+
+        log.warn("Thread cleanup exception: Listener Failure", re);
+
+        replayControls();
+
+        ThreadCleanupListener l = new ThreadCleanupListener()
+        {
+            public void threadDidCleanup()
+            {
+                throw re;
+            }
+        };
+
+        ThreadEventNotifier n = new ThreadEventNotifierImpl(log);
+
+        n.addThreadCleanupListener(l);
+
+        n.fireThreadCleanup();
+
+        verifyControls();
+    }
 }

Copied: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadLocalStorageTest.java (from r355764, jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadLocalStorage.java)
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadLocalStorageTest.java?p2=jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadLocalStorageTest.java&p1=jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadLocalStorage.java&r1=355764&r2=357405&rev=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/TestThreadLocalStorage.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/ThreadLocalStorageTest.java Sat Dec 17 16:13:59 2005
@@ -16,9 +16,7 @@
 
 import hivemind.test.FrameworkTestCase;
 
-import org.apache.hivemind.service.ThreadEventNotifier;
 import org.apache.hivemind.service.ThreadLocalStorage;
-import org.apache.hivemind.service.impl.ThreadEventNotifierImpl;
 import org.apache.hivemind.service.impl.ThreadLocalStorageImpl;
 
 /**
@@ -26,11 +24,9 @@
  * 
  * @author Howard Lewis Ship, Harish Krishnaswamy
  */
-public class TestThreadLocalStorage extends FrameworkTestCase
+public class ThreadLocalStorageTest extends FrameworkTestCase
 {
     private ThreadLocalStorage _s = new ThreadLocalStorageImpl();
-    private Throwable          _testRunnerFailure;
-    private boolean            _testRunnerCompleted;
 
     public void testGetEmpty()
     {
@@ -60,86 +56,5 @@
         _s.clear();
 
         assertNull(_s.get("foo"));
-    }
-
-    public void testWithNotifier()
-    {
-        ThreadEventNotifier notifier = new ThreadEventNotifierImpl();
-        ThreadLocalStorageImpl s = new ThreadLocalStorageImpl();
-
-        s.setNotifier(notifier);
-
-        s.put("biff", "bamf");
-
-        assertEquals("bamf", s.get("biff"));
-
-        notifier.fireThreadCleanup();
-
-        assertNull(s.get("biff"));
-    }
-
-    private class TestRunner implements Runnable
-    {
-        private ThreadLocalStorage  _local;
-        private ThreadEventNotifier _notifier;
-
-        private TestRunner(ThreadLocalStorage local, ThreadEventNotifier notifier)
-        {
-            _local = local;
-            _notifier = notifier;
-        }
-
-        public void run()
-        {
-            _local.put("session", "Test Runner Session");
-
-            assertEquals(_local.get("session"), "Test Runner Session");
-
-            _notifier.fireThreadCleanup();
-
-            assertNull(_local.get("session"));
-
-            _testRunnerCompleted = true;
-        }
-    }
-
-    private class TestThreadGroup extends ThreadGroup
-    {
-        public TestThreadGroup(String name)
-        {
-            super(name);
-        }
-
-        public void uncaughtException(Thread th, Throwable t)
-        {
-            _testRunnerFailure = t;
-            _testRunnerCompleted = true;
-        }
-    }
-
-    public void testThreadCleanup() throws Throwable
-    {
-        ThreadEventNotifier notifier = new ThreadEventNotifierImpl();
-        ThreadLocalStorageImpl local = new ThreadLocalStorageImpl();
-
-        local.setNotifier(notifier);
-
-        local.put("session", "Main Session");
-
-        TestRunner tr = new TestRunner(local, notifier);
-        TestThreadGroup tg = new TestThreadGroup("Test Thread Group");
-        new Thread(tg, tr).start();
-
-        while (!_testRunnerCompleted)
-            Thread.yield();
-
-        if (_testRunnerFailure != null)
-            throw _testRunnerFailure;
-
-        assertEquals(local.get("session"), "Main Session");
-
-        notifier.fireThreadCleanup();
-
-        assertNull(local.get("session"));
     }
 }

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/impl/RegistryShutdownStringHolderImpl.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/impl/RegistryShutdownStringHolderImpl.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/impl/RegistryShutdownStringHolderImpl.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/hivemind/test/services/impl/RegistryShutdownStringHolderImpl.java Sat Dec 17 16:13:59 2005
@@ -14,7 +14,7 @@
 
 package hivemind.test.services.impl;
 
-import hivemind.test.services.TestThreadedModel;
+import hivemind.test.services.TheadedServiceModelTest;
 
 import org.apache.hivemind.events.RegistryShutdownListener;
 
@@ -31,7 +31,7 @@
 
     public void registryDidShutdown()
     {
-        TestThreadedModel._didShutdown = true;
+        TheadedServiceModelTest._didShutdown = true;
     }
 
 }

Modified: jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/org/apache/hivemind/impl/TestServiceModelThreading.java
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/org/apache/hivemind/impl/TestServiceModelThreading.java?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/org/apache/hivemind/impl/TestServiceModelThreading.java (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/framework/src/test/java/org/apache/hivemind/impl/TestServiceModelThreading.java Sat Dec 17 16:13:59 2005
@@ -18,16 +18,18 @@
 import org.apache.hivemind.test.HiveMindTestCase;
 
 /**
- * Tests to verify that the service models work properly even under high-thread count
- * concurrent stress.
- *
+ * Tests to verify that the service models work properly even under high-thread count concurrent
+ * stress.
+ * 
  * @author Howard Lewis Ship
  */
 public class TestServiceModelThreading extends HiveMindTestCase
 {
-    public static final int THREAD_COUNT = 20;
-    public static final int ITERATIONS = 10;
-    public static final long JOIN_WAIT = 500;
+    public static final int THREAD_COUNT = 150;
+
+    public static final int ITERATIONS = 20;
+
+    public static final long JOIN_WAIT = 1000;
 
     private static class RunnableFixture implements Runnable
     {
@@ -47,8 +49,11 @@
     private static class RunnableManager implements Runnable
     {
         private Registry _registry;
+
         private Worker _worker;
+
         private Runnable _runnable;
+
         private boolean _completed = false;
 
         private RunnableManager(Registry registry, Worker worker, Runnable runnable)
@@ -113,28 +118,28 @@
         }
 
         assertEquals(
-            "Number of executions of the RunnableFixture",
-            THREAD_COUNT * ITERATIONS,
-            fixture.getInvokeCount());
+                "Number of executions of the RunnableFixture",
+                THREAD_COUNT * ITERATIONS,
+                fixture.getInvokeCount());
     }
 
     public void testPrimitive() throws Exception
     {
         execute("hivemind.test.threading.PrimitiveWorker");
     }
-    
+
     public void testSingleton() throws Exception
     {
-    	execute("hivemind.test.threading.SingletonWorker");
+        execute("hivemind.test.threading.SingletonWorker");
     }
-    
+
     public void testThreaded() throws Exception
     {
-    	execute("hivemind.test.threading.ThreadedWorker");
+        execute("hivemind.test.threading.ThreadedWorker");
     }
-    
+
     public void testPooled() throws Exception
     {
-    	execute("hivemind.test.threading.PooledWorker");
+        execute("hivemind.test.threading.PooledWorker");
     }
 }

Modified: jakarta/hivemind/branches/maven2-dec12-2005/status.xml
URL: http://svn.apache.org/viewcvs/jakarta/hivemind/branches/maven2-dec12-2005/status.xml?rev=357405&r1=357404&r2=357405&view=diff
==============================================================================
--- jakarta/hivemind/branches/maven2-dec12-2005/status.xml (original)
+++ jakarta/hivemind/branches/maven2-dec12-2005/status.xml Sat Dec 17 16:13:59 2005
@@ -32,6 +32,7 @@
   <changes>
     <release version="1.2-alpha-1" date="unreleased">
       <action type="fix" dev="HLS" fixes-bug="HIVEMIND-162">Performance bottleneck with threaded services</action>
+      <action type="fix" dev="HLS" fixes-bug="HIVEMIND-161">ThreadLocal object is never removed in ThreadEventNotifierImpl and holds the classloader</action>
     </release>
     <release version="1.1" date="unreleased">
       <action type="update" dev="HLS">Change project web site navigation</action>



---------------------------------------------------------------------
To unsubscribe, e-mail: hivemind-cvs-unsubscribe@jakarta.apache.org
For additional commands, e-mail: hivemind-cvs-help@jakarta.apache.org