Author: etnu
Date: Wed Nov 26 03:53:55 2008
New Revision: 720820
URL: http://svn.apache.org/viewvc?rev=720820&view=rev
Log:
Try to execute one of the concurrently preloaded items in the originating
thread. This avoids high thread growth on the thread pool when using a cached
thread pool, and reduces blocking on fixed size thread pools.
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderService.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderServiceTest.java
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderService.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderService.java?rev=720820&r1=720819&r2=720820&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderService.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderService.java
Wed Nov 26 03:53:55 2008
@@ -20,16 +20,21 @@
import org.apache.shindig.gadgets.GadgetContext;
import org.apache.shindig.gadgets.spec.GadgetSpec;
+import com.google.common.collect.Maps;
import com.google.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
/**
* Preloads will be fetched concurrently using the injected ExecutorService,
and they can be read
* lazily using the returned map of futures.
+ *
+ * The last preloaded object always executes in the current thread to avoid
creating unnecessary
+ * additional threads when we're blocking the current request anyway.
*/
public class ConcurrentPreloaderService implements PreloaderService {
private final ExecutorService executor;
@@ -43,9 +48,20 @@
public Preloads preload(GadgetContext context, GadgetSpec gadget) {
ConcurrentPreloads preloads = new ConcurrentPreloads();
+ Map<String, Callable<PreloadedData>> tasks = Maps.newHashMap();
for (Preloader preloader : preloaders) {
- Map<String, Callable<PreloadedData>> tasks =
preloader.createPreloadTasks(context, gadget);
- for (Map.Entry<String, Callable<PreloadedData>> entry :
tasks.entrySet()) {
+ tasks.putAll(preloader.createPreloadTasks(context, gadget));
+ }
+
+ int processed = tasks.size();
+ for (Map.Entry<String, Callable<PreloadedData>> entry : tasks.entrySet()) {
+ processed -= 1;
+ if (processed == 0) {
+ // The last preload fires in the current thread.
+ FutureTask<PreloadedData> futureTask = new
FutureTask<PreloadedData>(entry.getValue());
+ futureTask.run();
+ preloads.add(entry.getKey(), futureTask);
+ } else {
preloads.add(entry.getKey(), executor.submit(entry.getValue()));
}
}
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderServiceTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderServiceTest.java?rev=720820&r1=720819&r2=720820&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderServiceTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/ConcurrentPreloaderServiceTest.java
Wed Nov 26 03:53:55 2008
@@ -18,6 +18,9 @@
package org.apache.shindig.gadgets.preload;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import org.apache.shindig.common.testing.TestExecutorService;
import org.apache.shindig.gadgets.GadgetContext;
@@ -30,6 +33,7 @@
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
/**
* Tests for FuturePreloaderService.
@@ -79,6 +83,54 @@
assertEquals(PRELOAD_MAP_VALUE,
preloads.getData(PRELOAD_MAP_KEY).toJson());
}
+ @Test
+ public void multiplePreloadsFiresJustOneInCurrentThread() throws Exception {
+ preloader.tasks.put(PRELOAD_STRING_KEY,
+ new TestPreloadCallable(new DataPreload(PRELOAD_STRING_VALUE)));
+
+ preloader.tasks.put(PRELOAD_NUMERIC_KEY,
+ new TestPreloadCallable(new DataPreload(PRELOAD_MAP_VALUE)));
+
+ preloader.tasks.put(PRELOAD_MAP_KEY,
+ new TestPreloadCallable(new DataPreload(PRELOAD_NUMERIC_VALUE)));
+
+ PreloaderService service = new
ConcurrentPreloaderService(Executors.newFixedThreadPool(5),
+ Arrays.<Preloader>asList(preloader));
+
+ service.preload(null, null);
+
+ TestPreloadCallable first =
(TestPreloadCallable)preloader.tasks.get(PRELOAD_STRING_KEY);
+ TestPreloadCallable second =
(TestPreloadCallable)preloader.tasks.get(PRELOAD_NUMERIC_KEY);
+ TestPreloadCallable third =
(TestPreloadCallable)preloader.tasks.get(PRELOAD_MAP_KEY);
+
+ assertFalse("Multiple preloads executed in the same thread",
+ first.executedThread == second.executedThread ||
+ first.executedThread == third.executedThread ||
+ second.executedThread == third.executedThread);
+
+ Thread current = Thread.currentThread();
+ assertTrue("No preloads executed in the current thread.",
+ current == first.executedThread ||
+ current == second.executedThread ||
+ current == third.executedThread);
+ }
+
+ @Test
+ public void singlePreloadExecutesInCurrentThread() throws Exception {
+ preloader.tasks.put(PRELOAD_STRING_KEY,
+ new TestPreloadCallable(new DataPreload(PRELOAD_STRING_VALUE)));
+
+ PreloaderService service = new
ConcurrentPreloaderService(Executors.newCachedThreadPool(),
+ Arrays.<Preloader>asList(preloader));
+
+ service.preload(null, null);
+
+ TestPreloadCallable first =
(TestPreloadCallable)preloader.tasks.get(PRELOAD_STRING_KEY);
+
+ assertSame("Single request not run in current thread",
+ Thread.currentThread(), first.executedThread);
+ }
+
@Test(expected = PreloadException.class)
public void exceptionsArePropagated() throws PreloadException {
preloader.tasks.put(PRELOAD_STRING_KEY, new TestPreloadCallable(null));
@@ -98,12 +150,14 @@
private static class TestPreloadCallable implements Callable<PreloadedData> {
private final PreloadedData preload;
+ public Thread executedThread;
public TestPreloadCallable(PreloadedData preload) {
this.preload = preload;
}
public PreloadedData call() throws Exception {
+ executedThread = Thread.currentThread();
if (preload == null) {
throw new PreloadException("No preload for this test.");
}