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

org.broadinstitute.hellbender.utils.runtime.ProcessControllerBase Maven / Gradle / Ivy

The newest version!
package org.broadinstitute.hellbender.utils.runtime;

import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.broadinstitute.hellbender.exceptions.GATKException;
import org.broadinstitute.hellbender.utils.Utils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;

public abstract class ProcessControllerBase
{
    private static final Logger logger = LogManager.getLogger(ProcessControllerBase.class);

    protected enum ProcessStream {STDOUT, STDERR}

    // Tracks running controllers.
    protected static final Set> running = Collections.synchronizedSet(new LinkedHashSet<>());

    // We can't use a fixed thread pool size, since we could run out if multiple controllers were to make blocking calls
    final static protected ExecutorService executorService = Executors.newCachedThreadPool(
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    final Thread newThread = Executors.defaultThreadFactory().newThread(r);
                    newThread.setDaemon(true);
                    newThread.setName("GATKProcessController");
                    return newThread;
                }
            }
    );

    // Tracks the running process associated with this controller.
    protected Process process;

    // The capture policy dictates the termination condition when reading output data from the remote process,
    // either read everything (for ProcessController) or read while available (StreamingProcessController).
    protected Future stdOutFuture;
    protected Future stdErrFuture;

    // When a caller destroys a controller a new thread local version will be created
    protected boolean destroyed = false;

    // Useful for debugging if background threads have shut down correctly
    private static int nextControllerId = 0;
    protected final int controllerId;

    public ProcessControllerBase() {
        synchronized (running) {
            controllerId = nextControllerId++;
        }
    }

    /**
     * @return The set of still running processes.
     */
    public static Set> getRunning() {
        synchronized (running) {
            return new LinkedHashSet<>(running);
        }
    }

    /**
     * Executes a command line program with the settings and waits for it to return,
     * processing the output on a background thread.
     *
     * @param settings Settings to be run.
     * @return The output of the command.
     */
    protected Process launchProcess(ProcessSettings settings) {
        Utils.validate(!destroyed, "This controller was destroyed");

        final ProcessBuilder builder = new ProcessBuilder(settings.getCommand());
        builder.directory(settings.getDirectory());

        final Map settingsEnvironment = settings.getEnvironment();
        if (settingsEnvironment != null) {
            final Map builderEnvironment = builder.environment();
            builderEnvironment.clear();
            builderEnvironment.putAll(settingsEnvironment);
        }

        builder.redirectErrorStream(settings.isRedirectErrorStream());

        // Start the process running.
        try {
            process = builder.start();
            running.add(this);
        } catch (IOException e) {
            final String message = String.format("Unable to start command: %s\nReason: %s",
                    StringUtils.join(builder.command(), " "),
                    e.getMessage());
            throw new GATKException(message, e);
        }
        return process;
    }

    @VisibleForTesting
    public Process getProcess() {
        return process;
    }

    /**
     * Stops the process from running and tries to ensure process is cleaned up properly.
     * NOTE: sub-processes started by process may be zombied with their parents set to pid 1.
     * NOTE: capture threads may block on read.
     * TODO: Try to use NIO to interrupt streams.
     */
    abstract void tryCleanShutdown();

    @Override
    @SuppressWarnings("deprecation")
    protected void finalize() throws Throwable {
        try {
            tryCleanShutdown();
        } catch (Exception e) {
            logger.error(e);
        }
        super.finalize();
    }

    protected class OutputCapture implements Callable {
        private final CAPTURE_POLICY capturedProcessStream;
        private final ProcessStream key; // un-referenced, but retained for debugging
        private final String controllerContextName;  // un-referenced, but retained for debugging

        /**
         * Reads in the output of a stream on a background thread to keep the output pipe from backing up and freezing the called process.
         *
         * @param key The stdout or stderr key for this output capture.
         * @param controllerId Unique id of the controller.
         */
        public OutputCapture(
                final CAPTURE_POLICY capturedProcessStream,
                ProcessStream key,
                final String className,
                int controllerId) {
            this.key = key;
            this.capturedProcessStream = capturedProcessStream;
            this.controllerContextName = String.format(
                    "OutputCapture-%s-%d-%s", className, controllerId, key.name().toLowerCase()
            );
        }

        /**
         * Runs the capture.
         */
        @Override
        public CAPTURE_POLICY call() {
            try {
                // Delegate to the capture stream and let it's policy dictate how much to read
                capturedProcessStream.read();
            } catch (IOException e) {
                logger.error("Error reading process output", e);
            }
            return capturedProcessStream;
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy