001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.common.util;
017
018import java.util.concurrent.ConcurrentHashMap;
019import java.util.concurrent.ConcurrentMap;
020import java.util.concurrent.ExecutorService;
021import java.util.concurrent.Executors;
022import java.util.concurrent.ScheduledExecutorService;
023import java.util.concurrent.SynchronousQueue;
024import java.util.concurrent.ThreadPoolExecutor;
025import java.util.concurrent.TimeUnit;
026import java.util.function.Predicate;
027import org.modeshape.common.annotation.ThreadSafe;
028
029/**
030 * A simple {@link ThreadPoolFactory} implementation.
031 */
032@ThreadSafe
033public class ThreadPools implements ThreadPoolFactory {
034
035    private static final int DEFAULT_MAX_THREAD_COUNT = 4;
036    private static final int DEFAULT_SCHEDULED_THREAD_COUNT = 1;
037
038    private final ConcurrentMap<String, ExecutorService> poolsByName = new ConcurrentHashMap<String, ExecutorService>();
039
040    @Override
041    public ExecutorService getThreadPool( String name ) {
042        return getOrCreateNewPool(name, Executors.newFixedThreadPool(DEFAULT_MAX_THREAD_COUNT, new NamedThreadFactory(name)));
043    }
044
045    @Override
046    public ExecutorService getCachedTreadPool( String name, int maxPoolSize ) {
047        NamedThreadFactory threadFactory = new NamedThreadFactory(name);
048        ExecutorService executorService = new ThreadPoolExecutor(0, maxPoolSize, 60L, TimeUnit.SECONDS, 
049                                                                 new SynchronousQueue<Runnable>(), 
050                                                                 threadFactory);
051        return getOrCreateNewPool(name, executorService);
052    }
053
054    @Override
055    public ScheduledExecutorService getScheduledThreadPool( String name ) {
056        return (ScheduledExecutorService)getOrCreateNewPool(name,
057                                                            Executors.newScheduledThreadPool(DEFAULT_SCHEDULED_THREAD_COUNT,
058                                                                                             new NamedThreadFactory(name)));
059    }
060
061    private ExecutorService getOrCreateNewPool( String name,
062                                                ExecutorService executorService ) {
063        ExecutorService executor = poolsByName.get(name);
064        if (executor == null) {
065            executor = poolsByName.putIfAbsent(name, executorService);
066            if (executor != null) {
067                // There was an existing one created since we originally checked, so shut down the new executor we just created
068                executor.shutdownNow();
069            }
070            executor = executorService;
071        }
072        return executor;
073    }
074
075    @Override
076    public void releaseThreadPool( ExecutorService executor ) {
077        for (String executorServiceName : poolsByName.keySet()) {
078            ExecutorService executorService = poolsByName.get(executorServiceName);
079            if (executor.equals(executorService)) {
080                executorService.shutdown();
081                return;
082            }
083        }
084    }
085
086    @Override
087    public void terminateAllPools( long maxWaitTime, TimeUnit unit ) {
088        poolsByName.values().stream()
089                            .filter(((Predicate<ExecutorService>) ExecutorService::isShutdown).negate())    
090                            .forEach(executorService -> {
091                                executorService.shutdown();
092                                try {
093                                    if (maxWaitTime > 0) {
094                                        executorService.awaitTermination(maxWaitTime, unit);
095                                    }
096                                    executorService.shutdownNow();
097                                } catch (InterruptedException e) {
098                                    if (Thread.interrupted()) {
099                                        executorService.shutdownNow();
100                                    }
101                                }
102                            });
103        poolsByName.clear();
104    }
105}