import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.Lifecycle;
import org.sonar.process.Lifecycle.State;
import org.sonar.process.ProcessCommands;
+import org.sonar.process.ProcessUtils;
import org.sonar.process.SystemExit;
public class Monitor {
public void awaitTermination() {
while (awaitChildProcessesTermination()) {
trace("await termination of restartor...");
- awaitTermination(restartor);
+ ProcessUtils.awaitTermination(restartor);
}
cleanAfterTermination();
}
trace("finished waiting, restartRequested={}", restartRequested);
if (restartRequested) {
trace("awaitTermination restartor={}", restartor);
- awaitTermination(restartor);
+ ProcessUtils.awaitTermination(restartor);
}
return restartRequested;
}
trace("await termination of child processes...");
List<WatcherThread> watcherThreadsCopy = new ArrayList<>(this.watcherThreads);
for (WatcherThread watcherThread : watcherThreadsCopy) {
- awaitTermination(watcherThread);
+ ProcessUtils.awaitTermination(watcherThread);
}
trace("all child processes done");
return hasRestartBeenRequested(watcherThreadsCopy);
trace("start hard stop async...");
stopAsync(State.HARD_STOPPING);
trace("await termination of terminator...");
- awaitTermination(terminator);
+ ProcessUtils.awaitTermination(terminator);
cleanAfterTermination();
trace("exit...");
systemExit.exit(0);
if (lifecycle.tryToMoveTo(State.STOPPED)) {
trace("await termination of restartWatcher and hardStopWatcher...");
// wait for restartWatcher and hardStopWatcher to cleanly stop
- awaitTermination(restartWatcher, hardStopWatcher);
+ ProcessUtils.awaitTermination(restartWatcher, hardStopWatcher);
trace("restartWatcher done");
// removing shutdown hook to avoid called stop() unnecessarily unless already in shutdownHook
if (!systemExit.isInShutdownHook()) {
}
}
- private static void awaitTermination(Thread... threads) {
- for (Thread thread : threads) {
- awaitTermination(thread);
- }
- }
-
- private static void awaitTermination(@Nullable Thread t) {
- if (t == null || Thread.currentThread() == t) {
- return;
- }
-
- while (t.isAlive()) {
- try {
- t.join();
- } catch (InterruptedException e) {
- // ignore and stop blocking
- }
- }
- }
-
private static void trace(String s) {
LOG.trace(s);
}
import org.sonar.test.TestUtils;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.process.ProcessUtils.awaitTermination;
public class ProcessUtilsTest {
public void private_constructor() {
assertThat(TestUtils.hasOnlyPrivateConstructors(ProcessUtils.class)).isTrue();
}
+
+ @Test
+ public void awaitTermination_does_not_fail_on_null_Thread_argument() {
+ awaitTermination((Thread) null);
+ }
+
+ @Test(timeout = 100L)
+ public void awaitTermination_does_not_wait_on_currentThread() {
+ awaitTermination(Thread.currentThread());
+ }
+
+ @Test(timeout = 3000)
+ public void awaitTermination_ignores_interrupted_exception_of_current_thread() throws InterruptedException {
+ final EverRunningThread runningThread = new EverRunningThread();
+ final Thread safeJoiner = new Thread() {
+ @Override
+ public void run() {
+ awaitTermination(runningThread);
+ }
+ };
+ final Thread simpleJoiner = new Thread() {
+ @Override
+ public void run() {
+ try {
+ runningThread.join();
+ } catch (InterruptedException e) {
+ System.err.println("runningThread interruption detected in SimpleJoiner");
+ }
+ }
+ };
+ runningThread.start();
+ safeJoiner.start();
+ simpleJoiner.start();
+
+ // interrupt safeJoiner _before simpleJoiner to work around some arbitrary sleep delay_ which should not stop watching
+ safeJoiner.interrupt();
+
+ // interrupting simpleJoiner which should stop
+ simpleJoiner.interrupt();
+
+ while (simpleJoiner.isAlive()) {
+ // wait for simpleJoiner to stop
+ }
+
+ // safeJoiner must still be alive
+ assertThat(safeJoiner.isAlive()).isTrue() ;
+
+ // stop runningThread
+ runningThread.stopIt();
+
+ while (runningThread.isAlive()) {
+ // wait for runningThread to stop
+ }
+
+ // wait for safeJoiner to stop because runningThread has stopped, if it doesn't, the test will fail with a timeout
+ safeJoiner.join();
+ }
+
+ @Test(timeout = 100L)
+ public void awaitTermination_of_vararg_does_not_fail_when_there_is_a_null_or_current_thread() {
+ awaitTermination(null, Thread.currentThread(), null);
+ }
+
+ private static class EverRunningThread extends Thread {
+ private volatile boolean stop = false;
+
+ @Override
+ public void run() {
+ while (!stop) {
+ // infinite loop!
+ }
+ }
+
+ public void stopIt() {
+ this.stop = true;
+ }
+ }
+
}