Use ReentrantLock to skip intermediate close attempt from shutdown hook
See gh-31811
This commit is contained in:
parent
f0abdf2264
commit
eae53560e4
|
@ -27,6 +27,8 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -203,8 +205,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
/** Flag that indicates whether this context has been closed already. */
|
/** Flag that indicates whether this context has been closed already. */
|
||||||
private final AtomicBoolean closed = new AtomicBoolean();
|
private final AtomicBoolean closed = new AtomicBoolean();
|
||||||
|
|
||||||
/** Synchronization monitor for the "refresh" and "destroy". */
|
/** Synchronization lock for the "refresh" and "destroy". */
|
||||||
private final Object startupShutdownMonitor = new Object();
|
private final Lock startupShutdownLock = new ReentrantLock();
|
||||||
|
|
||||||
/** Reference to the JVM shutdown hook, if registered. */
|
/** Reference to the JVM shutdown hook, if registered. */
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -576,7 +578,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void refresh() throws BeansException, IllegalStateException {
|
public void refresh() throws BeansException, IllegalStateException {
|
||||||
synchronized (this.startupShutdownMonitor) {
|
this.startupShutdownLock.lock();
|
||||||
|
try {
|
||||||
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
|
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
|
||||||
|
|
||||||
// Prepare this context for refreshing.
|
// Prepare this context for refreshing.
|
||||||
|
@ -624,6 +627,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
logger.warn("Exception encountered during context initialization - " +
|
logger.warn("Exception encountered during context initialization - " +
|
||||||
"cancelling refresh attempt: " + ex);
|
"cancelling refresh attempt: " + ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy already created singletons to avoid dangling resources.
|
// Destroy already created singletons to avoid dangling resources.
|
||||||
destroyBeans();
|
destroyBeans();
|
||||||
|
|
||||||
|
@ -638,6 +642,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
contextRefresh.end();
|
contextRefresh.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
this.startupShutdownLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -645,12 +652,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
* active flag as well as performing any initialization of property sources.
|
* active flag as well as performing any initialization of property sources.
|
||||||
*/
|
*/
|
||||||
protected void prepareRefresh() {
|
protected void prepareRefresh() {
|
||||||
this.startupDate = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// Remove shutdown hook during refresh phase.
|
|
||||||
removeShutdownHook();
|
|
||||||
|
|
||||||
// Switch to active.
|
// Switch to active.
|
||||||
|
this.startupDate = System.currentTimeMillis();
|
||||||
this.closed.set(false);
|
this.closed.set(false);
|
||||||
this.active.set(true);
|
this.active.set(true);
|
||||||
|
|
||||||
|
@ -970,9 +973,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
|
|
||||||
// Publish the final event.
|
// Publish the final event.
|
||||||
publishEvent(new ContextRefreshedEvent(this));
|
publishEvent(new ContextRefreshedEvent(this));
|
||||||
|
|
||||||
// Restore shutdown hook if registered before.
|
|
||||||
restoreShutdownHook();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1022,37 +1022,20 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
|
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized (startupShutdownMonitor) {
|
if (startupShutdownLock.tryLock()) {
|
||||||
|
try {
|
||||||
doClose();
|
doClose();
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
startupShutdownLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
|
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeShutdownHook() {
|
|
||||||
if (this.shutdownHook != null) {
|
|
||||||
try {
|
|
||||||
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
|
|
||||||
}
|
|
||||||
catch (IllegalStateException ex) {
|
|
||||||
// ignore - VM is already shutting down
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreShutdownHook() {
|
|
||||||
if (this.shutdownHook != null) {
|
|
||||||
try {
|
|
||||||
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
|
|
||||||
}
|
|
||||||
catch (IllegalStateException | IllegalArgumentException ex) {
|
|
||||||
// ignore - VM is already shutting down or hook already registered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close this application context, destroying all beans in its bean factory.
|
* Close this application context, destroying all beans in its bean factory.
|
||||||
* <p>Delegates to {@code doClose()} for the actual closing procedure.
|
* <p>Delegates to {@code doClose()} for the actual closing procedure.
|
||||||
|
@ -1062,12 +1045,23 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
synchronized (this.startupShutdownMonitor) {
|
if (this.startupShutdownLock.tryLock()) {
|
||||||
// If we registered a JVM shutdown hook, we don't need it anymore now:
|
try {
|
||||||
// We're already explicitly closing the context.
|
|
||||||
removeShutdownHook();
|
|
||||||
|
|
||||||
doClose();
|
doClose();
|
||||||
|
// If we registered a JVM shutdown hook, we don't need it anymore now:
|
||||||
|
// We've already explicitly closed the context.
|
||||||
|
if (this.shutdownHook != null) {
|
||||||
|
try {
|
||||||
|
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// ignore - VM is already shutting down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.startupShutdownLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue