Skip to content

Commit

Permalink
Merge pull request quarkusio#39837 from Ladicek/arc-avoid-thread-locals
Browse files Browse the repository at this point in the history
ArC: replace thread locals with fields in CreationalContextImpl
  • Loading branch information
Ladicek committed Apr 15, 2024
2 parents ed82c3f + 2cde5a6 commit 1e7403a
Show file tree
Hide file tree
Showing 18 changed files with 166 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public Object create(CreationalContext<Object> creationalContext, Map<String, Ob
throw new IllegalStateException("Cannot load required type: " + requiredType);
}

InjectionPoint injectionPoint = InjectionPointProvider.get();
InjectionPoint injectionPoint = InjectionPointProvider.getCurrent(creationalContext);
if (injectionPoint == null) {
throw new IllegalStateException("No current injection point found");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.jboss.logging.Logger;

import io.quarkus.arc.Arc;
import io.quarkus.arc.impl.InjectionPointProvider;
import io.quarkus.runtime.ExecutionMode;
import io.quarkus.runtime.annotations.StaticInitSafe;
import io.smallrye.config.SmallRyeConfig;
Expand All @@ -37,7 +36,14 @@ public class ConfigStaticInitCheckInterceptor {

@AroundInvoke
Object aroundInvoke(InvocationContext context) throws Exception {
recordConfigValue(null, configValues);
InjectionPoint injectionPoint = null;
for (Object parameter : context.getParameters()) {
if (parameter instanceof InjectionPoint) {
injectionPoint = (InjectionPoint) parameter;
break;
}
}
recordConfigValue(injectionPoint, configValues);
return context.proceed();
}

Expand All @@ -46,9 +52,6 @@ static void recordConfigValue(InjectionPoint injectionPoint, ConfigStaticInitVal
// No-op for any other execution mode
return;
}
if (injectionPoint == null) {
injectionPoint = InjectionPointProvider.get();
}
if (injectionPoint == null) {
throw new IllegalStateException("No current injection point found");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class RawOptionalClaimCreator implements BeanCreator<Optional<?>> {

@Override
public Optional<?> create(CreationalContext<Optional<?>> creationalContext, Map<String, Object> params) {
InjectionPoint injectionPoint = InjectionPointProvider.get();
InjectionPoint injectionPoint = InjectionPointProvider.getCurrent(creationalContext);
if (injectionPoint == null) {
throw new IllegalStateException("No current injection point found");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1305,43 +1305,77 @@ private ResultHandle newInstanceHandle(BeanInfo bean, ClassCreator beanCreator,
if (Modifier.isPrivate(constructor.flags())) {
privateMembers.add(isApplicationClass,
String.format("Bean constructor %s on %s", constructor, constructor.declaringClass().name()));
ResultHandle paramTypesArray = creator.newArray(Class.class, creator.load(providerHandles.size()));
ResultHandle argsArray = creator.newArray(Object.class, creator.load(providerHandles.size()));
int params = providerHandles.size();
if (DecoratorGenerator.isAbstractDecoratorImpl(bean, providerTypeName)) {
params++;
}
ResultHandle paramTypesArray = creator.newArray(Class.class, creator.load(params));
ResultHandle argsArray = creator.newArray(Object.class, creator.load(params));
for (int i = 0; i < injectionPoints.size(); i++) {
creator.writeArrayValue(paramTypesArray, i,
creator.loadClass(injectionPoints.get(i).getType().name().toString()));
creator.writeArrayValue(argsArray, i, providerHandles.get(i));
}
if (DecoratorGenerator.isAbstractDecoratorImpl(bean, providerTypeName)) {
creator.writeArrayValue(paramTypesArray, params - 1, creator.loadClass(CreationalContext.class));
creator.writeArrayValue(argsArray, params - 1, createMethod.getMethodParam(0));
}
registration.registerMethod(constructor);
return creator.invokeStaticMethod(MethodDescriptors.REFLECTIONS_NEW_INSTANCE,
creator.loadClass(constructor.declaringClass().name().toString()),
paramTypesArray, argsArray);
} else {
// new SimpleBean(foo)
String[] paramTypes = new String[injectionPoints.size()];
int params = injectionPoints.size();
if (DecoratorGenerator.isAbstractDecoratorImpl(bean, providerTypeName)) {
params++;
}
String[] paramTypes = new String[params];
for (ListIterator<InjectionPointInfo> iterator = injectionPoints.listIterator(); iterator.hasNext();) {
InjectionPointInfo injectionPoint = iterator.next();
paramTypes[iterator.previousIndex()] = DescriptorUtils.typeToString(injectionPoint.getType());
}
return creator.newInstance(MethodDescriptor.ofConstructor(providerTypeName, paramTypes),
providerHandles.toArray(new ResultHandle[0]));
ResultHandle[] args = new ResultHandle[params];
for (int i = 0; i < providerHandles.size(); i++) {
args[i] = providerHandles.get(i);
}
if (DecoratorGenerator.isAbstractDecoratorImpl(bean, providerTypeName)) {
paramTypes[params - 1] = CreationalContext.class.getName();
args[params - 1] = createMethod.getMethodParam(0);
}
return creator.newInstance(MethodDescriptor.ofConstructor(providerTypeName, paramTypes), args);
}
} else {
MethodInfo noArgsConstructor = bean.getTarget().get().asClass().method(Methods.INIT);
if (Modifier.isPrivate(noArgsConstructor.flags())) {
privateMembers.add(isApplicationClass,
String.format("Bean constructor %s on %s", noArgsConstructor,
noArgsConstructor.declaringClass().name()));
ResultHandle paramTypesArray = creator.newArray(Class.class, creator.load(0));
ResultHandle argsArray = creator.newArray(Object.class, creator.load(0));
ResultHandle paramTypesArray;
ResultHandle argsArray;
if (DecoratorGenerator.isAbstractDecoratorImpl(bean, providerTypeName)) {
paramTypesArray = creator.newArray(Class.class, 1);
argsArray = creator.newArray(Object.class, 1);
creator.writeArrayValue(paramTypesArray, 0, creator.loadClass(CreationalContext.class));
creator.writeArrayValue(argsArray, 0, createMethod.getMethodParam(0));
} else {
paramTypesArray = creator.newArray(Class.class, 0);
argsArray = creator.newArray(Object.class, 0);
}

registration.registerMethod(noArgsConstructor);
return creator.invokeStaticMethod(MethodDescriptors.REFLECTIONS_NEW_INSTANCE,
creator.loadClass(noArgsConstructor.declaringClass().name().toString()), paramTypesArray,
argsArray);
} else {
// new SimpleBean()
return creator.newInstance(MethodDescriptor.ofConstructor(providerTypeName));
if (DecoratorGenerator.isAbstractDecoratorImpl(bean, providerTypeName)) {
// new SimpleDecorator_Impl(ctx)
return creator.newInstance(MethodDescriptor.ofConstructor(providerTypeName, CreationalContext.class),
createMethod.getMethodParam(0));
} else {
// new SimpleBean()
return creator.newInstance(MethodDescriptor.ofConstructor(providerTypeName));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -16,6 +17,8 @@
import java.util.function.Predicate;
import java.util.function.Supplier;

import jakarta.enterprise.context.spi.CreationalContext;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
Expand Down Expand Up @@ -157,6 +160,10 @@ static String createBaseName(ClassInfo decoratorClass) {
return baseName;
}

static boolean isAbstractDecoratorImpl(BeanInfo bean, String providerTypeName) {
return bean.isDecorator() && ((DecoratorInfo) bean).isAbstract() && providerTypeName.endsWith(ABSTRACT_IMPL_SUFFIX);
}

private String generateDecoratorImplementation(String baseName, String targetPackage, DecoratorInfo decorator,
ClassInfo decoratorClass, ClassOutput classOutput) {
// MyDecorator_Impl
Expand All @@ -171,8 +178,13 @@ private String generateDecoratorImplementation(String baseName, String targetPac

// Constructor
MethodInfo decoratorConstructor = decoratorClass.firstMethod(Methods.INIT);
List<String> decoratorConstructorParams = new ArrayList<>();
for (org.jboss.jandex.Type parameterType : decoratorConstructor.parameterTypes()) {
decoratorConstructorParams.add(parameterType.name().toString());
}
decoratorConstructorParams.add(CreationalContext.class.getName());
MethodCreator constructor = decoratorImplCreator.getMethodCreator(Methods.INIT, "V",
decoratorConstructor.parameterTypes().stream().map(it -> it.name().toString()).toArray());
decoratorConstructorParams.toArray(new Object[0]));
ResultHandle[] constructorArgs = new ResultHandle[decoratorConstructor.parametersCount()];
for (int i = 0; i < decoratorConstructor.parametersCount(); i++) {
constructorArgs[i] = constructor.getMethodParam(i);
Expand All @@ -181,7 +193,8 @@ private String generateDecoratorImplementation(String baseName, String targetPac
constructor.invokeSpecialMethod(decoratorConstructor, constructor.getThis(), constructorArgs);
// Set the delegate field
constructor.writeInstanceField(delegateField.getFieldDescriptor(), constructor.getThis(),
constructor.invokeStaticMethod(MethodDescriptors.DECORATOR_DELEGATE_PROVIDER_GET));
constructor.invokeStaticMethod(MethodDescriptors.DECORATOR_DELEGATE_PROVIDER_GET,
constructor.getMethodParam(decoratorConstructor.parametersCount())));
constructor.returnValue(null);

// Find non-decorated methods from all decorated types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,14 +287,11 @@ public final class MethodDescriptors {
public static final MethodDescriptor CLIENT_PROXIES_GET_DELEGATE = MethodDescriptor.ofMethod(ClientProxies.class,
"getDelegate", Object.class, InjectableBean.class);

public static final MethodDescriptor DECORATOR_DELEGATE_PROVIDER_SET = MethodDescriptor
.ofMethod(DecoratorDelegateProvider.class, "set", Object.class, Object.class);
public static final MethodDescriptor DECORATOR_DELEGATE_PROVIDER_GET = MethodDescriptor.ofMethod(
DecoratorDelegateProvider.class, "getCurrent", Object.class, CreationalContext.class);

public static final MethodDescriptor DECORATOR_DELEGATE_PROVIDER_UNSET = MethodDescriptor
.ofMethod(DecoratorDelegateProvider.class, "unset", void.class);

public static final MethodDescriptor DECORATOR_DELEGATE_PROVIDER_GET = MethodDescriptor
.ofMethod(DecoratorDelegateProvider.class, "get", Object.class);
public static final MethodDescriptor DECORATOR_DELEGATE_PROVIDER_SET = MethodDescriptor.ofMethod(
DecoratorDelegateProvider.class, "setCurrent", Object.class, CreationalContext.class, Object.class);

public static final MethodDescriptor INSTANCES_LIST_OF = MethodDescriptor
.ofMethod(Instances.class, "listOf", List.class, InjectableBean.class, Type.class, Type.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -817,12 +817,14 @@ && isDecorated(decoratedMethodDescriptors, methodDescriptor, resolvedMethodDescr
}
ResultHandle delegateSubclassInstance = subclassConstructor.newInstance(MethodDescriptor.ofConstructor(
delegateSubclass.getClassName(), constructorParameterTypes.toArray(new String[0])), paramHandles);
subclassConstructor.invokeStaticMethod(MethodDescriptors.DECORATOR_DELEGATE_PROVIDER_SET, delegateSubclassInstance);
ResultHandle prev = subclassConstructor.invokeStaticMethod(
MethodDescriptors.DECORATOR_DELEGATE_PROVIDER_SET, creationalContext, delegateSubclassInstance);

ResultHandle decoratorInstance = subclassConstructor.invokeInterfaceMethod(
MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, constructorMethodParam, creationalContext);
// And unset the delegate IP afterwards
subclassConstructor.invokeStaticMethod(MethodDescriptors.DECORATOR_DELEGATE_PROVIDER_UNSET);
subclassConstructor.invokeStaticMethod(
MethodDescriptors.DECORATOR_DELEGATE_PROVIDER_SET, creationalContext, prev);

decoratorToResultHandle.put(decorator.getIdentifier(), decoratorInstance);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,14 +553,14 @@ static <T> InstanceHandle<T> beanInstanceHandle(InjectableBean<T> bean, Creation
}
InjectionPoint prev = null;
if (resetCurrentInjectionPoint) {
prev = InjectionPointProvider.set(CurrentInjectionPointProvider.EMPTY);
prev = InjectionPointProvider.setCurrent(creationalContext, CurrentInjectionPointProvider.EMPTY);
}
try {
return new EagerInstanceHandle<>(bean, bean.get(creationalContext), creationalContext, parentContext,
destroyLogic);
} finally {
if (resetCurrentInjectionPoint) {
InjectionPointProvider.set(prev);
InjectionPointProvider.setCurrent(creationalContext, prev);
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ public Object getReference(Bean<?> bean, Type beanType, CreationalContext<?> ctx
if (bean instanceof InjectableBean && ctx instanceof CreationalContextImpl) {
// there's no actual injection point or an `Instance` object,
// the "current" injection point must be `null`
InjectionPoint prev = InjectionPointProvider.set(null);
InjectionPoint prev = InjectionPointProvider.setCurrent(ctx, null);
try {
return ArcContainerImpl.beanInstanceHandle((InjectableBean) bean, (CreationalContextImpl) ctx,
false, null, true).get();
} finally {
InjectionPointProvider.set(prev);
InjectionPointProvider.setCurrent(ctx, prev);
}
}
throw new IllegalArgumentException(
Expand All @@ -86,12 +86,12 @@ public Object getInjectableReference(InjectionPoint ij, CreationalContext<?> ctx
throw new UnsatisfiedResolutionException();
}
InjectableBean<?> bean = (InjectableBean<?>) resolve(beans);
InjectionPoint prev = InjectionPointProvider.set(ij);
InjectionPoint prev = InjectionPointProvider.setCurrent(ctx, ij);
try {
return ArcContainerImpl.beanInstanceHandle(bean, (CreationalContextImpl) ctx,
false, null, true).get();
} finally {
InjectionPointProvider.set(prev);
InjectionPointProvider.setCurrent(ctx, prev);
}
}
throw new IllegalArgumentException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import jakarta.enterprise.context.spi.Contextual;
import jakarta.enterprise.context.spi.CreationalContext;
import jakarta.enterprise.inject.spi.InjectionPoint;

import io.quarkus.arc.InjectableBean;
import io.quarkus.arc.InjectableReferenceProvider;
Expand All @@ -24,6 +25,9 @@ public class CreationalContextImpl<T> implements CreationalContext<T>, Function<
private final CreationalContextImpl<?> parent;
private List<InstanceHandle<?>> dependentInstances;

private InjectionPoint currentInjectionPoint;
private Object currentDecoratorDelegate;

public CreationalContextImpl(Contextual<T> contextual) {
this(contextual, null);
}
Expand Down Expand Up @@ -129,4 +133,50 @@ public static <I> void addDependencyToParent(InjectableBean<I> bean, I instance,
}
}

static <T> InjectionPoint getCurrentInjectionPoint(CreationalContext<T> ctx) {
CreationalContextImpl<?> instance = unwrap(ctx);
while (instance != null) {
synchronized (instance) {
InjectionPoint result = instance.currentInjectionPoint;
if (result != null) {
return result;
}
}
instance = instance.parent;
}
return null;
}

static <T> InjectionPoint setCurrentInjectionPoint(CreationalContext<T> ctx, InjectionPoint injectionPoint) {
CreationalContextImpl<T> instance = unwrap(ctx);
synchronized (instance) {
InjectionPoint previous = instance.currentInjectionPoint;
instance.currentInjectionPoint = injectionPoint;
return previous;
}
}

static <T> Object getCurrentDecoratorDelegate(CreationalContext<T> ctx) {
CreationalContextImpl<?> instance = unwrap(ctx);
while (instance != null) {
synchronized (instance) {
Object result = instance.currentDecoratorDelegate;
if (result != null) {
return result;
}
}
instance = instance.parent;
}
return null;
}

static <T> Object setCurrentDecoratorDelegate(CreationalContext<T> ctx, Object decoratorDelegate) {
CreationalContextImpl<T> instance = unwrap(ctx);
synchronized (instance) {
Object previous = instance.currentDecoratorDelegate;
instance.currentDecoratorDelegate = decoratorDelegate;
return previous;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public CurrentInjectionPointProvider(InjectableBean<?> bean, Supplier<Injectable

@Override
public T get(CreationalContext<T> creationalContext) {
InjectionPoint prev = InjectionPointProvider.set(injectionPoint);
InjectionPoint prev = InjectionPointProvider.setCurrent(creationalContext, injectionPoint);
try {
return delegateSupplier.get().get(creationalContext);
} finally {
InjectionPointProvider.set(prev);
InjectionPointProvider.setCurrent(creationalContext, prev);
}
}

Expand Down

0 comments on commit 1e7403a

Please sign in to comment.