Skip to content

Commit

Permalink
ArC - fix @TransientReference destruction
Browse files Browse the repository at this point in the history
- remove the transient dependent instance from the parent creational
context when the invocation completes
- fixes quarkusio#27906

(cherry picked from commit bcffcb4)
  • Loading branch information
mkouba authored and gsmet committed Sep 20, 2022
1 parent 2cfc8a3 commit f2f8c86
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 13 deletions.
Expand Up @@ -47,16 +47,17 @@ public synchronized boolean hasDependentInstances() {
return dependentInstances != null && !dependentInstances.isEmpty();
}

boolean destroyDependentInstance(Object dependentInstance) {
synchronized (this) {
if (dependentInstances != null) {
for (Iterator<InstanceHandle<?>> iterator = dependentInstances.iterator(); iterator.hasNext();) {
InstanceHandle<?> instanceHandle = iterator.next();
if (instanceHandle.get() == dependentInstance) {
instanceHandle.destroy();
iterator.remove();
return true;
public synchronized boolean removeDependentInstance(Object dependentInstance, boolean destroy) {
if (dependentInstances != null) {
for (Iterator<InstanceHandle<?>> it = dependentInstances.iterator(); it.hasNext();) {
InstanceHandle<?> handle = it.next();
// The reference equality is used on purpose!
if (handle.get() == dependentInstance) {
if (destroy) {
handle.destroy();
}
it.remove();
return true;
}
}
}
Expand Down
Expand Up @@ -11,22 +11,29 @@ private InjectableReferenceProviders() {

/**
* Unwraps the provider if necessary and invokes {@link Contextual#destroy(Object, CreationalContext)}.
* <p>
* If there is a parent context available then attempt to remove the dependent instance.
*
* @param <T>
* @param provider
* @param instance
* @param creationalContext
* @throws IllegalArgumentException If the specified provider is not a bean
*/
@SuppressWarnings("unchecked")
public static <T> void destroy(InjectableReferenceProvider<T> provider, T instance,
CreationalContext<T> creationalContext) {
if (provider instanceof CurrentInjectionPointProvider) {
provider = ((CurrentInjectionPointProvider<T>) provider).getDelegate();
}
if (provider instanceof Contextual) {
@SuppressWarnings("unchecked")
Contextual<T> contextual = (Contextual<T>) provider;
contextual.destroy(instance, creationalContext);
CreationalContextImpl<T> ctx = CreationalContextImpl.unwrap(creationalContext);
CreationalContextImpl<?> parent = ctx.getParent();
if (parent != null) {
parent.removeDependentInstance(instance, false);
}
} else {
throw new IllegalArgumentException("Injetable reference provider is not a bean: " + provider.getClass());
}
Expand Down
Expand Up @@ -138,7 +138,7 @@ public void destroy(Object instance) {
context.destroy(proxy.arc_bean());
} else {
// First try to destroy a dependent instance
if (!creationalContext.destroyDependentInstance(instance)) {
if (!creationalContext.removeDependentInstance(instance, true)) {
// If not successful then try the singleton context
SingletonContext singletonContext = (SingletonContext) Arc.container().getActiveContext(Singleton.class);
singletonContext.destroyInstance(instance);
Expand Down
Expand Up @@ -5,6 +5,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

import io.quarkus.arc.Arc;
import io.quarkus.arc.InstanceHandle;
import io.quarkus.arc.test.ArcTestContainer;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
Expand All @@ -30,24 +31,35 @@ public class TransientReferenceDestroyedTest {

@Test
public void testTransientReferences() {
Controller controller = Arc.container().instance(Controller.class).get();
InstanceHandle<Controller> controllerHandle = Arc.container().instance(Controller.class);
Controller controller = controllerHandle.get();
assertNotNull(controller.theBeer);
assertTrue(Arc.container().instance(Integer.class).get() > 0);
assertEquals(3, BeerProducer.DESTROYED.size(), "Destroyed beers: " + BeerProducer.DESTROYED);
assertTrue(BeerProducer.DESTROYED.contains(1));
assertTrue(BeerProducer.DESTROYED.contains(2));
assertTrue(BeerProducer.DESTROYED.contains(3));

controllerHandle.destroy();
// Controller.theBeer is also destroyed
assertEquals(4, BeerProducer.DESTROYED.size());

BeerProducer.COUNTER.set(0);
BeerProducer.DESTROYED.clear();

InterceptedController interceptedController = Arc.container().instance(InterceptedController.class).get();
InstanceHandle<InterceptedController> interceptedControllerHandle = Arc.container()
.instance(InterceptedController.class);
InterceptedController interceptedController = interceptedControllerHandle.get();
assertNotNull(interceptedController.getTheBeer());
assertTrue(Arc.container().instance(Long.class).get() > 0);
assertEquals(3, BeerProducer.DESTROYED.size(), "Destroyed beers: " + BeerProducer.DESTROYED);
assertTrue(BeerProducer.DESTROYED.contains(1));
assertTrue(BeerProducer.DESTROYED.contains(2));
assertTrue(BeerProducer.DESTROYED.contains(3));

interceptedControllerHandle.destroy();
// InterceptedController.theBeer is also destroyed
assertEquals(4, BeerProducer.DESTROYED.size());
}

@Singleton
Expand Down

0 comments on commit f2f8c86

Please sign in to comment.