You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by re...@apache.org on 2021/12/13 11:33:27 UTC

[uima-uimaj] branch bugfix/UIMA-6400-UimaContextHolder-threadlocal-can-leak created (now 8ede6d4)

This is an automated email from the ASF dual-hosted git repository.

rec pushed a change to branch bugfix/UIMA-6400-UimaContextHolder-threadlocal-can-leak
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git.


      at 8ede6d4  [UIMA-6400] UimaContextHolder threadlocal can leak

This branch includes the following new commits:

     new 8ede6d4  [UIMA-6400] UimaContextHolder threadlocal can leak

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[uima-uimaj] 01/01: [UIMA-6400] UimaContextHolder threadlocal can leak

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rec pushed a commit to branch bugfix/UIMA-6400-UimaContextHolder-threadlocal-can-leak
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git

commit 8ede6d4ddbfc0189dabc4d7a3083338fc3b4ba5b
Author: Richard Eckart de Castilho <re...@apache.org>
AuthorDate: Mon Dec 13 12:33:14 2021 +0100

    [UIMA-6400] UimaContextHolder threadlocal can leak
    
    - Use a weak reference for the context in the context holder by default so that threads that are inheriting the context holder don't prevent the context from being garbage collected
---
 .../src/docbook/ref.xml.component_descriptor.xml   |  10 ++
 .../java/org/apache/uima/UimaContextHolder.java    | 123 ++++++++++++++++++---
 2 files changed, 116 insertions(+), 17 deletions(-)

diff --git a/uima-docbook-references/src/docbook/ref.xml.component_descriptor.xml b/uima-docbook-references/src/docbook/ref.xml.component_descriptor.xml
index ec084c6..5982b13 100644
--- a/uima-docbook-references/src/docbook/ref.xml.component_descriptor.xml
+++ b/uima-docbook-references/src/docbook/ref.xml.component_descriptor.xml
@@ -2090,6 +2090,16 @@ if (uimaContext != null) {
 			will be null if <literal>getUimaContext</literal> is not invoked by an annotator or flow
 			controller on the same thread or a child thread.
             </para>
+            <para>
+            Since UIMA 3.2.1, the context is stored in the InheritableThreadLocal as a weak reference.
+            This ensures that any long-running threads spawned while the context is set do not 
+            prevent garbage-collection of the context when the context is destroyed. If a child
+            thread should really retain a strong reference to the context, it should obtain the
+            context and store it in a field or in another ThreadLocal variable. For backwards
+            compatibility, the old behavior of using a strong reference by default can be enabled
+            by setting the system property <literal>uima.context_holder_reference_type</literal>
+            to <literal>STRONG</literal>.
+            </para>
           </section>
 
           <section id="&tp;aes.other_uses_for_external_configuration_parameters">
diff --git a/uimaj-core/src/main/java/org/apache/uima/UimaContextHolder.java b/uimaj-core/src/main/java/org/apache/uima/UimaContextHolder.java
index 9520b87..47fa839 100644
--- a/uimaj-core/src/main/java/org/apache/uima/UimaContextHolder.java
+++ b/uimaj-core/src/main/java/org/apache/uima/UimaContextHolder.java
@@ -16,50 +16,139 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.uima;
 
+import static java.util.Collections.synchronizedMap;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.uima.internal.util.Misc;
+
 /**
- * This class holds the UimaContext for the current thread, or a parent thread.
- * The getContext method may be used by any plain Java class invoked by an annotator,
- * The POJO must run in the same thread or a child thread of the annotator.
+ * This class holds the UimaContext for the current thread, or a parent thread. The getContext
+ * method may be used by any plain Java class invoked by an annotator, The POJO must run in the same
+ * thread or a child thread of the annotator.
  * 
- * For example a POJO can access the shared External Override Settings with:
- *     String paramValue = UimaContextHolder.getContext().getSetting(paramName);
+ * For example a POJO can access the shared External Override Settings with: String paramValue =
+ * UimaContextHolder.getContext().getSetting(paramName);
  */
 public class UimaContextHolder {
-  
-  private static InheritableThreadLocal<UimaContext> threadLocalContext = new InheritableThreadLocal<>();
-  
+
+  private static final String TRACK_CONTEXT_HOLDER_TRACKING = "uima.enable_context_holder_tracking";
+  private static final String CONTEXT_HOLDER_REFERENCE_TYPE = "uima.context_holder_reference_type";
+
+  private static final boolean IS_TRACK_CONTEXT_HOLDER_TRACKING = Misc
+          .getNoValueSystemProperty(TRACK_CONTEXT_HOLDER_TRACKING);
+
+  private static final ContextHolderReferenceType CONTEXT_HOLDER_REFERENCE_TYPE_VALUE = ContextHolderReferenceType
+          .valueOf(System.getProperty(CONTEXT_HOLDER_REFERENCE_TYPE,
+                  ContextHolderReferenceType.WEAK.toString()));
+
+  private static final InheritableThreadLocal<Object> THREAD_LOCAL_CONTEXT = new InheritableThreadLocal<>();
+
+  private static final Map<UimaContext, StackTraceElement[]> CONTEXT_SETTERS = IS_TRACK_CONTEXT_HOLDER_TRACKING
+          ? synchronizedMap(new WeakHashMap<>())
+          : null;
+
+  private UimaContextHolder() {
+    // No instances
+  }
+
   /**
    * Get the UimaContext for this thread
    * 
-   * @return      the thread-specific UimaContext
+   * @return the thread-specific UimaContext
    */
   public static UimaContext getContext() {
-    return threadLocalContext.get();
+    Object obj = THREAD_LOCAL_CONTEXT.get();
+
+    if (!(obj instanceof Reference)) {
+      return (UimaContext) obj;
+    }
+
+    @SuppressWarnings("unchecked")
+    Reference<UimaContext> ref = (Reference<UimaContext>) obj;
+    UimaContext context = ref.get();
+    if (context == null) {
+      THREAD_LOCAL_CONTEXT.set(null);
+    }
+
+    return context;
   }
-  
+
   /**
    * Sets the UimaContext for the current thread.
    * <p>
    * NOTE - Should be used only by the UIMA Framework.
    * 
-   * @param uimaContext - new UimaContext for this thread
+   * @param uimaContext
+   *          - new UimaContext for this thread
    * @return - previous UimaContext for this thread
    */
   public static UimaContext setContext(UimaContext uimaContext) {
-    UimaContext prevContext = threadLocalContext.get();
-    threadLocalContext.set(uimaContext);
+    Object prevContextObj = THREAD_LOCAL_CONTEXT.get();
+    @SuppressWarnings("unchecked")
+    UimaContext prevContext = prevContextObj instanceof Reference
+            ? ((Reference<UimaContext>) prevContextObj).get()
+            : (UimaContext) prevContextObj;
+
+    if (uimaContext == null) {
+      // Clear context
+      THREAD_LOCAL_CONTEXT.set(null);
+      if (prevContext != null && CONTEXT_SETTERS != null) {
+        CONTEXT_SETTERS.remove(prevContext);
+      }
+    } else {
+      // Set context with the configured reference type
+      THREAD_LOCAL_CONTEXT.set(makeRef(uimaContext));
+      if (CONTEXT_SETTERS != null) {
+        CONTEXT_SETTERS.put(uimaContext, new Exception().getStackTrace());
+      }
+    }
+
     return prevContext;
   }
-  
+
+  private static Object makeRef(UimaContext aContext) {
+    switch (CONTEXT_HOLDER_REFERENCE_TYPE_VALUE) {
+      case SOFT:
+        return new SoftReference<>(aContext);
+      case WEAK:
+        return new WeakReference<>(aContext);
+      case STRONG:
+        return aContext;
+      default:
+        throw new IllegalArgumentException(
+                "Unsupported reference type: [" + CONTEXT_HOLDER_REFERENCE_TYPE_VALUE + "]");
+    }
+  }
+
   /**
    * Clears the UimaContext entry for the current thread
    * <p>
    * NOTE - Should be used only by the UIMA Framework.
    */
   public static void clearContext() {
-    threadLocalContext.set(null);
+    if (CONTEXT_SETTERS != null) {
+      Object prevContextObj = THREAD_LOCAL_CONTEXT.get();
+      @SuppressWarnings("unchecked")
+      UimaContext prevContext = prevContextObj instanceof Reference
+              ? ((Reference<UimaContext>) prevContextObj).get()
+              : (UimaContext) prevContextObj;
+
+      if (prevContext != null) {
+        CONTEXT_SETTERS.remove(prevContext);
+      }
+    }
+
+    THREAD_LOCAL_CONTEXT.set(null);
+  }
+
+  private enum ContextHolderReferenceType {
+    STRONG, WEAK, SOFT;
   }
 }