Skip to content

Commit

Permalink
Remove the use of synchronized in the getBinder method of DefaultBind…
Browse files Browse the repository at this point in the history
…erFactory class for virtual-threads

The getBinder method in DefaultBinderFactory was made thread-safe using ReentrantLock. This commit ensures that the method is friendly for virtual threads to avoid blocking and pinning. The lock is acquired at the beginning of the method and released in a finally block to ensure it is always released, even if an exception occurs. Fixes spring-cloudgh-3004
  • Loading branch information
Fernando Blanch committed Sep 24, 2024
1 parent 5d881b2 commit 2f82f3c
Showing 1 changed file with 32 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;

import org.apache.commons.logging.Log;
Expand Down Expand Up @@ -98,6 +99,8 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl

private volatile String defaultBinder;

private static final ReentrantLock lock = new ReentrantLock();

public DefaultBinderFactory(Map<String, BinderConfiguration> binderConfigurations,
BinderTypeRegistry binderTypeRegistry, BinderCustomizer binderCustomizer) {
this.binderConfigurations = new HashMap<>(binderConfigurations);
Expand Down Expand Up @@ -142,34 +145,39 @@ public void destroy() {

@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public <T> Binder<T, ?, ?> getBinder(String name, Class<? extends T> bindingTargetType) {
lock.lock();
try {
String binderName = StringUtils.hasText(name) ? name : this.defaultBinder;

public synchronized <T> Binder<T, ?, ?> getBinder(String name, Class<? extends T> bindingTargetType) {
String binderName = StringUtils.hasText(name) ? name : this.defaultBinder;

Map<String, Binder> binders = this.context == null ? Collections.emptyMap() : this.context.getBeansOfType(Binder.class);
Binder<T, ConsumerProperties, ProducerProperties> binder;
if (StringUtils.hasText(binderName) && binders.containsKey(binderName)) {
binder = (Binder<T, ConsumerProperties, ProducerProperties>) this.context.getBean(binderName);
}
else if (binders.size() == 1) {
binder = binders.values().iterator().next();
}
else if (binders.size() > 1) {
throw new IllegalStateException(
Map<String, Binder> binders = this.context == null ? Collections.emptyMap() : this.context.getBeansOfType(Binder.class);
Binder<T, ConsumerProperties, ProducerProperties> binder;
if (StringUtils.hasText(binderName) && binders.containsKey(binderName)) {
binder = (Binder<T, ConsumerProperties, ProducerProperties>) this.context.getBean(binderName);
}
else if (binders.size() == 1) {
binder = binders.values().iterator().next();
}
else if (binders.size() > 1) {
throw new IllegalStateException(
"Multiple binders are available, however neither default nor "
+ "per-destination binder name is provided. Available binders are "
+ binders.keySet());
}
else {
/*
* This is the fallback to the old bootstrap that relies on spring.binders.
*/
binder = this.doGetBinder(binderName, bindingTargetType);
+ "per-destination binder name is provided. Available binders are "
+ binders.keySet());
}
else {
/*
* This is the fallback to the old bootstrap that relies on spring.binders.
*/
binder = this.doGetBinder(binderName, bindingTargetType);
}
if (this.binderCustomizer != null) {
this.binderCustomizer.customize(binder, binderName);
}
return binder;
}
if (this.binderCustomizer != null) {
this.binderCustomizer.customize(binder, binderName);
finally {
lock.unlock();
}
return binder;
}

private <T> Binder<T, ConsumerProperties, ProducerProperties> doGetBinder(String name, Class<? extends T> bindingTargetType) {
Expand Down

0 comments on commit 2f82f3c

Please sign in to comment.