Issue 346: fixed executor doesn't close problem

This commit is contained in:
Adrian Cole 2010-09-10 17:47:24 -07:00
parent 53adab1ab4
commit 6a8382773e
3 changed files with 66 additions and 18 deletions

View File

@ -72,8 +72,10 @@ public class ExecutorServiceModule extends AbstractModule {
} }
} }
private final ExecutorService userExecutorFromConstructor; @VisibleForTesting
private final ExecutorService ioExecutorFromConstructor; final ExecutorService userExecutorFromConstructor;
@VisibleForTesting
final ExecutorService ioExecutorFromConstructor;
@Inject @Inject
public ExecutorServiceModule(@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, public ExecutorServiceModule(@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
@ -107,7 +109,7 @@ public class ExecutorServiceModule extends AbstractModule {
@Named(Constants.PROPERTY_USER_THREADS) @Named(Constants.PROPERTY_USER_THREADS)
ExecutorService provideExecutorService(@Named(Constants.PROPERTY_USER_THREADS) int count, Closer closer) { ExecutorService provideExecutorService(@Named(Constants.PROPERTY_USER_THREADS) int count, Closer closer) {
if (userExecutorFromConstructor != null) if (userExecutorFromConstructor != null)
return shutdownOnClose(userExecutorFromConstructor, closer); return userExecutorFromConstructor;
return shutdownOnClose(newThreadPoolNamed("user thread %d", count), closer); return shutdownOnClose(newThreadPoolNamed("user thread %d", count), closer);
} }
@ -116,7 +118,7 @@ public class ExecutorServiceModule extends AbstractModule {
@Named(Constants.PROPERTY_IO_WORKER_THREADS) @Named(Constants.PROPERTY_IO_WORKER_THREADS)
ExecutorService provideIOExecutor(@Named(Constants.PROPERTY_IO_WORKER_THREADS) int count, Closer closer) { ExecutorService provideIOExecutor(@Named(Constants.PROPERTY_IO_WORKER_THREADS) int count, Closer closer) {
if (ioExecutorFromConstructor != null) if (ioExecutorFromConstructor != null)
return shutdownOnClose(ioExecutorFromConstructor, closer); return ioExecutorFromConstructor;
return shutdownOnClose(newThreadPoolNamed("i/o thread %d", count), closer); return shutdownOnClose(newThreadPoolNamed("i/o thread %d", count), closer);
} }

View File

@ -23,15 +23,20 @@ import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Singleton;
import com.google.common.collect.Lists;
/** /**
* This will close objects in the reverse order that they were added. * This will close objects in the reverse order that they were added.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton
public class Closer implements Closeable { public class Closer implements Closeable {
List<Closeable> methodsToClose = new CopyOnWriteArrayList<Closeable>(); // guice is single threaded. no need to lock this
List<Closeable> methodsToClose = Lists.<Closeable> newArrayList();
public void addToClose(Closeable toClose) { public void addToClose(Closeable toClose) {
methodsToClose.add(toClose); methodsToClose.add(toClose);

View File

@ -19,16 +19,24 @@
package org.jclouds.concurrent.config; package org.jclouds.concurrent.config;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jclouds.Constants;
import org.jclouds.lifecycle.Closer; import org.jclouds.lifecycle.Closer;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
/** /**
* *
@ -37,20 +45,53 @@ import com.google.inject.Injector;
@Test @Test
public class ExecutorServiceModuleTest { public class ExecutorServiceModuleTest {
private Closer closer; @Test
public void testShutdownOnClose() throws IOException {
@BeforeTest
public void setUp() throws Exception {
Injector i = Guice.createInjector(); Injector i = Guice.createInjector();
closer = i.getInstance(Closer.class);
Closer closer = i.getInstance(Closer.class);
ExecutorService executor = createMock(ExecutorService.class);
ExecutorServiceModule.shutdownOnClose(executor, closer);
expect(executor.shutdownNow()).andReturn(ImmutableList.<Runnable> of()).atLeastOnce();
replay(executor);
closer.close();
verify(executor);
} }
@Test @Test
public void testShutdownOnClose() throws IOException { public void testShutdownOnCloseThroughModule() throws IOException {
ExecutorService executor = Executors.newCachedThreadPool();
assert !executor.isShutdown(); ExecutorServiceModule module = new ExecutorServiceModule() {
ExecutorServiceModule.shutdownOnClose(executor, closer);
@Override
protected void configure() {
bindConstant().annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).to(1);
bindConstant().annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).to(1);
super.configure();
}
};
Injector i = Guice.createInjector(module);
assertEquals(module.userExecutorFromConstructor, null);
assertEquals(module.ioExecutorFromConstructor, null);
Closer closer = i.getInstance(Closer.class);
ExecutorService user = i
.getInstance(Key.get(ExecutorService.class, Names.named(Constants.PROPERTY_USER_THREADS)));
ExecutorService io = i.getInstance(Key.get(ExecutorService.class, Names
.named(Constants.PROPERTY_IO_WORKER_THREADS)));
assert !user.isShutdown();
assert !io.isShutdown();
closer.close(); closer.close();
assert executor.isShutdown();
assert user.isShutdown();
assert io.isShutdown();
} }
} }