
com.hubspot.singularity.runner.base.shared.SafeProcessManager Maven / Gradle / Ivy
package com.hubspot.singularity.runner.base.shared;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.hubspot.mesos.JavaUtils;
public abstract class SafeProcessManager {
private final Logger log;
private final Lock processLock;
// unnecessary code here : we shouldn't really subclass this when all we need is the following
// (1) artifact manager needs to kill to ensure it doesn't start something after killing.
// (2) process lock is unnecessary since we don't need to run multiple processes except in artifact manager
// (3) we could probably hand off these process variables to make them final in some other object.
private volatile Optional currentProcessCmd;
private volatile Optional currentProcess;
private volatile Optional currentProcessPid;
private volatile Optional currentProcessStart;
private final AtomicBoolean killed;
private final ProcessUtils processUtils;
public SafeProcessManager(Logger log) {
this.log = log;
this.processUtils = new ProcessUtils(log);
this.currentProcessCmd = Optional.absent();
this.currentProcess = Optional.absent();
this.currentProcessPid = Optional.absent();
this.processLock = new ReentrantLock();
this.killed = new AtomicBoolean(false);
}
public Logger getLog() {
return log;
}
public boolean wasKilled() {
return killed.get();
}
public void markKilled() {
this.processLock.lock();
try {
killed.set(true);
} finally {
this.processLock.unlock();
}
}
private void lockInterruptibly() {
try {
this.processLock.lockInterruptibly();
} catch (InterruptedException e) {
throw Throwables.propagate(e);
}
}
public Process startProcess(ProcessBuilder builder) {
final String cmd = builder.command().get(0);
log.debug("Starting process {}", Joiner.on(" ").join(builder.command()));
processLock.lock();
Process process = null;
try {
Preconditions.checkState(!killed.get(), "Can not start new process, killed is set");
Preconditions.checkState(!currentProcess.isPresent(), "Can not start new process, already had process");
currentProcessStart = Optional.of(System.currentTimeMillis());
process = builder.start();
currentProcessPid = Optional.of(processUtils.getUnixPID(process));
currentProcess = Optional.of(process);
currentProcessCmd = Optional.of(cmd);
log.debug("Started process {}", getCurrentProcessToString());
} catch (IOException e) {
throw Throwables.propagate(e);
} finally {
processLock.unlock();
}
return process;
}
private void resetCurrentVariables() {
currentProcess = Optional.absent();
currentProcessPid = Optional.absent();
currentProcessCmd = Optional.absent();
currentProcessStart = Optional.absent();
}
protected void processFinished(Optional exitCode) {
lockInterruptibly();
try {
if (currentProcessCmd.isPresent() && currentProcessStart.isPresent()) {
if (exitCode.isPresent()) {
log.debug("Process {} exited with {} after {}", currentProcessCmd.get(), exitCode.get(), JavaUtils.duration(currentProcessStart.get()));
} else {
log.debug("Process {} abandoned after {}", currentProcessCmd.get(), JavaUtils.duration(currentProcessStart.get()));
}
} else {
log.warn("Process finished called on an empty process manager");
}
resetCurrentVariables();
} finally {
processLock.unlock();
}
}
public Optional getCurrentPid() {
return currentProcessPid;
}
public String getCurrentProcessToString() {
return String.format("%s - (pid: %s)", currentProcessCmd.or(""), currentProcessPid.or(0));
}
public void signalTermToProcessIfActive() {
this.processLock.lock();
try {
if (currentProcessPid.isPresent()) {
processUtils.sendSignal(Signal.SIGTERM, currentProcessPid.get());
}
} finally {
this.processLock.unlock();
}
}
public void signalKillToProcessIfActive() {
this.processLock.lock();
try {
if (currentProcess.isPresent()) {
processUtils.sendSignal(Signal.SIGKILL, currentProcessPid.get());
}
} finally {
this.processLock.unlock();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy