[#1120] Make sure releaseExternal() can not cause a deadlock if the ExecutorService is shared

This commit is contained in:
Norman Maurer 2013-03-27 09:30:25 +01:00
parent 7013315fd4
commit bce398819a
7 changed files with 53 additions and 24 deletions

View File

@ -88,7 +88,7 @@ public abstract class AbstractNioBossPool<E extends Boss>
public void releaseExternalResources() { public void releaseExternalResources() {
shutdown(); shutdown();
ExecutorUtil.terminate(bossExecutor); ExecutorUtil.shutdownNow(bossExecutor);
} }
public void shutdown() { public void shutdown() {

View File

@ -112,7 +112,7 @@ public abstract class AbstractNioWorkerPool<E extends AbstractNioWorker>
public void releaseExternalResources() { public void releaseExternalResources() {
shutdown(); shutdown();
ExecutorUtil.terminate(workerExecutor); ExecutorUtil.shutdownNow(workerExecutor);
} }
public void shutdown() { public void shutdown() {

View File

@ -122,12 +122,12 @@ public class OioClientSocketChannelFactory implements ClientSocketChannelFactory
public void shutdown() { public void shutdown() {
if (shutdownExecutor) { if (shutdownExecutor) {
ExecutorUtil.terminate(workerExecutor); ExecutorUtil.shutdownNow(workerExecutor);
} }
} }
public void releaseExternalResources() { public void releaseExternalResources() {
shutdown(); shutdown();
ExecutorUtil.terminate(workerExecutor); ExecutorUtil.shutdownNow(workerExecutor);
} }
} }

View File

@ -121,12 +121,12 @@ public class OioDatagramChannelFactory implements DatagramChannelFactory {
public void shutdown() { public void shutdown() {
if (shutdownExecutor) { if (shutdownExecutor) {
ExecutorUtil.terminate(workerExecutor); ExecutorUtil.shutdownNow(workerExecutor);
} }
} }
public void releaseExternalResources() { public void releaseExternalResources() {
shutdown(); shutdown();
ExecutorUtil.terminate(workerExecutor); ExecutorUtil.shutdownNow(workerExecutor);
} }
} }

View File

@ -145,12 +145,12 @@ public class OioServerSocketChannelFactory implements ServerSocketChannelFactory
public void shutdown() { public void shutdown() {
if (shutdownExecutor) { if (shutdownExecutor) {
ExecutorUtil.terminate(workerExecutor); ExecutorUtil.shutdownNow(workerExecutor);
} }
} }
public void releaseExternalResources() { public void releaseExternalResources() {
shutdown(); shutdown();
ExecutorUtil.terminate(workerExecutor); ExecutorUtil.shutdownNow(workerExecutor);
} }
} }

View File

@ -27,6 +27,30 @@ import java.util.concurrent.TimeUnit;
*/ */
public final class ExecutorUtil { public final class ExecutorUtil {
/**
* Try to call {@link ExecutorService#shutdownNow()}
*/
public static void shutdownNow(Executor executor) {
if (executor instanceof ExecutorService) {
ExecutorService es = (ExecutorService) executor;
try {
es.shutdownNow();
} catch (SecurityException ex) {
// Running in a restricted environment - fall back.
try {
es.shutdown();
} catch (SecurityException ex2) {
// Running in a more restricted environment.
// Can't shut down this executor - skip to the next.
} catch (NullPointerException ex2) {
// Some JDK throws NPE here, but shouldn't.
}
} catch (NullPointerException ex) {
// Some JDK throws NPE here, but shouldn't.
}
}
}
/** /**
* Returns {@code true} if and only if the specified {@code executor} * Returns {@code true} if and only if the specified {@code executor}
* is an {@link ExecutorService} and is shut down. Please note that this * is an {@link ExecutorService} and is shut down. Please note that this
@ -88,22 +112,7 @@ public final class ExecutorUtil {
ExecutorService es = (ExecutorService) e; ExecutorService es = (ExecutorService) e;
for (;;) { for (;;) {
try { shutdownNow(es);
es.shutdownNow();
} catch (SecurityException ex) {
// Running in a restricted environment - fall back.
try {
es.shutdown();
} catch (SecurityException ex2) {
// Running in a more restricted environment.
// Can't shut down this executor - skip to the next.
break;
} catch (NullPointerException ex2) {
// Some JDK throws NPE here, but shouldn't.
}
} catch (NullPointerException ex) {
// Some JDK throws NPE here, but shouldn't.
}
try { try {
if (es.awaitTermination(100, TimeUnit.MILLISECONDS)) { if (es.awaitTermination(100, TimeUnit.MILLISECONDS)) {

View File

@ -24,11 +24,15 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandler; import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.util.DummyHandler; import org.jboss.netty.util.DummyHandler;
import org.junit.Test; import org.junit.Test;
@ -271,4 +275,20 @@ public class BootstrapTest {
protected Bootstrap newBootstrap() { protected Bootstrap newBootstrap() {
return new Bootstrap(); return new Bootstrap();
} }
@Test
public void testReleaseSharedNotDeadlock() {
// create bootstraps
final ExecutorService pool = Executors.newFixedThreadPool(2);
final ClientBootstrap client = new ClientBootstrap(
new NioClientSocketChannelFactory(pool,
Executors.newCachedThreadPool()));
final ServerBootstrap server = new ServerBootstrap(
new NioServerSocketChannelFactory(pool,
Executors.newCachedThreadPool()));
// release resources
client.releaseExternalResources();
server.releaseExternalResources();
}
} }