Index: components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java |
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java |
index f3d376c0278596dad3c9f4f81239fc7e8564f5a0..9e1f79f4cb45252273a2a0cc88e2bd1458c863e6 100644 |
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java |
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java |
@@ -5,10 +5,13 @@ |
package org.chromium.net.urlconnection; |
import java.io.IOException; |
+import java.io.InterruptedIOException; |
+import java.net.SocketTimeoutException; |
import java.util.concurrent.BlockingQueue; |
import java.util.concurrent.Executor; |
import java.util.concurrent.LinkedBlockingQueue; |
import java.util.concurrent.RejectedExecutionException; |
+import java.util.concurrent.TimeUnit; |
/** |
* A MessageLoop class for use in {@link CronetHttpURLConnection}. |
@@ -42,13 +45,56 @@ class MessageLoop implements Executor { |
} |
/** |
+ * Retrieves a task from the queue with the given timeout. |
+ * |
+ * @param useTimeout whether to use a timeout. |
+ * @param timeoutNano Time to wait, in nanoseconds. |
+ * @return A non-{@code null} Runnable from the queue. |
+ * @throws InterruptedIOException |
+ */ |
+ private Runnable take(boolean useTimeout, long timeoutNano) throws InterruptedIOException { |
+ Runnable task = null; |
+ try { |
+ if (!useTimeout) { |
+ task = mQueue.take(); // Blocks if the queue is empty. |
+ } else { |
+ // poll returns null upon timeout. |
+ task = mQueue.poll(timeoutNano, TimeUnit.NANOSECONDS); |
+ } |
+ } catch (InterruptedException e) { |
+ InterruptedIOException exception = new InterruptedIOException(); |
+ exception.initCause(e); |
+ throw exception; |
+ } |
+ if (task == null) { |
+ // This will terminate the loop. |
+ throw new SocketTimeoutException(); |
+ } |
+ return task; |
+ } |
+ |
+ /** |
* Runs the message loop. Be sure to call {@link MessageLoop#quit()} |
* to end the loop. If an interruptedException occurs, the loop cannot be |
* started again (see {@link #mLoopFailed}). |
* @throws IOException |
*/ |
public void loop() throws IOException { |
+ loop(0); |
+ } |
+ |
+ /** |
+ * Runs the message loop. Be sure to call {@link MessageLoop#quit()} |
+ * to end the loop. If an interruptedException occurs, the loop cannot be |
+ * started again (see {@link #mLoopFailed}). |
+ * @param timeoutMilli Timeout, in milliseconds, or 0 for no timeout. |
+ * @throws IOException |
+ */ |
+ public void loop(int timeoutMilli) throws IOException { |
assert calledOnValidThread(); |
+ // Use System.nanoTime() which is monotonically increasing. |
+ long startNano = System.nanoTime(); |
+ long timeoutNano = TimeUnit.NANOSECONDS.convert(timeoutMilli, TimeUnit.MILLISECONDS); |
if (mLoopFailed) { |
throw new IllegalStateException( |
"Cannot run loop as an exception has occurred previously."); |
@@ -60,16 +106,15 @@ class MessageLoop implements Executor { |
mLoopRunning = true; |
while (mLoopRunning) { |
try { |
- Runnable task = mQueue.take(); // Blocks if the queue is empty. |
- task.run(); |
- } catch (InterruptedException | RuntimeException e) { |
+ if (timeoutMilli == 0) { |
+ take(false, 0).run(); |
+ } else { |
+ take(true, timeoutNano - System.nanoTime() + startNano).run(); |
+ } |
+ } catch (InterruptedIOException | RuntimeException e) { |
mLoopRunning = false; |
mLoopFailed = true; |
- if (e instanceof InterruptedException) { |
- throw new IOException(e); |
- } else if (e instanceof RuntimeException) { |
- throw (RuntimeException) e; |
- } |
+ throw e; |
} |
} |
} |