/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2015 Wisdom Framework * %% * 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. * #L% */ package org.wisdom.executors; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wisdom.api.concurrent.*; import java.util.*; import java.util.concurrent.*; import java.util.stream.Collectors; /** * Common methods used in the different * {@link org.wisdom.api.concurrent.ManagedExecutorService} implementations. */ public abstract class AbstractManagedExecutorService implements ManagedExecutorService { protected final String name; protected final long hungTime; protected ListeningExecutorService executor; protected ThreadPoolExecutor internalPool; protected final Set<Task<?>> tasks = new LinkedHashSet<>(); protected final Logger logger; protected ExecutionStatistics statistics = new ExecutionStatistics(); protected List<ExecutionContextService> ecs; protected AbstractManagedExecutorService(String name, long hungTime, List<ExecutionContextService> ecs) { Preconditions.checkNotNull(name); this.name = name; this.logger = LoggerFactory.getLogger("executor-" + name); this.hungTime = hungTime; this.ecs = ecs; } protected AbstractManagedExecutorService setInternalPool(ThreadPoolExecutor executor) { this.internalPool = executor; this.executor = MoreExecutors.listeningDecorator(this.internalPool); return this; } protected ThreadPoolExecutor getInternalPool() { return internalPool; } protected ListeningExecutorService getExecutor() { return executor; } public String name() { return this.name; } @Override public ExecutionStatistics getExecutionTimeStatistics() { return statistics.copy(); } @Override public synchronized Collection<ManagedFutureTask> getHungTasks() { return tasks.stream().filter(task -> task.isTaskHang()).collect(Collectors.toList()); } @Override public synchronized void shutdown() { executor.shutdown(); } @Override public synchronized List<Runnable> shutdownNow() { for (Task task : tasks) { task.cancel(true); } return executor.shutdownNow(); } protected synchronized ExecutionContext createExecutionContext() { if (ecs == null) { return null; } List<ExecutionContextService> copy = new ArrayList<>(ecs); List<ExecutionContext> ec = new ArrayList<>(); for (ExecutionContextService svc : copy) { ec.add(svc.prepare()); } return CompositeExecutionContext.create(ec); } @Override public synchronized boolean isShutdown() { return executor.isShutdown(); } /** * Returns {@code true} if all tasks have completed following shut down. * Note that {@code isTerminated} is never {@code true} unless * either {@code shutdown} or {@code shutdownNow} was called first. * * @return {@code true} if all tasks have completed following shut down */ @Override public synchronized boolean isTerminated() { return executor.isTerminated(); } @Override public synchronized boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return executor.awaitTermination(timeout, unit); } @Override public synchronized <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { List<Future<T>> futures = executor.invokeAll(tasks); List<Future<T>> manageable = new ArrayList<>(futures.size()); int i = 0; for (Callable<T> callable : tasks) { Task task = getNewTaskFor(callable); task.submitted(futures.get(i)); manageable.add(task); i++; } return manageable; } protected abstract <T> Task getNewTaskFor(Callable<T> callable); protected abstract <V> Task<V> getNewTaskFor(Runnable task, V result); @Override public synchronized <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { List<Future<T>> futures = executor.invokeAll(tasks, timeout, unit); List<Future<T>> manageable = new ArrayList<>(futures.size()); int i = 0; for (Callable<T> callable : tasks) { Task task = getNewTaskFor(callable); task.submitted(futures.get(i)); manageable.add(task); i++; } return manageable; } @Override public synchronized <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return executor.invokeAny(tasks); } @Override public synchronized <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return executor.invokeAny(tasks, timeout, unit); } @Override public synchronized void execute(Runnable command) { Task<Void> task = getNewTaskFor(command, null); task.execute(); } /** * Returns the largest number of threads that have ever * simultaneously been in the pool. * * @return the number of threads */ @Override public synchronized int getLargestPoolSize() { return internalPool.getLargestPoolSize(); } /** * Returns the maximum allowed number of threads. * * @return the maximum allowed number of threads */ @Override public synchronized int getMaximumPoolSize() { return internalPool.getMaximumPoolSize(); } /** * Returns the current number of threads in the pool. * * @return the number of threads */ @Override public synchronized int getPoolSize() { return internalPool.getPoolSize(); } /** * Returns the core number of threads. * * @return the core number of threads */ @Override public synchronized int getCorePoolSize() { return internalPool.getCorePoolSize(); } /** * Returns the approximate total number of tasks that have * completed execution. Because the states of tasks and threads * may change dynamically during computation, the returned value * is only an approximation, but one that does not ever decrease * across successive calls. * * @return the number of tasks */ @Override public synchronized long getCompletedTaskCount() { return internalPool.getCompletedTaskCount(); } /** * Returns the approximate number of threads that are actively * executing tasks. * * @return the number of threads */ @Override public synchronized int getActiveCount() { return internalPool.getActiveCount(); } /** * Returns the task queue used by this executor. Access to the * task queue is intended primarily for debugging and monitoring. * This queue may be in active use. Retrieving the task queue * does not prevent queued tasks from executing. * * @return the task queue */ @Override public synchronized BlockingQueue<Runnable> getQueue() { return internalPool.getQueue(); } /** * Tries to remove from the work queue all {@link java.util.concurrent.Future} * tasks that have been cancelled. This method can be useful as a * storage reclamation operation, that has no other impact on * functionality. Cancelled tasks are never executed, but may * accumulate in work queues until worker threads can actively * remove them. Invoking this method instead tries to remove them now. * However, this method may fail to remove tasks in * the presence of interference by other threads. */ @Override public synchronized void purge() { internalPool.purge(); } /** * Removes this task from the executor's internal queue if it is * present, thus causing it not to be run if it has not already * started. * <p> * <p>This method may be useful as one part of a cancellation * scheme. It may fail to remove tasks that have been converted * into other forms before being placed on the internal queue. For * example, a task entered using {@code submit} might be * converted into a form that maintains {@code Future} status. * However, in such cases, method {@link #purge} may be used to * remove those Futures that have been cancelled. * * @param task the task to remove * @return {@code true} if the task was removed */ @Override public synchronized boolean remove(Runnable task) { return internalPool.remove(task); } /** * Returns the approximate total number of tasks that have ever been * scheduled for execution. Because the states of tasks and * threads may change dynamically during computation, the returned * value is only an approximation. * * @return the number of tasks */ @Override public synchronized long getTaskCount() { return internalPool.getTaskCount(); } /** * Returns the thread keep-alive time, which is the amount of time * that threads in excess of the core pool size may remain * idle before being terminated. * * @param unit the desired time unit of the result * @return the time limit */ @Override public synchronized long getKeepAliveTime(TimeUnit unit) { return internalPool.getKeepAliveTime(unit); } @Override public synchronized <T> ManagedFutureTask<T> submit(Callable<T> task) { if (task == null) { throw new NullPointerException(); } return getNewTaskFor(task).execute(); } @Override public synchronized <T> ManagedFutureTask<T> submit(Runnable task, T result) { if (task == null) { throw new NullPointerException(); } final Task t = getNewTaskFor(task, result).execute(); t.addListener(() -> tasks.remove(t)); tasks.add(t); return t; } @Override public ManagedFutureTask<?> submit(Runnable task) { // Passing null may lead to issue as submit ask for a non-null parameter. return submit(task, null); //NOSONAR } /** * Computes the execution time of the completed task (given), and add it to the statistics. * * @param task the completed task */ protected synchronized void addToStatistics(Task task) { statistics.accept(task.getTaskCompletionTime() - task.getTaskStartTime()); } }