From 09d12079fa99980ee761da4a3ef3db4d21016bdc Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 6 Jun 2022 08:15:07 -0500 Subject: [PATCH] Convert `synchronized` to use semaphores (#4284) * Convert to use locks instead of `synchonized` Signed-off-by: tim.quinn@oracle.com --- .../grpc/server/ProtoReflectionService.java | 20 ++++- .../helidon/grpc/server/GrpcServiceTest.java | 20 ++++- .../metrics/api/RegistryFactoryManager.java | 36 ++++++-- .../io/helidon/metrics/PeriodicExecutor.java | 18 ++-- .../io/helidon/metrics/RegistryFactory.java | 14 ++-- .../cdi/BuildTimeInitializer.java | 43 +++++++--- .../cdi/ContainerInstanceHolder.java | 60 ++++++++----- .../faulttolerance/BulkheadBean.java | 57 +++++++++---- .../microprofile/grpc/core/Instance.java | 31 +++++-- .../io/helidon/openapi/OpenAPISupport.java | 84 +++++++++++-------- 10 files changed, 262 insertions(+), 121 deletions(-) diff --git a/grpc/server/src/main/java/io/helidon/grpc/server/ProtoReflectionService.java b/grpc/server/src/main/java/io/helidon/grpc/server/ProtoReflectionService.java index 30665896032..0ea82aa378c 100644 --- a/grpc/server/src/main/java/io/helidon/grpc/server/ProtoReflectionService.java +++ b/grpc/server/src/main/java/io/helidon/grpc/server/ProtoReflectionService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,9 @@ import java.util.Queue; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; import com.google.protobuf.Any; import com.google.protobuf.DescriptorProtos; @@ -88,7 +91,7 @@ */ public final class ProtoReflectionService extends ServerReflectionGrpc.ServerReflectionImplBase { - private final Object lock = new Object(); + private final Lock indexAccess = new ReentrantLock(true); private final Map serverReflectionIndexes = new WeakHashMap<>(); @@ -110,7 +113,7 @@ public static BindableService newInstance() { * mutable services or a change in the service names. */ private ServerReflectionIndex getRefreshedIndex() { - synchronized (lock) { + return accessIndex(() -> { Server server = InternalServer.SERVER_CONTEXT_KEY.get(); ServerReflectionIndex index = serverReflectionIndexes.get(server); if (index == null) { @@ -147,7 +150,7 @@ private ServerReflectionIndex getRefreshedIndex() { } return index; - } + }); } @Override @@ -594,4 +597,13 @@ private void processExtension(FieldDescriptor extension, FileDescriptor fd) { fileDescriptorsByExtensionAndNumber.get(extensionName).put(extensionNumber, fd); } } + + private T accessIndex(Supplier operation) { + indexAccess.lock(); + try { + return operation.get(); + } finally { + indexAccess.unlock(); + } + } } diff --git a/grpc/server/src/test/java/io/helidon/grpc/server/GrpcServiceTest.java b/grpc/server/src/test/java/io/helidon/grpc/server/GrpcServiceTest.java index 57f05002717..f0eeb8083a9 100644 --- a/grpc/server/src/test/java/io/helidon/grpc/server/GrpcServiceTest.java +++ b/grpc/server/src/test/java/io/helidon/grpc/server/GrpcServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021 Oracle and/or its affiliates. + * Copyright (c) 2019, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Stream; @@ -511,9 +513,21 @@ public void update(ServiceDescriptor.Rules rules) { */ private static class SynchronizedObserver extends TestStreamObserver { + + private final Lock nextAccess = new ReentrantLock(true); + @Override - public synchronized void onNext(T t) { - super.onNext(t); + public void onNext(T t) { + accessNext(() -> super.onNext(t)); + } + + private void accessNext(Runnable operation) { + nextAccess.lock(); + try { + operation.run(); + } finally { + nextAccess.unlock(); + } } } } diff --git a/metrics/api/src/main/java/io/helidon/metrics/api/RegistryFactoryManager.java b/metrics/api/src/main/java/io/helidon/metrics/api/RegistryFactoryManager.java index 5880ba5c4bc..8c52fd7ff68 100644 --- a/metrics/api/src/main/java/io/helidon/metrics/api/RegistryFactoryManager.java +++ b/metrics/api/src/main/java/io/helidon/metrics/api/RegistryFactoryManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,9 @@ package io.helidon.metrics.api; import java.util.ServiceLoader; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -83,6 +86,8 @@ private static RegistryFactoryProvider loadRegistryFactoryProvider() { return provider; } + private static final Lock SETTINGS_ACCESS = new ReentrantLock(true); + private RegistryFactoryManager() { } @@ -105,21 +110,34 @@ static RegistryFactory getInstance() { return INSTANCE.get(); } - static synchronized RegistryFactory getInstance(MetricsSettings metricsSettings) { - RegistryFactoryManager.metricsSettings = metricsSettings; - RegistryFactory result = INSTANCE.get(); - result.update(metricsSettings); - return result; + static RegistryFactory getInstance(MetricsSettings metricsSettings) { + + return accessMetricsSettings(() -> { + RegistryFactoryManager.metricsSettings = metricsSettings; + RegistryFactory result = INSTANCE.get(); + result.update(metricsSettings); + return result; + }); } - static synchronized RegistryFactory getInstance(ComponentMetricsSettings componentMetricsSettings) { - return componentMetricsSettings.isEnabled() + static RegistryFactory getInstance(ComponentMetricsSettings componentMetricsSettings) { + + return accessMetricsSettings(() -> componentMetricsSettings.isEnabled() ? INSTANCE.get() - : NO_OP_INSTANCE; + : NO_OP_INSTANCE); } @Deprecated static RegistryFactory getInstance(Config config) { return getInstance(MetricsSettings.create(config)); } + + private static T accessMetricsSettings(Supplier operation) { + SETTINGS_ACCESS.lock(); + try { + return operation.get(); + } finally { + SETTINGS_ACCESS.unlock(); + } + } } diff --git a/metrics/metrics/src/main/java/io/helidon/metrics/PeriodicExecutor.java b/metrics/metrics/src/main/java/io/helidon/metrics/PeriodicExecutor.java index 3188d4380d0..52a542fce2f 100644 --- a/metrics/metrics/src/main/java/io/helidon/metrics/PeriodicExecutor.java +++ b/metrics/metrics/src/main/java/io/helidon/metrics/PeriodicExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,9 @@ import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; @@ -32,10 +33,6 @@ * Some enrollments might arrive before the manager is started. We save those and act on them once the * manager starts. This makes sure the executor's thread starts only at native image runtime. *

- *

- * In production use, starting and stopping the executor and even enrolling callbacks are not performance-critical operations, - * so simple synchronization on methods which access shared data is clear and sufficient. - *

*/ class PeriodicExecutor { @@ -81,7 +78,7 @@ private static class Enrollment { private final Collection deferredEnrollments = new ArrayList<>(); - private final Semaphore access = new Semaphore(1, true); + private final Lock access = new ReentrantLock(true); private PeriodicExecutor() { } @@ -173,12 +170,11 @@ State executorState() { } private void sync(String taskDescription, Runnable task) { + access.lock(); try { - access.acquire(); task.run(); - access.release(); - } catch (InterruptedException ex) { - LOGGER.log(Level.WARNING, "Attempt to " + taskDescription + " failed", ex); + } finally { + access.unlock(); } } } diff --git a/metrics/metrics/src/main/java/io/helidon/metrics/RegistryFactory.java b/metrics/metrics/src/main/java/io/helidon/metrics/RegistryFactory.java index ed55b9de67d..63c5129baf4 100644 --- a/metrics/metrics/src/main/java/io/helidon/metrics/RegistryFactory.java +++ b/metrics/metrics/src/main/java/io/helidon/metrics/RegistryFactory.java @@ -17,7 +17,8 @@ package io.helidon.metrics; import java.util.EnumMap; -import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import io.helidon.config.Config; import io.helidon.metrics.api.MetricsSettings; @@ -45,7 +46,7 @@ public class RegistryFactory implements io.helidon.metrics.api.RegistryFactory { private final EnumMap registries = new EnumMap<>(Type.class); - private final Semaphore metricsSettingsAccess = new Semaphore(1); + private final Lock metricsSettingsAccess = new ReentrantLock(true); private MetricsSettings metricsSettings; /** @@ -68,12 +69,11 @@ private RegistryFactory(MetricsSettings metricsSettings) { } private void accessMetricsSettings(Runnable operation) { + metricsSettingsAccess.lock(); try { - metricsSettingsAccess.acquire(); operation.run(); - metricsSettingsAccess.release(); - } catch (InterruptedException e) { - throw new RuntimeException(e); + } finally { + metricsSettingsAccess.unlock(); } } @@ -164,7 +164,7 @@ public void update(MetricsSettings metricsSettings) { }); } - private synchronized void ensureBase() { + private void ensureBase() { if (null == registries.get(Type.BASE)) { accessMetricsSettings(() -> { Registry registry = BaseRegistry.create(metricsSettings); diff --git a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java index 02e1352d419..e4981ec7148 100644 --- a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java +++ b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. + * Copyright (c) 2019, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,10 @@ */ package io.helidon.microprofile.cdi; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; + import io.helidon.common.LogConfig; /** @@ -25,6 +29,8 @@ final class BuildTimeInitializer { private static volatile HelidonContainerImpl container; + private static final Lock CONTAINER_ACCESS = new ReentrantLock(true); + static { // need to initialize logging as soon as possible LogConfig.initClass(); @@ -34,21 +40,38 @@ final class BuildTimeInitializer { private BuildTimeInitializer() { } - static synchronized HelidonContainerImpl get() { - if (null == container) { - createContainer(); - } + static HelidonContainerImpl get() { + return accessContainer(() -> { + if (null == container) { + createContainer(); + } - return container; + return container; + }); } - static synchronized void reset() { - container = null; + static void reset() { + accessContainer(() -> { + container = null; + return null; + }); } private static void createContainer() { // static initialization to support GraalVM native image - container = HelidonContainerImpl.create(); - ContainerInstanceHolder.addListener(BuildTimeInitializer::reset); + accessContainer(() -> { + container = HelidonContainerImpl.create(); + ContainerInstanceHolder.addListener(BuildTimeInitializer::reset); + return null; + }); + } + + private static T accessContainer(Supplier operation) { + CONTAINER_ACCESS.lock(); + try { + return operation.get(); + } finally { + CONTAINER_ACCESS.unlock(); + } } } diff --git a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ContainerInstanceHolder.java b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ContainerInstanceHolder.java index 890774a86fe..cac7ed9ed48 100644 --- a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ContainerInstanceHolder.java +++ b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ContainerInstanceHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. + * Copyright (c) 2019, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,9 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; /** * This class contains the container used by this Helidon runtime. @@ -32,6 +35,8 @@ final class ContainerInstanceHolder { private static final List RESET_LISTENERS = new LinkedList<>(); private static boolean isReset = false; + private static final Lock ACCESS_GUARD = new ReentrantLock(); + private ContainerInstanceHolder() { } @@ -40,17 +45,19 @@ static void set(HelidonContainer container) { } // return true if the container was reset, indicating somebody started CDI by hand and then shut it down - static synchronized boolean isReset() { - return isReset; + static boolean isReset() { + return access(() -> isReset); } - static synchronized HelidonContainer get() { - HelidonContainer helidonContainer = CONTAINER.get(); - if (null == helidonContainer) { - helidonContainer = fromBuildTimeInitializer(); - CONTAINER.compareAndSet(null, helidonContainer); - } - return helidonContainer; + static HelidonContainer get() { + return access(() -> { + HelidonContainer helidonContainer = CONTAINER.get(); + if (null == helidonContainer) { + helidonContainer = fromBuildTimeInitializer(); + CONTAINER.compareAndSet(null, helidonContainer); + } + return helidonContainer; + }); } private static HelidonContainer fromBuildTimeInitializer() { @@ -58,17 +65,32 @@ private static HelidonContainer fromBuildTimeInitializer() { return BuildTimeInitializer.get(); } - static synchronized void addListener(Runnable runnable) { - RESET_LISTENERS.add(runnable); + static void addListener(Runnable runnable) { + access(() -> { + RESET_LISTENERS.add(runnable); + return null; + }); + } + + static void reset() { + access(() -> { + isReset = true; + CONTAINER.set(null); + for (Runnable resetListener : RESET_LISTENERS) { + resetListener.run(); + } + HelidonCdiProvider.unset(); + RESET_LISTENERS.clear(); + return null; + }); } - static synchronized void reset() { - isReset = true; - CONTAINER.set(null); - for (Runnable resetListener : RESET_LISTENERS) { - resetListener.run(); + private static T access(Supplier operation) { + try { + ACCESS_GUARD.lock(); + return operation.get(); + } finally { + ACCESS_GUARD.unlock(); } - HelidonCdiProvider.unset(); - RESET_LISTENERS.clear(); } } diff --git a/microprofile/fault-tolerance/src/test/java/io/helidon/microprofile/faulttolerance/BulkheadBean.java b/microprofile/fault-tolerance/src/test/java/io/helidon/microprofile/faulttolerance/BulkheadBean.java index 354f2c1c1c7..d77c8035cad 100644 --- a/microprofile/fault-tolerance/src/test/java/io/helidon/microprofile/faulttolerance/BulkheadBean.java +++ b/microprofile/fault-tolerance/src/test/java/io/helidon/microprofile/faulttolerance/BulkheadBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * Copyright (c) 2018, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,9 @@ package io.helidon.microprofile.faulttolerance; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; import org.eclipse.microprofile.faulttolerance.Asynchronous; import org.eclipse.microprofile.faulttolerance.Bulkhead; @@ -37,30 +40,50 @@ static class ConcurrencyCounter { private int concurrentCalls; private int totalCalls; - synchronized void increment() { - currentCalls++; - if (currentCalls > concurrentCalls) { - concurrentCalls = currentCalls; - } - totalCalls++; + private final Lock accessGuard = new ReentrantLock(true); + + void increment() { + access(() -> { + currentCalls++; + if (currentCalls > concurrentCalls) { + concurrentCalls = currentCalls; + } + totalCalls++; + return null; + }); + } + + void decrement() { + access(() -> { + currentCalls--; + return null; + }); } - synchronized void decrement() { - currentCalls--; + int concurrentCalls() { + return access(() -> concurrentCalls); } - synchronized int concurrentCalls() { - return concurrentCalls; + int totalCalls() { + return access(() -> totalCalls); } - synchronized int totalCalls() { - return totalCalls; + void reset() { + access(() -> { + currentCalls = 0; + concurrentCalls = 0; + totalCalls = 0; + return null; + }); } - synchronized void reset() { - currentCalls = 0; - concurrentCalls = 0; - totalCalls = 0; + private T access(Supplier operation) { + accessGuard.lock(); + try { + return operation.get(); + } finally { + accessGuard.unlock(); + } } } diff --git a/microprofile/grpc/core/src/main/java/io/helidon/microprofile/grpc/core/Instance.java b/microprofile/grpc/core/src/main/java/io/helidon/microprofile/grpc/core/Instance.java index 2e7deb8e631..fdea246d603 100644 --- a/microprofile/grpc/core/src/main/java/io/helidon/microprofile/grpc/core/Instance.java +++ b/microprofile/grpc/core/src/main/java/io/helidon/microprofile/grpc/core/Instance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021 Oracle and/or its affiliates. + * Copyright (c) 2019, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package io.helidon.microprofile.grpc.core; import java.util.Objects; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import io.grpc.Status; @@ -115,6 +117,8 @@ class SingletonInstance private T instance; + private final Lock instanceAccess = new ReentrantLock(true); + private SingletonInstance(Class instanceClass) { this.instanceClass = instanceClass; } @@ -129,15 +133,26 @@ public T get() { return ensureInstance(); } - private synchronized T ensureInstance() { - if (instance == null) { - try { - instance = instanceClass.newInstance(); - } catch (Throwable e) { - throw Status.INTERNAL.withCause(e).asRuntimeException(); + private T ensureInstance() { + return accessInstance(() -> { + if (instance == null) { + try { + instance = instanceClass.newInstance(); + } catch (Throwable e) { + throw Status.INTERNAL.withCause(e).asRuntimeException(); + } } + return instance; + }); + } + + private T accessInstance(Supplier operation) { + instanceAccess.lock(); + try { + return operation.get(); + } finally { + instanceAccess.unlock(); } - return instance; } } } diff --git a/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java b/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java index 381192bf2a4..08ed62f8011 100644 --- a/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java +++ b/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.function.Supplier; import java.util.logging.Level; @@ -140,6 +142,8 @@ MediaType mediaType() { */ private static SnakeYAMLParserHelper helper = null; + private static final Lock HELPER_ACCESS = new ReentrantLock(true); + private final String webContext; private OpenAPI model = null; @@ -156,6 +160,8 @@ MediaType mediaType() { private final OpenApiStaticFile openApiStaticFile; private final Supplier> indexViewsSupplier; + private final Lock modelAccess = new ReentrantLock(true); + /** * Creates a new instance of {@code OpenAPISupport}. * @@ -196,11 +202,13 @@ protected void prepareModel() { model(); } - private synchronized OpenAPI model() { - if (model == null) { - model = prepareModel(openApiConfig, openApiStaticFile, indexViewsSupplier.get()); - } - return model; + private OpenAPI model() { + return access(modelAccess, () -> { + if (model == null) { + model = prepareModel(openApiConfig, openApiStaticFile, indexViewsSupplier.get()); + } + return model; + }); } private void registerJsonpSupport(ServerRequest req, ServerResponse res) { @@ -210,12 +218,14 @@ private void registerJsonpSupport(ServerRequest req, ServerResponse res) { req.next(); } - static synchronized SnakeYAMLParserHelper helper() { - if (helper == null) { - helper = SnakeYAMLParserHelper.create(ExpandedTypeDescription::create); - adjustTypeDescriptions(helper.types()); - } - return helper; + static SnakeYAMLParserHelper helper() { + return access(HELPER_ACCESS, () -> { + if (helper == null) { + helper = SnakeYAMLParserHelper.create(ExpandedTypeDescription::create); + adjustTypeDescriptions(helper.types()); + } + return helper; + }); } static Map, ExpandedTypeDescription> buildImplsToTypes(SnakeYAMLParserHelper helper) { @@ -306,28 +316,27 @@ private static String methodName(String operation, PathItem.HttpMethod method) { private OpenAPI prepareModel(OpenApiConfig config, OpenApiStaticFile staticFile, List filteredIndexViews) { try { - synchronized (OpenApiDocument.INSTANCE) { - OpenApiDocument.INSTANCE.reset(); - OpenApiDocument.INSTANCE.config(config); - OpenApiDocument.INSTANCE.modelFromReader(OpenApiProcessor.modelFromReader(config, getContextClassLoader())); - if (staticFile != null) { - OpenApiDocument.INSTANCE.modelFromStaticFile(OpenAPIParser.parse(helper().types(), staticFile.getContent(), - OpenAPIMediaType.byFormat(staticFile.getFormat()))); - } - if (isAnnotationProcessingEnabled(config)) { - expandModelUsingAnnotations(config, filteredIndexViews); - } else { - LOGGER.log(Level.FINE, "OpenAPI Annotation processing is disabled"); - } - OpenApiDocument.INSTANCE.filter(OpenApiProcessor.getFilter(config, getContextClassLoader())); - OpenApiDocument.INSTANCE.initialize(); - OpenAPIImpl instance = OpenAPIImpl.class.cast(OpenApiDocument.INSTANCE.get()); - - // Create a copy, primarily to avoid problems during unit testing. - // The SmallRye MergeUtil omits the openapi value, so we need to set it explicitly. - return MergeUtil.merge(new OpenAPIImpl(), instance) - .openapi(instance.getOpenapi()); + // The write lock guarding the model has already been acquired. + OpenApiDocument.INSTANCE.reset(); + OpenApiDocument.INSTANCE.config(config); + OpenApiDocument.INSTANCE.modelFromReader(OpenApiProcessor.modelFromReader(config, getContextClassLoader())); + if (staticFile != null) { + OpenApiDocument.INSTANCE.modelFromStaticFile(OpenAPIParser.parse(helper().types(), staticFile.getContent(), + OpenAPIMediaType.byFormat(staticFile.getFormat()))); + } + if (isAnnotationProcessingEnabled(config)) { + expandModelUsingAnnotations(config, filteredIndexViews); + } else { + LOGGER.log(Level.FINE, "OpenAPI Annotation processing is disabled"); } + OpenApiDocument.INSTANCE.filter(OpenApiProcessor.getFilter(config, getContextClassLoader())); + OpenApiDocument.INSTANCE.initialize(); + OpenAPIImpl instance = OpenAPIImpl.class.cast(OpenApiDocument.INSTANCE.get()); + + // Create a copy, primarily to avoid problems during unit testing. + // The SmallRye MergeUtil omits the openapi value, so we need to set it explicitly. + return MergeUtil.merge(new OpenAPIImpl(), instance) + .openapi(instance.getOpenapi()); } catch (IOException ex) { throw new RuntimeException("Error initializing OpenAPI information", ex); } @@ -915,4 +924,13 @@ private OpenApiStaticFile getDefaultStaticFile() { return null; } } + + private static T access(Lock guard, Supplier operation) { + guard.lock(); + try { + return operation.get(); + } finally { + guard.unlock(); + } + } }