Fixes pathological slowness in remote UI logger (GPE).

Before: all logging to remote UI occurred in a synchronous, blocking manner.

After: except for creating top-level loggers, all sub-branches and log events happen asynchronously and are flushed on a background thread.

http://gwt-code-reviews.appspot.com/550801/show
Review by: rdayal


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8242 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/shell/remoteui/MessageTransport.java b/dev/core/src/com/google/gwt/dev/shell/remoteui/MessageTransport.java
index 2b60808..093ad21 100644
--- a/dev/core/src/com/google/gwt/dev/shell/remoteui/MessageTransport.java
+++ b/dev/core/src/com/google/gwt/dev/shell/remoteui/MessageTransport.java
@@ -20,6 +20,7 @@
 import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.MessageType;
 import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Request;
 import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Response;
+import com.google.gwt.dev.util.Callback;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -29,13 +30,11 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -85,31 +84,23 @@
   }
 
   class PendingRequest extends PendingSend {
-    private final ReentrantLock lock = new ReentrantLock();
-    private final Condition availableResponseCondition = lock.newCondition();
-    private Response responseMessage;
-    private Exception exception;
+    private final Callback<Response> callback;
     private final Message message;
 
-    public PendingRequest(Message message) {
+    PendingRequest(Message message, Callback<Response> callback) {
       this.message = message;
+      this.callback = callback;
     }
 
     @Override
-    public void failed(Exception e) {
+    void failed(Exception e) {
+      assert e != null;
       pendingRequestMap.remove(message.getMessageId());
-
-      lock.lock();
-      try {
-        exception = e;
-        availableResponseCondition.signal();
-      } finally {
-        lock.unlock();
-      }
+      callback.onError(e);
     }
 
     @Override
-    public void send(OutputStream outputStream) throws IOException {
+    void send(OutputStream outputStream) throws IOException {
       int messageId = message.getMessageId();
       pendingRequestMap.put(messageId, this);
       message.writeDelimitedTo(outputStream);
@@ -122,50 +113,16 @@
      * @param responseMessage the server's response
      * @throws InterruptedException
      */
-    public void setResponse(Message.Response responseMessage)
-        throws InterruptedException {
+    void setResponse(Response responseMessage) {
       assert (responseMessage != null);
-      lock.lock();
-      try {
-        if (this.responseMessage != null) {
-          throw new IllegalStateException("Response has already been set.");
-        }
-        this.responseMessage = responseMessage;
-        availableResponseCondition.signal();
-      } finally {
-        lock.unlock();
-      }
-    }
-
-    /**
-     * Waits for a response to be returned for a given request.
-     * 
-     * @return the response from the server
-     * @throws Exception if an exception occurred while processing the request
-     */
-    public Response waitForResponse() throws Exception {
-      lock.lock();
-
-      try {
-        while (responseMessage == null && exception == null) {
-          availableResponseCondition.await();
-        }
-
-        if (exception != null) {
-          throw exception;
-        }
-
-        return responseMessage;
-      } finally {
-        lock.unlock();
-      }
+      callback.onDone(responseMessage);
     }
   }
 
   static class PendingRequestMap {
     private final Lock mapLock = new ReentrantLock();
-    private final Map<Integer, PendingRequest> requestIdToPendingServerRequest = new HashMap<Integer, PendingRequest>();
     private boolean noMoreAdds;
+    private final Map<Integer, PendingRequest> requestIdToPendingServerRequest = new HashMap<Integer, PendingRequest>();
 
     public void blockAdds(Exception e) {
       mapLock.lock();
@@ -222,22 +179,28 @@
   }
 
   abstract class PendingSend {
-    public abstract void failed(Exception e);
+    abstract void failed(Exception e);
 
-    public abstract void send(OutputStream outputStream) throws IOException;
+    abstract void send(OutputStream outputStream) throws IOException;
   }
 
-  private static final int DEFAULT_SERVICE_THREADS = 2;
-
+  /**
+   * A callable that does nothing.
+   */
+  private static final Callable<Response> DUMMY_CALLABLE = new Callable<Response>() {
+    public Response call() throws Exception {
+      return null;
+    }
+  };
+  private final InputStream inputStream;
   private final AtomicBoolean isStarted = new AtomicBoolean(false);
   private final AtomicInteger nextMessageId = new AtomicInteger();
+  private final OutputStream outputStream;
+  private final PendingRequestMap pendingRequestMap = new PendingRequestMap();
   private final RequestProcessor requestProcessor;
   private final LinkedBlockingQueue<PendingSend> sendQueue = new LinkedBlockingQueue<PendingSend>();
-  private final ExecutorService serverRequestExecutor;
-  private final PendingRequestMap pendingRequestMap = new PendingRequestMap();
+
   private final TerminationCallback terminationCallback;
-  private final InputStream inputStream;
-  private final OutputStream outputStream;
 
   /**
    * Create a new instance using the given streams and request processor.
@@ -256,7 +219,6 @@
     this.terminationCallback = terminationCallback;
     this.inputStream = inputStream;
     this.outputStream = outputStream;
-    serverRequestExecutor = Executors.newFixedThreadPool(DEFAULT_SERVICE_THREADS);
   }
 
   /**
@@ -266,24 +228,65 @@
    * 
    * @return a {@link Future} that can be used to access the server's response
    */
-  public Future<Response> executeRequestAsync(final Request requestMessage) {
-    Future<Response> responseFuture = serverRequestExecutor.submit(new Callable<Response>() {
-      public Response call() throws Exception {
-        Message.Builder messageBuilder = Message.newBuilder();
-        int messageId = nextMessageId.getAndIncrement();
-        messageBuilder.setMessageId(messageId);
-        messageBuilder.setMessageType(Message.MessageType.REQUEST);
-        messageBuilder.setRequest(requestMessage);
+  public Future<Response> executeRequestAsync(Request requestMessage) {
+    Message.Builder messageBuilder = Message.newBuilder();
+    int messageId = nextMessageId.getAndIncrement();
+    messageBuilder.setMessageId(messageId);
+    messageBuilder.setMessageType(Message.MessageType.REQUEST);
+    messageBuilder.setRequest(requestMessage);
 
-        Message message = messageBuilder.build();
-        PendingRequest pendingRequest = new PendingRequest(message);
-        sendQueue.put(pendingRequest);
+    Message message = messageBuilder.build();
 
-        return pendingRequest.waitForResponse();
+    class FutureTaskExtension extends FutureTask<Response> {
+      private FutureTaskExtension() {
+        super(DUMMY_CALLABLE);
       }
-    });
 
-    return responseFuture;
+      public void set(Response v) {
+        super.set(v);
+      }
+
+      public void setException(Throwable t) {
+        super.setException(t);
+      }
+    }
+
+    final FutureTaskExtension future = new FutureTaskExtension();
+    PendingRequest pendingRequest = new PendingRequest(message,
+        new Callback<Response>() {
+
+          public void onDone(Response result) {
+            future.set(result);
+          }
+
+          public void onError(Throwable t) {
+            future.setException(t);
+          }
+        });
+    sendQueue.add(pendingRequest);
+    return future;
+  }
+
+  /**
+   * Asynchronously executes the request on a remote server. The callback will
+   * generally be called by another thread. Memory consistency effects: actions
+   * in a thread prior to calling this method happen-before the callback is
+   * invoked.
+   * 
+   * @param requestMessage The request to execute
+   * @param callback The callback to invoke when the response is received
+   */
+  public void executeRequestAsync(Request requestMessage,
+      Callback<Response> callback) {
+    Message.Builder messageBuilder = Message.newBuilder();
+    int messageId = nextMessageId.getAndIncrement();
+    messageBuilder.setMessageId(messageId);
+    messageBuilder.setMessageType(Message.MessageType.REQUEST);
+    messageBuilder.setRequest(requestMessage);
+
+    Message message = messageBuilder.build();
+    PendingRequest pendingRequest = new PendingRequest(message, callback);
+    sendQueue.add(pendingRequest);
   }
 
   /**
@@ -351,7 +354,7 @@
     } catch (Exception e) {
       messageBuilder.setMessageType(Message.MessageType.FAILURE);
       Message.Failure.Builder failureMessage = Message.Failure.newBuilder();
-      
+
       failureMessage.setMessage(e.getLocalizedMessage() != null
           ? e.getLocalizedMessage() : e.getClass().getName());
       StringWriter sw = new StringWriter();
@@ -405,8 +408,7 @@
     }
   }
 
-  private void processServerResponse(int messageId, Response response)
-      throws InterruptedException {
+  private void processServerResponse(int messageId, Response response) {
     PendingRequest pendingServerRequest = pendingRequestMap.remove(messageId);
     if (pendingServerRequest != null) {
       pendingServerRequest.setResponse(response);
diff --git a/dev/core/src/com/google/gwt/dev/shell/remoteui/RemoteUI.java b/dev/core/src/com/google/gwt/dev/shell/remoteui/RemoteUI.java
index 450319c..d69d69c 100644
--- a/dev/core/src/com/google/gwt/dev/shell/remoteui/RemoteUI.java
+++ b/dev/core/src/com/google/gwt/dev/shell/remoteui/RemoteUI.java
@@ -72,12 +72,11 @@
       String url, String tabKey, String moduleName, String sessionKey,
       String agentTag, byte[] agentIcon, Type logLevel) {
 
-    int logHandle;
-    logHandle = viewerServiceClient.addModuleLog(remoteSocket, url, tabKey,
-        moduleName, sessionKey, agentTag, agentIcon);
+    final int logHandle = viewerServiceClient.addModuleLog(remoteSocket, url,
+        tabKey, moduleName, sessionKey, agentTag, agentIcon);
     final ViewerServiceTreeLogger moduleLogger = new ViewerServiceTreeLogger(
         viewerServiceClient);
-    moduleLogger.setLogHandle(logHandle);
+    moduleLogger.initLogHandle(logHandle);
     moduleLogger.setMaxDetail(getLogLevel());
     ModuleHandle handle = new ModuleHandle() {
       public TreeLogger getLogger() {
@@ -90,11 +89,8 @@
             return;
           }
         }
-
-        ViewerServiceTreeLogger moduleLogger = (ViewerServiceTreeLogger) (getLogger());
-
         try {
-          viewerServiceClient.disconnectLog(moduleLogger.getLogHandle());
+          viewerServiceClient.disconnectLog(logHandle);
         } finally {
           synchronized (modulesLock) {
             modules.remove(this);
diff --git a/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceClient.java b/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceClient.java
index e04a3ff..820adfc 100644
--- a/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceClient.java
+++ b/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceClient.java
@@ -26,6 +26,7 @@
 import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Request.ViewerRequest.RequestType;
 import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Response.ViewerResponse;
 import com.google.gwt.dev.shell.remoteui.RemoteMessageProto.Message.Response.ViewerResponse.CapabilityExchange.Capability;
+import com.google.gwt.dev.util.Callback;
 import com.google.gwt.dev.util.log.AbstractTreeLogger;
 
 import java.util.List;
@@ -41,6 +42,15 @@
  */
 public class ViewerServiceClient {
 
+  private static final Callback<Response> DUMMY_CALLBACK = new Callback<Response>() {
+    public void onDone(Response result) {
+    }
+
+    public void onError(Throwable t) {
+      // TODO(rdayal): handle errors?
+    }
+  };
+
   private final MessageTransport transport;
 
   /**
@@ -56,6 +66,8 @@
   /**
    * Add an entry that also serves as a log branch.
    * 
+   * @param indexInParent The index of this entry/branch within the parent
+   *          logger
    * @param type The severity of the log message.
    * @param msg The message.
    * @param caught An exception associated with the message
@@ -63,12 +75,11 @@
    *          information related to the log message
    * @param parentLogHandle The log handle of the parent of this log
    *          entry/branch
-   * @param indexInParent The index of this entry/branch within the parent
-   *          logger
-   * @return the log handle of the newly-created branch logger
+   * @param callback the callback to call when a branch handle is available
    */
-  public int addLogBranch(Type type, String msg, Throwable caught,
-      HelpInfo helpInfo, int parentLogHandle, int indexInParent) {
+  public void addLogBranch(int indexInParent, Type type, String msg,
+      Throwable caught, HelpInfo helpInfo, int parentLogHandle,
+      final Callback<Integer> callback) {
 
     LogData.Builder logDataBuilder = generateLogData(type, msg, caught,
         helpInfo);
@@ -85,16 +96,21 @@
     Request requestMessage = buildRequestMessageFromViewerRequest(
         viewerRequestBuilder).build();
 
-    Future<Response> responseFuture = transport.executeRequestAsync(requestMessage);
+    transport.executeRequestAsync(requestMessage, new Callback<Response>() {
+      public void onDone(Response result) {
+        callback.onDone(result.getViewerResponse().getAddLogBranch().getLogHandle());
+      }
 
-    return waitForResponseOrThrowUncheckedException(responseFuture).getViewerResponse().getAddLogBranch().getLogHandle();
+      public void onError(Throwable t) {
+        callback.onError(t);
+      }
+    });
   }
 
   /**
    * Add a log entry.
    * 
-   * @param indexOfLogEntryWithinParentLogger The index of this entry within the
-   *          parent logger
+   * @param indexInParent The index of this entry within the parent logger
    * @param type The severity of the log message.
    * @param msg The message.
    * @param caught An exception associated with the message
@@ -102,14 +118,14 @@
    *          information related to the log message
    * @param logHandle The log handle of the parent of this log entry/branch
    */
-  public void addLogEntry(int indexOfLogEntryWithinParentLogger, Type type,
-      String msg, Throwable caught, HelpInfo helpInfo, int logHandle) {
+  public void addLogEntry(int indexInParent, Type type, String msg,
+      Throwable caught, HelpInfo helpInfo, int logHandle) {
     LogData.Builder logDataBuilder = generateLogData(type, msg, caught,
         helpInfo);
 
     ViewerRequest.AddLogEntry.Builder addLogEntryBuilder = ViewerRequest.AddLogEntry.newBuilder();
     addLogEntryBuilder.setLogHandle(logHandle);
-    addLogEntryBuilder.setIndexInLog(indexOfLogEntryWithinParentLogger);
+    addLogEntryBuilder.setIndexInLog(indexInParent);
     addLogEntryBuilder.setLogData(logDataBuilder);
 
     ViewerRequest.Builder viewerRequestBuilder = ViewerRequest.newBuilder();
@@ -119,8 +135,7 @@
     Request requestMessage = buildRequestMessageFromViewerRequest(
         viewerRequestBuilder).build();
 
-    Future<Response> responseFuture = transport.executeRequestAsync(requestMessage);
-    waitForResponseOrThrowUncheckedException(responseFuture);
+    transport.executeRequestAsync(requestMessage, DUMMY_CALLBACK);
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceTreeLogger.java b/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceTreeLogger.java
index 734d41a..ea00347 100644
--- a/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceTreeLogger.java
+++ b/dev/core/src/com/google/gwt/dev/shell/remoteui/ViewerServiceTreeLogger.java
@@ -15,14 +15,67 @@
  */
 package com.google.gwt.dev.shell.remoteui;
 
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.util.Callback;
+import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.dev.util.log.AbstractTreeLogger;
 
+import java.util.List;
+
 /**
  * A tree logger that creates log entries using a ViewerService.
  */
 public final class ViewerServiceTreeLogger extends AbstractTreeLogger {
 
-  private int logHandle = -1;
+  private abstract class Pending {
+    protected final Throwable caught;
+    protected final HelpInfo helpInfo;
+    protected final String msg;
+    protected final TreeLogger.Type type;
+
+    public Pending(Type type, String msg, Throwable caught, HelpInfo helpInfo) {
+      this.caught = caught;
+      this.msg = msg;
+      this.type = type;
+      this.helpInfo = helpInfo;
+    }
+
+    public abstract void send();
+  }
+
+  private class PendingBranch extends Pending {
+    public final ViewerServiceTreeLogger branch;
+
+    public PendingBranch(ViewerServiceTreeLogger branch, Type type, String msg,
+        Throwable caught, HelpInfo helpInfo) {
+      super(type, msg, caught, helpInfo);
+      this.branch = branch;
+    }
+
+    @Override
+    public void send() {
+      sendBranch(branch, type, msg, caught, helpInfo);
+    }
+  }
+
+  private class PendingLog extends Pending {
+    protected final int indexOfLogEntry;
+
+    public PendingLog(int indexOfLogEntry, Type type, String msg,
+        Throwable caught, HelpInfo helpInfo) {
+      super(type, msg, caught, helpInfo);
+      this.indexOfLogEntry = indexOfLogEntry;
+    }
+
+    public void send() {
+      sendEntry(indexOfLogEntry, type, msg, caught, helpInfo);
+    }
+  }
+
+  private volatile int logHandle = -1;
+
+  private List<Pending> pending = Lists.create();
+
   private final ViewerServiceClient viewerServiceClient;
 
   /**
@@ -40,43 +93,71 @@
    * been set as yet; it will only be set once the branch is committed.
    */
   @Override
-  public AbstractTreeLogger doBranch() {
-    ViewerServiceTreeLogger childLogger = new ViewerServiceTreeLogger(
-        viewerServiceClient);
-    return childLogger;
-  }
-
-  /**
-   * Commits the branch, and sets the log handle of the branch logger.
-   */
-  @Override
-  public void doCommitBranch(AbstractTreeLogger childBeingCommitted, Type type,
-      String msg, Throwable caught, HelpInfo helpInfo) {
-    int branchLogHandle = viewerServiceClient.addLogBranch(type, msg, caught,
-        helpInfo, getLogHandle(), childBeingCommitted.getBranchedIndex());
-    ((ViewerServiceTreeLogger) childBeingCommitted).setLogHandle(branchLogHandle);
+  protected AbstractTreeLogger doBranch() {
+    return new ViewerServiceTreeLogger(viewerServiceClient);
   }
 
   @Override
-  public void doLog(int indexOfLogEntryWithinParentLogger, Type type,
-      String msg, Throwable caught, HelpInfo helpInfo) {
-    viewerServiceClient.addLogEntry(indexOfLogEntryWithinParentLogger, type,
-        msg, caught, helpInfo, getLogHandle());
+  protected void doCommitBranch(AbstractTreeLogger childBeingCommitted,
+      Type type, String msg, Throwable caught, HelpInfo helpInfo) {
+    // Already synchronized via superclass.
+    ViewerServiceTreeLogger child = (ViewerServiceTreeLogger) childBeingCommitted;
+    if (isSent()) {
+      // Immediately send the child branch.
+      sendBranch(child, type, msg, caught, helpInfo);
+    } else {
+      // Queue the child branch until I'm committed.
+      pending = Lists.add(pending, new PendingBranch(child, type, msg, caught,
+          helpInfo));
+    }
   }
 
-  /**
-   * Get the handle of this logger. The handle is generated by the viewer
-   * service when this logger is first created. If this logger was created as a
-   * branch of another logger, then the handle will not be available until the
-   * branch has been committed.
-   * 
-   * @return the handle for this logger, or -1 if it has not been set
-   */
-  public int getLogHandle() {
-    return logHandle;
+  @Override
+  protected void doLog(int indexOfLogEntry, Type type, String msg,
+      Throwable caught, HelpInfo helpInfo) {
+    // Already synchronized via superclass.
+    if (isSent()) {
+      // Immediately send the child log entry.
+      sendEntry(indexOfLogEntry, type, msg, caught, helpInfo);
+    } else {
+      // Queue the log entry until I'm committed.
+      pending = Lists.add(pending, new PendingLog(indexOfLogEntry, type, msg,
+          caught, helpInfo));
+    }
   }
 
-  public void setLogHandle(int logHandle) {
-    this.logHandle = logHandle;
+  synchronized void initLogHandle(int newLogHandle) {
+    assert !isSent();
+    logHandle = newLogHandle;
+    for (Pending item : pending) {
+      item.send();
+    }
+    pending = null;
+  }
+
+  void sendBranch(final ViewerServiceTreeLogger branch, Type type, String msg,
+      Throwable caught, HelpInfo helpInfo) {
+    assert isSent();
+    viewerServiceClient.addLogBranch(branch.getBranchedIndex(), type, msg,
+        caught, helpInfo, logHandle, new Callback<Integer>() {
+          public void onDone(Integer result) {
+            branch.initLogHandle(result);
+          }
+
+          public void onError(Throwable t) {
+            // TODO(rdayal): handle errors?
+          }
+        });
+  }
+
+  void sendEntry(int indexOfLogEntry, Type type, String msg, Throwable caught,
+      HelpInfo helpInfo) {
+    assert isSent();
+    viewerServiceClient.addLogEntry(indexOfLogEntry, type, msg, caught,
+        helpInfo, logHandle);
+  }
+
+  private boolean isSent() {
+    return logHandle >= 0;
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/util/Callback.java b/dev/core/src/com/google/gwt/dev/util/Callback.java
new file mode 100644
index 0000000..0dd6380
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/Callback.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util;
+
+/**
+ * Generic callback interface.
+ * 
+ * @param <T> the type of the expected result
+ */
+public interface Callback<T> {
+  /**
+   * Called when an operation completes normally.
+   * 
+   * @param result the result of the operation
+   */
+  void onDone(T result);
+
+  /**
+   * Called when the task fails to complete.
+   * 
+   * @param t the error thrown from the operation
+   */
+  void onError(Throwable t);
+}
diff --git a/dev/core/test/com/google/gwt/dev/shell/remoteui/MessageTransportTest.java b/dev/core/test/com/google/gwt/dev/shell/remoteui/MessageTransportTest.java
index 7f41a84..8a32250 100644
--- a/dev/core/test/com/google/gwt/dev/shell/remoteui/MessageTransportTest.java
+++ b/dev/core/test/com/google/gwt/dev/shell/remoteui/MessageTransportTest.java
@@ -12,12 +12,10 @@
 import junit.framework.TestCase;
 
 import java.io.IOException;
+import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -66,19 +64,11 @@
     }
   }
 
-  private static MockNetwork createMockNetwork() throws IOException,
-      InterruptedException, ExecutionException {
-    final ServerSocket listenSocket = new ServerSocket(0);
-    ExecutorService executorService = Executors.newFixedThreadPool(1);
-    Future<Socket> future = executorService.submit(new Callable<Socket>() {
-      public Socket call() throws Exception {
-        return listenSocket.accept();
-      }
-    });
-
-    Socket clientSocket = new Socket("localhost", listenSocket.getLocalPort());
-    Socket serverSocket = future.get();
-
+  private static MockNetwork createMockNetwork() throws IOException {
+    InetAddress localHost = InetAddress.getLocalHost();
+    ServerSocket listenSocket = new ServerSocket(0, 1, localHost);
+    Socket clientSocket = new Socket(localHost, listenSocket.getLocalPort());
+    Socket serverSocket = listenSocket.accept();
     return new MockNetwork(clientSocket, serverSocket, listenSocket);
   }
 
@@ -91,7 +81,7 @@
    * @throws IOException
    */
   public void testExecuteAsyncRequestWithClosedServerSocket()
-      throws IOException, InterruptedException, ExecutionException {
+      throws IOException, InterruptedException {
     MockNetwork network = createMockNetwork();
 
     /*
@@ -237,9 +227,7 @@
    * exception on the server side ends up throwing the proper exception via the
    * future that the client is waiting on.
    */
-  public void testExecuteRequestAsyncServerThrowsException()
-      throws InterruptedException, ExecutionException, IOException,
-      TimeoutException {
+  public void testExecuteRequestAsyncServerThrowsException() throws IOException {
     MockNetwork network = createMockNetwork();
 
     /*
@@ -319,8 +307,7 @@
    * @throws ExecutionException
    * @throws InterruptedException
    */
-  public void testRequestProcessor() throws IOException, InterruptedException,
-      ExecutionException {
+  public void testRequestProcessor() throws IOException {
     MockNetwork network = createMockNetwork();
 
     // Create the request that will be sent to the server
@@ -390,8 +377,7 @@
    * @throws ExecutionException
    * @throws InterruptedException
    */
-  public void testRequestProcessorThrowsException() throws IOException,
-      InterruptedException, ExecutionException {
+  public void testRequestProcessorThrowsException() throws IOException {
     MockNetwork network = createMockNetwork();
 
     /*