
io.lettuce.core.resource.DefaultEventLoopGroupProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lettuce-core Show documentation
Show all versions of lettuce-core Show documentation
Advanced and thread-safe Java Redis client for synchronous, asynchronous, and
reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs
and much more.
The newest version!
package io.lettuce.core.resource;
import static io.lettuce.core.resource.PromiseAdapter.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import io.lettuce.core.internal.LettuceAssert;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseCombiner;
import io.netty.util.concurrent.SucceededFuture;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/**
* Default implementation which manages one event loop group instance per type.
*
* @author Mark Paluch
* @since 3.4
*/
public class DefaultEventLoopGroupProvider implements EventLoopGroupProvider {
protected static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultEventLoopGroupProvider.class);
private final Lock lock = new ReentrantLock();
private final Map, EventExecutorGroup> eventLoopGroups = new ConcurrentHashMap<>(2);
private final Map refCounter = new ConcurrentHashMap<>(2);
private final int numberOfThreads;
private final io.lettuce.core.resource.ThreadFactoryProvider threadFactoryProvider;
private volatile boolean shutdownCalled = false;
/**
* Creates a new instance of {@link DefaultEventLoopGroupProvider}.
*
* @param numberOfThreads number of threads (pool size)
*/
public DefaultEventLoopGroupProvider(int numberOfThreads) {
this(numberOfThreads, DefaultThreadFactoryProvider.INSTANCE);
}
/**
* Creates a new instance of {@link DefaultEventLoopGroupProvider}.
*
* @param numberOfThreads number of threads (pool size)
* @param threadFactoryProvider provides access to {@link ThreadFactory}.
* @since 6.0
*/
public DefaultEventLoopGroupProvider(int numberOfThreads, ThreadFactoryProvider threadFactoryProvider) {
LettuceAssert.isTrue(numberOfThreads > 0, "Number of threads must be greater than zero");
LettuceAssert.notNull(threadFactoryProvider, "ThreadFactoryProvider must not be null");
this.numberOfThreads = numberOfThreads;
this.threadFactoryProvider = threadFactoryProvider;
}
/**
* Creates a new instance of {@link DefaultEventLoopGroupProvider}.
*
* @param numberOfThreads number of threads (pool size)
* @param threadFactoryProvider provides access to {@link io.lettuce.core.resource.ThreadFactoryProvider}.
* @since 6.1.1
*/
public DefaultEventLoopGroupProvider(int numberOfThreads,
io.lettuce.core.resource.ThreadFactoryProvider threadFactoryProvider) {
LettuceAssert.isTrue(numberOfThreads > 0, "Number of threads must be greater than zero");
LettuceAssert.notNull(threadFactoryProvider, "ThreadFactoryProvider must not be null");
this.numberOfThreads = numberOfThreads;
this.threadFactoryProvider = threadFactoryProvider;
}
@Override
public T allocate(Class type) {
lock.lock();
try {
logger.debug("Allocating executor {}", type.getName());
return addReference(getOrCreate(type));
} finally {
lock.unlock();
}
}
private T addReference(T reference) {
lock.lock();
try {
long counter = 0;
if (refCounter.containsKey(reference)) {
counter = refCounter.get(reference);
}
logger.debug("Adding reference to {}, existing ref count {}", reference, counter);
counter++;
refCounter.put(reference, counter);
} finally {
lock.unlock();
}
return reference;
}
private T release(T reference) {
lock.lock();
try {
long counter = 0;
if (refCounter.containsKey(reference)) {
counter = refCounter.get(reference);
}
if (counter < 1) {
logger.debug("Attempting to release {} but ref count is {}", reference, counter);
}
counter--;
if (counter == 0) {
refCounter.remove(reference);
} else {
refCounter.put(reference, counter);
}
} finally {
lock.unlock();
}
return reference;
}
@SuppressWarnings("unchecked")
private T getOrCreate(Class type) {
if (shutdownCalled) {
throw new IllegalStateException("Provider is shut down and can not longer provide resources");
}
if (!eventLoopGroups.containsKey(type)) {
eventLoopGroups.put(type, doCreateEventLoopGroup(type, numberOfThreads, threadFactoryProvider));
}
return (T) eventLoopGroups.get(type);
}
/**
* Customization hook for {@link EventLoopGroup} creation.
*
* @param
* @param type requested event loop group type.
* @param numberOfThreads number of threads to create.
* @param threadFactoryProvider provider for {@link ThreadFactory}.
* @return
* @since 6.0
*/
protected EventExecutorGroup doCreateEventLoopGroup(Class type, int numberOfThreads,
io.lettuce.core.resource.ThreadFactoryProvider threadFactoryProvider) {
return createEventLoopGroup(type, numberOfThreads, threadFactoryProvider);
}
/**
* Create an instance of a {@link EventExecutorGroup} using the default {@link ThreadFactoryProvider}. Supported types are:
*
* - DefaultEventExecutorGroup
* - NioEventLoopGroup
* - EpollEventLoopGroup
* - KqueueEventLoopGroup
*
*
* @param type the type
* @param numberOfThreads the number of threads to use for the {@link EventExecutorGroup}
* @param type parameter
* @return a new instance of a {@link EventExecutorGroup}
* @throws IllegalArgumentException if the {@code type} is not supported.
*/
public static EventExecutorGroup createEventLoopGroup(Class type, int numberOfThreads) {
return createEventLoopGroup(type, numberOfThreads, DefaultThreadFactoryProvider.INSTANCE);
}
/**
* Create an instance of a {@link EventExecutorGroup}. Supported types are:
*
* - DefaultEventExecutorGroup
* - NioEventLoopGroup
* - EpollEventLoopGroup
* - KqueueEventLoopGroup
*
*
* @param type the type
* @param numberOfThreads the number of threads to use for the {@link EventExecutorGroup}
* @param type parameter
* @return a new instance of a {@link EventExecutorGroup}
* @throws IllegalArgumentException if the {@code type} is not supported.
* @since 5.3
*/
static EventExecutorGroup createEventLoopGroup(Class type, int numberOfThreads,
io.lettuce.core.resource.ThreadFactoryProvider factoryProvider) {
logger.debug("Creating executor {}", type.getName());
if (DefaultEventExecutorGroup.class.equals(type)) {
return new DefaultEventExecutorGroup(numberOfThreads,
factoryProvider.getThreadFactory("lettuce-eventExecutorLoop"));
}
if (NioEventLoopGroup.class.equals(type)) {
return new NioEventLoopGroup(numberOfThreads, factoryProvider.getThreadFactory("lettuce-nioEventLoop"));
}
if (EpollProvider.isAvailable()) {
EventLoopResources resources = EpollProvider.getResources();
if (resources.matches(type)) {
return resources.newEventLoopGroup(numberOfThreads, factoryProvider.getThreadFactory("lettuce-epollEventLoop"));
}
}
if (KqueueProvider.isAvailable()) {
EventLoopResources resources = KqueueProvider.getResources();
if (resources.matches(type)) {
return resources.newEventLoopGroup(numberOfThreads,
factoryProvider.getThreadFactory("lettuce-kqueueEventLoop"));
}
}
if (IOUringProvider.isAvailable()) {
EventLoopResources resources = IOUringProvider.getResources();
if (resources.matches(type)) {
return resources.newEventLoopGroup(numberOfThreads,
factoryProvider.getThreadFactory("lettuce-io_uringEventLoop"));
}
}
throw new IllegalArgumentException(String.format("Type %s not supported", type.getName()));
}
@Override
public Promise release(EventExecutorGroup eventLoopGroup, long quietPeriod, long timeout, TimeUnit unit) {
return toBooleanPromise(doRelease(eventLoopGroup, quietPeriod, timeout, unit));
}
private Future> doRelease(EventExecutorGroup eventLoopGroup, long quietPeriod, long timeout, TimeUnit unit) {
logger.debug("Release executor {}", eventLoopGroup);
Class> key = getKey(release(eventLoopGroup));
if ((key == null && eventLoopGroup.isShuttingDown()) || refCounter.containsKey(eventLoopGroup)) {
return new SucceededFuture<>(ImmediateEventExecutor.INSTANCE, true);
}
if (key != null) {
eventLoopGroups.remove(key);
}
return eventLoopGroup.shutdownGracefully(quietPeriod, timeout, unit);
}
private Class> getKey(EventExecutorGroup eventLoopGroup) {
Class> key = null;
Map, EventExecutorGroup> copy = new HashMap<>(eventLoopGroups);
for (Map.Entry, EventExecutorGroup> entry : copy.entrySet()) {
if (entry.getValue() == eventLoopGroup) {
key = entry.getKey();
break;
}
}
return key;
}
@Override
public int threadPoolSize() {
return numberOfThreads;
}
@Override
public Future shutdown(long quietPeriod, long timeout, TimeUnit timeUnit) {
logger.debug("Initiate shutdown ({}, {}, {})", quietPeriod, timeout, timeUnit);
shutdownCalled = true;
Map, EventExecutorGroup> copy = new HashMap<>(eventLoopGroups);
DefaultPromise overall = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
PromiseCombiner combiner = new PromiseCombiner(ImmediateEventExecutor.INSTANCE);
for (EventExecutorGroup executorGroup : copy.values()) {
combiner.add(doRelease(executorGroup, quietPeriod, timeout, timeUnit));
}
combiner.finish(overall);
return PromiseAdapter.toBooleanPromise(overall);
}
/**
* Interface to provide a custom {@link java.util.concurrent.ThreadFactory}. Implementations are asked through
* {@link #getThreadFactory(String)} to provide a thread factory for a given pool name.
*
* @since 6.0
*/
public interface ThreadFactoryProvider extends io.lettuce.core.resource.ThreadFactoryProvider {
/**
* Return a {@link ThreadFactory} for the given {@code poolName}.
*
* @param poolName a descriptive pool name. Typically used as prefix for thread names.
* @return the {@link ThreadFactory}.
*/
@Override
ThreadFactory getThreadFactory(String poolName);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy