Fixes memory leak in per-thread context
Signed-off-by: Alexander Pinčuk <alexander.v.pinchuk@gmail.com>
diff --git a/hk2-api/src/main/java/org/glassfish/hk2/internal/PerThreadContext.java b/hk2-api/src/main/java/org/glassfish/hk2/internal/PerThreadContext.java
index 0ae3a88..244cbc9 100755
--- a/hk2-api/src/main/java/org/glassfish/hk2/internal/PerThreadContext.java
+++ b/hk2-api/src/main/java/org/glassfish/hk2/internal/PerThreadContext.java
@@ -1,6 +1,7 @@
/*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 Contributors to Eclipse Foundation.
* Copyright (c) 2023 Payara Foundation and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,13 +18,13 @@
package org.glassfish.hk2.internal;
+import jakarta.inject.Singleton;
+
import java.lang.annotation.Annotation;
-import org.glassfish.hk2.utilities.CleanerFactory;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
-
-import jakarta.inject.Singleton;
+import java.util.Map;
import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.Context;
@@ -31,6 +32,7 @@
import org.glassfish.hk2.api.PerThread;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.Visibility;
+import org.glassfish.hk2.utilities.CleanerFactory;
import org.glassfish.hk2.utilities.general.Hk2ThreadLocal;
import org.glassfish.hk2.utilities.reflection.Logger;
@@ -39,21 +41,18 @@
*/
@Singleton @Visibility(DescriptorVisibility.LOCAL)
public class PerThreadContext implements Context<PerThread> {
- private final static boolean LOG_THREAD_DESTRUCTION = AccessController.<Boolean>doPrivileged(new PrivilegedAction<Boolean>() {
- @Override
- public Boolean run() {
- return Boolean.parseBoolean(System.getProperty("org.hk2.debug.perthreadcontext.log", "false"));
- }
-
- });
+ private final static boolean LOG_THREAD_DESTRUCTION = AccessController.doPrivileged(
+ (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("org.hk2.debug.perthreadcontext.log"));
- private final Hk2ThreadLocal<PerContextThreadWrapper> threadMap =
- new Hk2ThreadLocal<PerContextThreadWrapper>() {
- public PerContextThreadWrapper initialValue() {
- return new PerContextThreadWrapper();
- }
- };
+ private final Hk2ThreadLocal<PerThreadContextWrapper> threadMap =
+ new Hk2ThreadLocal<>() {
+
+ @Override
+ public PerThreadContextWrapper initialValue() {
+ return new PerThreadContextWrapper();
+ }
+ };
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Context#getScope()
@@ -68,14 +67,12 @@
*/
@SuppressWarnings("unchecked")
@Override
- public <U> U findOrCreate(ActiveDescriptor<U> activeDescriptor,
- ServiceHandle<?> root) {
+ public <U> U findOrCreate(ActiveDescriptor<U> activeDescriptor, ServiceHandle<?> root) {
U retVal = (U) threadMap.get().get(activeDescriptor);
if (retVal == null) {
retVal = activeDescriptor.create(root);
threadMap.get().put(activeDescriptor, retVal);
}
-
return retVal;
}
@@ -115,39 +112,58 @@
public void destroyOne(ActiveDescriptor<?> descriptor) {
// per-thread instances live for the life of the thread,
// so we will ignore any request to destroy a descriptor
-
}
- private static class PerContextThreadWrapper {
+ private static class PerThreadContextWrapper {
- private final HashMap<ActiveDescriptor<?>, Object> instances = new HashMap<>();
- private final long id = Thread.currentThread().getId();
+ private final CleanableContext context = new CleanableContext();
- public PerContextThreadWrapper() {
+ public PerThreadContextWrapper() {
registerStopEvent();
}
- public boolean has(ActiveDescriptor<?> d) {
- return instances.containsKey(d);
+ public boolean has(ActiveDescriptor<?> descriptor) {
+ return context.has(descriptor);
}
- public Object get(ActiveDescriptor<?> d) {
- return instances.get(d);
+ public Object get(ActiveDescriptor<?> descriptor) {
+ return context.get(descriptor);
}
- public void put(ActiveDescriptor<?> d, Object v) {
- instances.put(d, v);
+ public void put(ActiveDescriptor<?> descriptor, Object value) {
+ context.put(descriptor, value);
}
public final void registerStopEvent() {
- CleanerFactory.create().register(this, () -> {
- instances.clear();
-
- if (LOG_THREAD_DESTRUCTION) {
- Logger.getLogger().debug("Removing PerThreadContext data for thread " + id);
- }
- });
+ CleanerFactory.create().register(this, context);
}
-
+ }
+
+ private static final class CleanableContext implements Runnable {
+
+ private final Map<ActiveDescriptor<?>, Object> instances = new HashMap<>();
+
+ private final long id = Thread.currentThread().getId();
+
+ public boolean has(ActiveDescriptor<?> descriptor) {
+ return instances.containsKey(descriptor);
+ }
+
+ public Object get(ActiveDescriptor<?> descriptor) {
+ return instances.get(descriptor);
+ }
+
+ public void put(ActiveDescriptor<?> descriptor, Object value) {
+ instances.put(descriptor, value);
+ }
+
+ @Override
+ public void run() {
+ instances.clear();
+
+ if (LOG_THREAD_DESTRUCTION) {
+ Logger.getLogger().debug("Removing PerThreadContext data for thread " + id);
+ }
+ }
}
}