/*
* Carrot2 project.
*
* Copyright (C) 2002-2016, Dawid Weiss, Stanisław Osiński.
* All rights reserved.
*
* Refer to the full license file "carrot2.LICENSE"
* in the root folder of the repository checkout or at:
* http://www.carrot2.org/carrot2.LICENSE
*/
package org.carrot2.util;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.carrot2.shaded.guava.common.collect.Lists;
import org.carrot2.shaded.guava.common.util.concurrent.ForwardingExecutorService;
/**
* A number of utility methods for working with the {@link Executor}s framework.
*/
public class ExecutorServiceUtils
{
final static class AccountingThreadFactory implements ThreadFactory
{
private final ClassLoader classLoader;
private final String baseName;
private final AtomicInteger counter = new AtomicInteger();
private final List<WeakReference<Thread>> threads =
Collections.synchronizedList(Lists.<WeakReference<Thread>> newArrayList());
public AccountingThreadFactory(ClassLoader classLoader, String baseName)
{
this.classLoader = classLoader;
this.baseName = baseName;
}
@Override
public Thread newThread(Runnable r)
{
final Thread t = new Thread(r, "SharedExecutor-" + baseName + "-" + counter.getAndIncrement());
t.setDaemon(true);
t.setContextClassLoader(classLoader);
threads.add(new WeakReference<Thread>(t));
return t;
}
void join()
{
for (WeakReference<Thread> r : threads) {
Thread t = r.get();
if (t != null) {
try {
t.join();
} catch (InterruptedException e) {
break;
}
}
}
}
}
final static class AccountingExecutorService extends ForwardingExecutorService {
private ExecutorService delegate;
private AccountingThreadFactory threadFactory;
public AccountingExecutorService(int maxConcurrentThreads, AccountingThreadFactory threadFactory)
{
this.delegate = Executors.newFixedThreadPool(maxConcurrentThreads, threadFactory);
this.threadFactory = threadFactory;
}
public void shutdown()
{
super.shutdown();
threadFactory.join();
}
public List<Runnable> shutdownNow()
{
final List<Runnable> result = super.shutdownNow();
threadFactory.join();
return result;
}
@Override
protected ExecutorService delegate()
{
return delegate;
}
}
/**
* @return Return an executor service with a fixed thread pool of
* <code>maxConcurrentThreads</code> threads and context class loader
* initialized to <code>clazz</code>'s context class loader.
* <p>
* A weak reference to the returned object is saved internally to make the
* necessary cleanups in Web applications and other dynamic environments
* possible.
* See <a href="http://issues.carrot2.org/browse/CARROT-388">CARROT-388</a>.
*/
public static ExecutorService createExecutorService(int maxConcurrentThreads,
Class<?> clazz)
{
final String baseName = clazz.getSimpleName();
final ClassLoader classLoader = clazz.getClassLoader();
final AccountingThreadFactory threadFactory =
new AccountingThreadFactory(classLoader, baseName);
return new AccountingExecutorService(maxConcurrentThreads, threadFactory);
}
}