com.lambdaworks.redis.resource.DefaultEventLoopGroupProvider Maven / Gradle / Ivy
/*
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lambdaworks.redis.resource;
import static com.lambdaworks.redis.resource.Futures.toBooleanPromise;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import com.lambdaworks.redis.EpollProvider;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.*;
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 Map, EventExecutorGroup> eventLoopGroups = new ConcurrentHashMap<>(2);
private final Map refCounter = new ConcurrentHashMap<>(2);
private final int numberOfThreads;
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 = numberOfThreads;
}
@Override
public T allocate(Class type) {
synchronized (this) {
return addReference(getOrCreate(type));
}
}
private T addReference(T reference) {
synchronized (refCounter){
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);
}
return reference;
}
private T release(T reference) {
synchronized (refCounter) {
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);
}
}
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, createEventLoopGroup(type, numberOfThreads));
}
return (T) eventLoopGroups.get(type);
}
/**
* Create an instance of a {@link EventExecutorGroup}. Supported types are:
*
* - DefaultEventExecutorGroup
* - NioEventLoopGroup
* - EpollEventLoopGroup
*
*
* @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) {
if (DefaultEventExecutorGroup.class.equals(type)) {
return new DefaultEventExecutorGroup(numberOfThreads, new DefaultThreadFactory("lettuce-eventExecutorLoop", true));
}
if (NioEventLoopGroup.class.equals(type)) {
return new NioEventLoopGroup(numberOfThreads, new DefaultThreadFactory("lettuce-nioEventLoop", true));
}
if (EpollProvider.epollEventLoopGroupClass != null && EpollProvider.epollEventLoopGroupClass.equals(type)) {
return EpollProvider.newEventLoopGroup(numberOfThreads, new DefaultThreadFactory("lettuce-epollEventLoop", true));
}
throw new IllegalArgumentException("Type " + type.getName() + " not supported");
}
@Override
public Promise release(EventExecutorGroup eventLoopGroup, long quietPeriod, long timeout, TimeUnit unit) {
Class> key = getKey(release(eventLoopGroup));
if ((key == null && eventLoopGroup.isShuttingDown()) || refCounter.containsKey(eventLoopGroup)) {
DefaultPromise promise = new DefaultPromise(GlobalEventExecutor.INSTANCE);
promise.setSuccess(true);
return promise;
}
if (key != null) {
eventLoopGroups.remove(key);
}
Future> shutdownFuture = eventLoopGroup.shutdownGracefully(quietPeriod, timeout, unit);
return toBooleanPromise(shutdownFuture);
}
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
@SuppressWarnings("unchecked")
public Future shutdown(long quietPeriod, long timeout, TimeUnit timeUnit) {
shutdownCalled = true;
Map, EventExecutorGroup> copy = new HashMap<>(eventLoopGroups);
DefaultPromise overall = new DefaultPromise(GlobalEventExecutor.INSTANCE);
DefaultPromise lastRelease = new DefaultPromise(GlobalEventExecutor.INSTANCE);
Futures.PromiseAggregator> aggregator = new Futures.PromiseAggregator>(
overall);
aggregator.expectMore(1 + copy.size());
aggregator.arm();
for (EventExecutorGroup executorGroup : copy.values()) {
Promise shutdown = toBooleanPromise(release(executorGroup, quietPeriod, timeout, timeUnit));
aggregator.add(shutdown);
}
aggregator.add(lastRelease);
lastRelease.setSuccess(null);
return toBooleanPromise(overall);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy