net.lldp.checksims.util.threading.ParallelAlgorithm.java Source code

Java tutorial

Introduction

Here is the source code for net.lldp.checksims.util.threading.ParallelAlgorithm.java

Source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * See LICENSE.txt included in this distribution for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at LICENSE.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright (c) 2014-2015 Nicholas DeMarinis, Matthew Heon, and Dolan Murvihill
 */

package net.lldp.checksims.util.threading;

import com.google.common.collect.ImmutableSet;

import net.lldp.checksims.ChecksimsException;
import net.lldp.checksims.algorithm.AlgorithmResults;
import net.lldp.checksims.algorithm.SimilarityDetector;
import net.lldp.checksims.algorithm.preprocessor.SubmissionPreprocessor;
import net.lldp.checksims.parse.Percentable;
import net.lldp.checksims.submission.Submission;
import net.lldp.checksims.util.completion.StatusLogger;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Apply a given algorithm to a given set of data in parallel.
 */
public final class ParallelAlgorithm {
    private ParallelAlgorithm() {
    }

    private static Logger logs = LoggerFactory.getLogger(ParallelAlgorithm.class);

    private static int threadCount = Runtime.getRuntime().availableProcessors();
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(threadCount, threadCount, 1,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());

    /**
     * @param threads Number of threads to be used for execution
     */
    public static void setThreadCount(int threads) {
        checkArgument(threads > 0,
                "Attempted to set number of threads to " + threads + ", but must be positive integer!");

        threadCount = threads;
        executor.shutdown();
        // Set up the executor again with the new thread count
        executor = new ThreadPoolExecutor(threadCount, threadCount, 1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
    }

    /**
     * Shut down the executor, preventing any more jobs from being processed.
     */
    public static void shutdownExecutor() {
        executor.shutdown();
    }

    /**
     * @return Number of threads to be used for execution. Defaults to number of CPUs available on system.
     */
    public static int getThreadCount() {
        return threadCount;
    }

    /**
     * Detect similarities in parallel.
     *
     * @param algorithm Algorithm to use for similarity detection
     * @param pairs Pairs of submissions to perform detection on
     * @return Collection of results, one for each pair
     */
    public static <T extends Percentable> Set<AlgorithmResults> parallelSimilarityDetection(
            SimilarityDetector<T> algorithm, Set<Pair<Submission, Submission>> pairs, StatusLogger logger)
            throws ChecksimsException {
        checkNotNull(algorithm);
        checkNotNull(pairs);

        // Map the pairs to ChecksimsWorker instances
        Collection<SimilarityDetectionWorker<T>> workers = pairs.stream()
                .map((pair) -> new SimilarityDetectionWorker<T>(algorithm, pair)).collect(Collectors.toList());

        //TODO do something with the right side?
        return ImmutableSet.copyOf(executeTasks(workers, logger));
    }

    public static Set<Submission> parallelSubmissionPreprocessing(SubmissionPreprocessor preprocessor,
            Set<Submission> submissions, StatusLogger logger) throws ChecksimsException {
        checkNotNull(preprocessor);
        checkNotNull(submissions);

        // Map the submissions to PreprocessorWorker instances
        Collection<PreprocessorWorker> workers = submissions.stream()
                .map((submission) -> new PreprocessorWorker(submission, preprocessor)).collect(Collectors.toList());

        return ImmutableSet.copyOf(executeTasks(workers, logger));
    }

    /**
     * Internal backend: Execute given tasks on a new thread pool.
     *
     * Expects Callable tasks, with non-void returns. If the need for void returning functions emerges, might need
     * another version of this?
     *
     * @param tasks Tasks to execute
     * @param <T> Type returned by the tasks
     * @return Collection of Ts
     */
    private static <T, T2 extends Callable<T>> Collection<T> executeTasks(Collection<T2> tasks, StatusLogger logger)
            throws ChecksimsException {
        checkNotNull(tasks);

        if (tasks.size() == 0) {
            logs.warn("Parallel execution called with no tasks - no work done!");
            return new ArrayList<>();
        }

        if (executor.isShutdown()) {
            throw new ChecksimsException("Attempted to call executeTasks while executor was shut down!");
        }

        logs.info("Starting work using " + threadCount + " threads.");

        // Invoke the executor on all the worker instances
        try {
            // Create a monitoring thread to show progress
            MonitorThread monitor = new MonitorThread(executor, logger);
            Thread monitorThread = new Thread(monitor);
            monitorThread.start();

            List<Future<T>> results = executor.invokeAll(tasks);

            // Stop the monitor
            monitor.shutDown();

            // Unpack the futures
            ArrayList<T> unpackInto = new ArrayList<>();

            for (Future<T> future : results) {
                try {
                    unpackInto.add(future.get());
                } catch (ExecutionException e) {
                    executor.shutdownNow();
                    logs.error("Fatal error in executed job!");
                    throw new ChecksimsException("Error while executing worker for future", e.getCause());
                }
            }

            return unpackInto;
        } catch (InterruptedException e) {
            executor.shutdownNow();
            throw new ChecksimsException("Execution of Checksims was interrupted!", e);
        } catch (RejectedExecutionException e) {
            executor.shutdownNow();
            throw new ChecksimsException("Could not schedule execution of all tasks!", e);
        }
    }
}