From efe63978e471edd3214db3f065353ff7dca45522 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Fri, 5 Aug 2022 16:44:03 +0200 Subject: [PATCH] Reuse the build executor thread pool for parallel execution - for class transformation and ArC resource generation - previously separate executor services were created and shut down --- .../java/io/quarkus/builder/BuildContext.java | 4 +- .../quarkus/deployment/ExtensionLoader.java | 4 +- .../steps/ClassTransformingBuildStep.java | 149 +++++++++--------- .../quarkus/arc/deployment/ArcProcessor.java | 60 +++---- 4 files changed, 103 insertions(+), 114 deletions(-) diff --git a/core/builder/src/main/java/io/quarkus/builder/BuildContext.java b/core/builder/src/main/java/io/quarkus/builder/BuildContext.java index 1e3b289b267a3..d2bddb2f5010b 100644 --- a/core/builder/src/main/java/io/quarkus/builder/BuildContext.java +++ b/core/builder/src/main/java/io/quarkus/builder/BuildContext.java @@ -8,7 +8,7 @@ import java.util.Comparator; import java.util.List; import java.util.Set; -import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -224,7 +224,7 @@ public void error(Location location, String format, Object... args) { * * @return an executor which can be used for asynchronous tasks */ - public Executor getExecutor() { + public ExecutorService getExecutor() { return execution.getExecutor(); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java index 720bee9cfe2e1..fd6105199dc7d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java @@ -34,6 +34,7 @@ import java.util.Optional; import java.util.Properties; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BooleanSupplier; @@ -620,7 +621,8 @@ private static Consumer loadStepsFromClass(Class clazz, .andThen(bsb -> bsb.consumes(buildItemClass, ConsumeFlags.of(ConsumeFlag.OPTIONAL))); methodParamFns.add((bc, bri) -> (Supplier>) () -> Optional .ofNullable(bc.consume(buildItemClass))); - } else if (rawTypeOf(parameterType) == Executor.class) { + } else if (rawTypeOf(parameterType) == Executor.class + || rawTypeOf(parameterType) == ExecutorService.class) { methodParamFns.add((bc, bri) -> bc.getExecutor()); } else if (parameterClass.isAnnotationPresent(ConfigRoot.class)) { final ConfigRoot annotation = parameterClass.getAnnotation(ConfigRoot.class); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java index 95517d41d08a8..654173f3ba080 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java @@ -20,7 +20,6 @@ import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -76,7 +75,8 @@ TransformedClassesBuildItem handleClassTransformation(List removedResourceBuildItems, - ArchiveRootBuildItem archiveRoot, LaunchModeBuildItem launchMode, PackageConfig packageConfig) + ArchiveRootBuildItem archiveRoot, LaunchModeBuildItem launchMode, PackageConfig packageConfig, + ExecutorService buildExecutor) throws ExecutionException, InterruptedException { if (bytecodeTransformerBuildItems.isEmpty() && classLoadingConfig.removedResources.isEmpty() && removedResourceBuildItems.isEmpty()) { @@ -113,7 +113,6 @@ TransformedClassesBuildItem handleClassTransformation(List> transformed = new ConcurrentLinkedDeque<>(); final Map> transformedClassesByJar = new HashMap<>(); ClassLoader transformCl = Thread.currentThread().getContextClassLoader(); @@ -173,90 +172,86 @@ public byte[] apply(String className, byte[] originalBytes) { } } }; - try { - for (Map.Entry> entry : bytecodeTransformers - .entrySet()) { - String className = entry.getKey(); - boolean cacheable = !nonCacheable.contains(className); - if (cacheable && transformedClassesCache.containsKey(className)) { - if (liveReloadBuildItem.getChangeInformation() != null) { - if (!liveReloadBuildItem.getChangeInformation().getChangedClasses().contains(className)) { - //we can use the cached transformation - handleTransformedClass(transformedToArchive, transformedClassesByJar, - transformedClassesCache.get(className)); - continue; - } - } - } - String classFileName = className.replace('.', '/') + ".class"; - List archives = cl.getElementsWithResource(classFileName); - if (!archives.isEmpty()) { - ClassPathElement classPathElement = archives.get(0); - Path jar = classPathElement.getRoot(); - if (jar == null) { - log.warnf("Cannot transform %s as its containing application archive could not be found.", - entry.getKey()); + for (Map.Entry> entry : bytecodeTransformers + .entrySet()) { + String className = entry.getKey(); + boolean cacheable = !nonCacheable.contains(className); + if (cacheable && transformedClassesCache.containsKey(className)) { + if (liveReloadBuildItem.getChangeInformation() != null) { + if (!liveReloadBuildItem.getChangeInformation().getChangedClasses().contains(className)) { + //we can use the cached transformation + handleTransformedClass(transformedToArchive, transformedClassesByJar, + transformedClassesCache.get(className)); continue; } + } + } + String classFileName = className.replace('.', '/') + ".class"; + List archives = cl.getElementsWithResource(classFileName); + if (!archives.isEmpty()) { + ClassPathElement classPathElement = archives.get(0); + Path jar = classPathElement.getRoot(); + if (jar == null) { + log.warnf("Cannot transform %s as its containing application archive could not be found.", + entry.getKey()); + continue; + } - boolean continueOnFailure = entry.getValue().stream() - .filter(a -> !a.isContinueOnFailure()) - .findAny().isEmpty(); - List> visitors = entry.getValue().stream() - .map(BytecodeTransformerBuildItem::getVisitorFunction).filter(Objects::nonNull) - .collect(Collectors.toList()); - List> preVisitFunctions = entry.getValue().stream() - .map(BytecodeTransformerBuildItem::getInputTransformer).filter(Objects::nonNull) - .collect(Collectors.toList()); - transformedToArchive.put(classFileName, jar); - transformed.add(executorPool.submit(new Callable() { - @Override - public TransformedClassesBuildItem.TransformedClass call() throws Exception { - ClassLoader old = Thread.currentThread().getContextClassLoader(); - try { - byte[] classData = classPathElement.getResource(classFileName).getData(); - Thread.currentThread().setContextClassLoader(transformCl); - Set constValues = constScanning.get(className); - if (constValues != null && !noConstScanning.contains(className)) { - if (!ConstPoolScanner.constPoolEntryPresent(classData, constValues)) { - return null; - } - } - byte[] data = transformClass(className, visitors, classData, preVisitFunctions, - classReaderOptions.getOrDefault(className, 0)); - TransformedClassesBuildItem.TransformedClass transformedClass = new TransformedClassesBuildItem.TransformedClass( - className, data, - classFileName, eager.contains(className)); - if (cacheable && launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT - && classData != null) { - transformedClassesCache.put(className, transformedClass); - } - return transformedClass; - } catch (Throwable e) { - if (continueOnFailure) { - if (log.isDebugEnabled()) { - log.errorf(e, "Failed to transform %s", className); - } else { - log.errorf("Failed to transform %s", className); - } + boolean continueOnFailure = entry.getValue().stream() + .filter(a -> !a.isContinueOnFailure()) + .findAny().isEmpty(); + List> visitors = entry.getValue().stream() + .map(BytecodeTransformerBuildItem::getVisitorFunction).filter(Objects::nonNull) + .collect(Collectors.toList()); + List> preVisitFunctions = entry.getValue().stream() + .map(BytecodeTransformerBuildItem::getInputTransformer).filter(Objects::nonNull) + .collect(Collectors.toList()); + transformedToArchive.put(classFileName, jar); + transformed.add(buildExecutor.submit(new Callable() { + @Override + public TransformedClassesBuildItem.TransformedClass call() throws Exception { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + try { + byte[] classData = classPathElement.getResource(classFileName).getData(); + Thread.currentThread().setContextClassLoader(transformCl); + Set constValues = constScanning.get(className); + if (constValues != null && !noConstScanning.contains(className)) { + if (!ConstPoolScanner.constPoolEntryPresent(classData, constValues)) { return null; + } + } + byte[] data = transformClass(className, visitors, classData, preVisitFunctions, + classReaderOptions.getOrDefault(className, 0)); + TransformedClassesBuildItem.TransformedClass transformedClass = new TransformedClassesBuildItem.TransformedClass( + className, data, + classFileName, eager.contains(className)); + if (cacheable && launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT + && classData != null) { + transformedClassesCache.put(className, transformedClass); + } + return transformedClass; + } catch (Throwable e) { + if (continueOnFailure) { + if (log.isDebugEnabled()) { + log.errorf(e, "Failed to transform %s", className); } else { - throw e; + log.errorf("Failed to transform %s", className); } - } finally { - Thread.currentThread().setContextClassLoader(old); + return null; + } else { + throw e; } + } finally { + Thread.currentThread().setContextClassLoader(old); } - })); - } else { - log.warnf("Cannot transform %s as its containing application archive could not be found.", - entry.getKey()); - } + } + })); + } else { + log.warnf("Cannot transform %s as its containing application archive could not be found.", + entry.getKey()); } - - } finally { - executorPool.shutdown(); } + handleRemovedResources(classLoadingConfig, curateOutcomeBuildItem, transformedClassesByJar, removedResourceBuildItems); if (!transformed.isEmpty()) { for (Future i : transformed) { diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java index 7ca590d890d39..ee1a2ef822daa 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java @@ -14,7 +14,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -516,7 +515,8 @@ public BeanContainerBuildItem generateResources(ArcConfig config, ArcRecorder re BuildProducer generatedResource, BuildProducer bytecodeTransformer, List reflectiveBeanClasses, - Optional currentContextFactory) throws Exception { + Optional currentContextFactory, + ExecutorService buildExecutor) throws Exception { for (ValidationErrorBuildItem validationError : validationErrors) { for (Throwable error : validationError.getValues()) { @@ -539,45 +539,37 @@ public BeanContainerBuildItem generateResources(ArcConfig config, ArcRecorder re boolean parallelResourceGeneration = Boolean .parseBoolean(System.getProperty("quarkus.arc.parallel-resource-generation", "true")); long start = System.nanoTime(); - ExecutorService executor = parallelResourceGeneration - ? Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) - : null; + ExecutorService executor = parallelResourceGeneration ? buildExecutor : null; List resources; - try { - resources = beanProcessor.generateResources(new ReflectionRegistration() { - @Override - public void registerMethod(MethodInfo methodInfo) { - reflectiveMethods.produce(new ReflectiveMethodBuildItem(methodInfo)); - } + resources = beanProcessor.generateResources(new ReflectionRegistration() { + @Override + public void registerMethod(MethodInfo methodInfo) { + reflectiveMethods.produce(new ReflectiveMethodBuildItem(methodInfo)); + } - @Override - public void registerField(FieldInfo fieldInfo) { - reflectiveFields.produce(new ReflectiveFieldBuildItem(fieldInfo)); - } + @Override + public void registerField(FieldInfo fieldInfo) { + reflectiveFields.produce(new ReflectiveFieldBuildItem(fieldInfo)); + } - @Override - public void registerClientProxy(DotName beanClassName, String clientProxyName) { - if (reflectiveBeanClassesNames.contains(beanClassName)) { - // Fields should never be registered for client proxies - reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, clientProxyName)); - } + @Override + public void registerClientProxy(DotName beanClassName, String clientProxyName) { + if (reflectiveBeanClassesNames.contains(beanClassName)) { + // Fields should never be registered for client proxies + reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, clientProxyName)); } + } - @Override - public void registerSubclass(DotName beanClassName, String subclassName) { - if (reflectiveBeanClassesNames.contains(beanClassName)) { - // Fields should never be registered for subclasses - reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, subclassName)); - } + @Override + public void registerSubclass(DotName beanClassName, String subclassName) { + if (reflectiveBeanClassesNames.contains(beanClassName)) { + // Fields should never be registered for subclasses + reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, subclassName)); } - - }, existingClasses.existingClasses, bytecodeTransformerConsumer, - config.shouldEnableBeanRemoval() && config.detectUnusedFalsePositives, executor); - } finally { - if (executor != null) { - executor.shutdown(); } - } + + }, existingClasses.existingClasses, bytecodeTransformerConsumer, + config.shouldEnableBeanRemoval() && config.detectUnusedFalsePositives, executor); for (ResourceOutput.Resource resource : resources) { switch (resource.getType()) {