/*-
* Copyright © 2012 Diamond Light Source Ltd.
*
* This file is part of GDA.
*
* GDA is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 3 as published by the Free
* Software Foundation.
*
* GDA is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with GDA. If not, see <http://www.gnu.org/licenses/>.
*/
package gda.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Kills a process if it runs for too long.
*/
public class ProcessKiller {
private static final Logger logger = LoggerFactory.getLogger(ProcessKiller.class);
private final Process process;
private final int timeoutInMs;
private final TimerThread timerThread;
private final WaitThread waitThread;
public ProcessKiller(Process process, int timeoutInMs) {
this.process = process;
this.timeoutInMs = timeoutInMs;
timerThread = new TimerThread();
waitThread = new WaitThread();
timerThread.setName(TimerThread.class.getName() + " for " + process);
waitThread.setName(WaitThread.class.getName() + " for " + process);
}
public void start() {
timerThread.start();
waitThread.start();
}
private volatile boolean processRunning = true;
private volatile boolean keepTiming = true;
private volatile boolean keepWaiting = true;
class TimerThread extends Thread {
@Override
public void run() {
logger.debug("timing process...");
final long startTime = System.currentTimeMillis();
long elapsedTime = 0;
while (keepTiming && elapsedTime < timeoutInMs) {
try {
Thread.sleep(timeoutInMs - elapsedTime);
} catch (InterruptedException e) {
logger.debug("process timer interrupted");
// ignore and continue
}
elapsedTime = System.currentTimeMillis() - startTime;
}
if (processRunning && elapsedTime >= timeoutInMs) {
waitThread.stopWaiting();
logger.warn("process timed out, and will be killed");
process.destroy();
}
logger.debug("process timer finished ({}ms)", elapsedTime);
}
public void stopTiming() {
keepTiming = false;
interrupt();
}
}
class WaitThread extends Thread {
@Override
public void run() {
while (keepWaiting && processRunning) {
try {
logger.debug("waiting for process to terminate...");
process.waitFor();
logger.debug("process has terminated");
processRunning = false;
timerThread.stopTiming();
} catch (InterruptedException e) {
logger.debug("interrupted while waiting for process to terminate");
// ignore and continue
}
}
logger.debug("wait thread finished (running={})", processRunning);
}
public void stopWaiting() {
keepWaiting = false;
interrupt();
}
}
}