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 extends IndexView> 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();
+ }
+ }
}