/*
* Copyright 2016-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.util.shutdown;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* If a shutdown hook causes an unhandled exception and the unhandled exception handler calls
* System.exit we end up deadlocking. This exists solely to prevent that scenario.
*/
public class NonReentrantSystemExit {
private AtomicBoolean shutdownInitiated;
private AtomicInteger exitCode;
private CountDownLatch doExitLatch;
private Thread thread;
public NonReentrantSystemExit() {
this.shutdownInitiated = new AtomicBoolean(false);
this.exitCode = new AtomicInteger(-1);
this.doExitLatch = new CountDownLatch(1);
this.thread =
new Thread(NonReentrantSystemExit.class.getSimpleName()) {
@Override
public void run() {
while (doExitLatch.getCount() > 0) {
try {
doExitLatch.await();
} catch (InterruptedException e) {
// This is one of the rare cases where it's OK to ignore InterruptedException:
// - nobody should be joining on this thread, so it's not like the user will have
// to wait longer or anything,
// - the shutdown hook depends on this thread running, exiting early will actually
// break functionality.
}
}
System.exit(exitCode.get());
}
};
this.thread.start();
}
public void shutdownSoon(int exitCode) {
if (!shutdownInitiated.compareAndSet(false, true)) {
return;
}
this.exitCode.set(exitCode);
doExitLatch.countDown();
}
}