/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate licenses
* this file to you 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.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.operation;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
public class ThreadPools {
/**
* runs each runnable of the runnableCollection in it's own thread unless there aren't enough threads available.
* In that case it will partition the runnableCollection to match the number of available threads.
*
* @param executor executor that is used to execute the callableList
* @param poolSize the corePoolSize of the given executor
* @param callableCollection a collection of callable that should be executed
* @param mergeFunction function that will be applied to merge the results of multiple callable in case that they are
* executed together if the threadPool is exhausted
* @param <T> type of the final result
* @return a future that will return a list of the results of the callableList
* @throws RejectedExecutionException in case all threads are busy and overloaded.
*/
public static <T> ListenableFuture<List<T>> runWithAvailableThreads(
ThreadPoolExecutor executor,
int poolSize,
Collection<Callable<T>> callableCollection,
final Function<List<T>, T> mergeFunction) throws RejectedExecutionException {
ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executor);
List<ListenableFuture<T>> futures;
int availableThreads = Math.max(poolSize - executor.getActiveCount(), 1);
if (availableThreads < callableCollection.size()) {
Iterable<List<Callable<T>>> partition = Iterables.partition(callableCollection,
callableCollection.size() / availableThreads);
futures = new ArrayList<>(availableThreads + 1);
for (final List<Callable<T>> callableList : partition) {
futures.add(listeningExecutorService.submit(new Callable<T>() {
@Override
public T call() throws Exception {
List<T> results = new ArrayList<T>(callableList.size());
for (Callable<T> tCallable : callableList) {
results.add(tCallable.call());
}
return mergeFunction.apply(results);
}
}));
}
} else {
futures = new ArrayList<>(callableCollection.size());
for (Callable<T> callable : callableCollection) {
futures.add(listeningExecutorService.submit(callable));
}
}
return Futures.allAsList(futures);
}
}