All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opentable.concurrent.OTExecutors Maven / Gradle / Ivy

There is a newer version: 6.0.0
Show newest version
/**
 * Copyright (C) 2012 Ness Computing, Inc.
 *
 * 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.opentable.concurrent;

import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Helper methods for ExecutorServices.
 */
public final class OTExecutors
{
    private static final Logger LOG = LoggerFactory.getLogger(OTExecutors.class);


    private OTExecutors() { }

    /**
     * Wrap an {@link ExecutorService} to make it appropriate for use in a
     * try-with-resources block.  The service is shutdown but may not be terminated
     * by the end of the try statement.
     */
    public static ShutdownExecutorService autoShutdown(ExecutorService service)
    {
        return new ShutdownExecutorService(service);
    }

    /**
     * Wrap an {@link ExecutorService} to make it appropriate for use in a
     * try-with-resources block.  The service is terminated at the end of the
     * try statement unless the timeout elapses, in which case a TimeoutException is thrown.
     */
    public static TerminatingExecutorService autoTerminate(ExecutorService service, Duration timeout)
    {
        return new TerminatingExecutorService(service, timeout);
    }

    /**
     * Wrap a {@link ScheduledExecutorService} to make it appropriate for use in a
     * try-with-resources block.  The service is shutdown but may not be terminated
     * by the end of the try statement.
     */
    public static ShutdownScheduledExecutorService autoShutdown(ScheduledExecutorService service)
    {
        return new ShutdownScheduledExecutorService(service);
    }

    /**
     * Wrap a {@link ScheduledExecutorService} to make it appropriate for use in a
     * try-with-resources block.  The service is terminated at the end of the
     * try statement unless the timeout elapses, in which case a TimeoutException is thrown.
     */
    public static TerminatingScheduledExecutorService autoTerminate(ScheduledExecutorService service, Duration timeout)
    {
        return new TerminatingScheduledExecutorService(service, timeout);
    }

    /**
     * Invoke all of the given callables.  If they all succeed, returns a list of the futures.  All will be
     * {@link Future#isDone()}.  If any fails, returns the list of Futures that succeeded before the failure, and
     * the final future that caused the computation to fail.  The remaining futures will be cancelled.
     * If the calling thread is interrupted, it will make a best-effort attempt to cancel running tasks.
     */
    public static  List> invokeAllExplosively(ExecutorService service, Collection> tasks)
    throws InterruptedException
    {
        final ExecutorCompletionService completionService = new ExecutorCompletionService<>(service);
        final ImmutableList.Builder> results = ImmutableList.builder();
        final Set> inFlight = Sets.newHashSetWithExpectedSize(tasks.size());

        boolean interrupted = false;

        for (Callable task : tasks) {
            inFlight.add(completionService.submit(task));
        }

        while (!inFlight.isEmpty()) {
            final Future future;
            try {
                future = completionService.take();
            } catch (InterruptedException e) {
                interrupted = true;
                break;
            }

            inFlight.remove(future);
            results.add(future);

            try {
                future.get();
            } catch (InterruptedException e) {
                interrupted = true;
                break;
            } catch (ExecutionException e) {
                break;
            }
        }

        for (final Future future : inFlight) {
            future.cancel(true);
        }

        if (interrupted) {
            throw new InterruptedException();
        }

        return results.build();
    }

    /**
     * Initiates shutdown and blocks until all tasks have completed execution, or the timeout occurs, or the current
     * thread is interrupted, whichever happens first. {@code shutdown} is first called, and if the executor times
     * out awaiting termination, {@code shutdownNow} is called, after which
     * {@link #shutdownAndAwaitTermination(ExecutorService, Duration) shutdownAndAwaitTermination} again awaits
     * termination. Note that the same timeout is used when waiting for shutdown after each shutdown command, so this
     * call can potentially block for twice the given timeout. If the thread is interrupted before termination
     * completes, the executor service is again instructed to {@code shutdownNow} and the interrupt status is
     * preserved.
     *
     * This method implementation is inspired by the ExecutorService documentation example.
     *
     * @param pool the executor service to shut down
     * @param timeout the amount of time to wait after shutdown
     * @return true if the executor service terminated and false if the timeout elapsed before termination
     * @throws InterruptedException if the thread is interrupted while waiting for shutdown
     */
    public static boolean shutdownAndAwaitTermination(final ExecutorService pool, final Duration timeout)
            throws InterruptedException {
        final Instant start = Instant.now();
        try {
            final TimeUnit unit = TimeUnit.NANOSECONDS;
            final long nanos = timeout.toNanos();
            pool.shutdown();
            try {
                if (pool.awaitTermination(nanos, unit)) {
                    return true;
                }
            } catch (InterruptedException e) {
                pool.shutdownNow();
                throw e;
            }
            LOG.warn("{} shutdown failed; calling shutdownNow and waiting again", pool);
            pool.shutdownNow();
            pool.awaitTermination(nanos, unit);
            return false;
        } finally {
            LOG.info("{} shutdown spent {}", pool, Duration.between(start, Instant.now()));
        }
    }

    /**
     * Alternate interface to {@link #shutdownAndAwaitTermination(ExecutorService, Duration)} that takes the old-school
     * timeout long and TimeUnit parameters instead of a Duration.
     * @param pool the executor service to shut down
     * @param timeout the amount of time to wait after shutdown
     * @param unit the time unit of the timeout argument
     * @return true if the executor service terminated and false if the timeout elapsed before termination
     * @throws InterruptedException if the thread is interrupted while waiting for shutdown
     */
    public static boolean shutdownAndAwaitTermination(final ExecutorService pool, final long timeout, final TimeUnit unit)
            throws InterruptedException {
        final Duration timeoutDuration = Duration.ofNanos(unit.toNanos(timeout));
        return shutdownAndAwaitTermination(pool, timeoutDuration);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy