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.");
       }


Reply via email to