Chromium Code Reviews| 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 |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e6bffc548d3749e2f42748773e27d83b1e9d39c2 |
| --- /dev/null |
| +++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java |
| @@ -0,0 +1,111 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.net.urlconnection; |
| + |
| +import java.io.IOException; |
| +import java.util.concurrent.BlockingQueue; |
| +import java.util.concurrent.Executor; |
| +import java.util.concurrent.LinkedBlockingQueue; |
| +import java.util.concurrent.RejectedExecutionException; |
| + |
| +/** |
| + * A MessageLoop class for use in {@link CronetHttpURLConnection}. |
| + */ |
| +public final class MessageLoop implements Executor { |
| + private final BlockingQueue<Runnable> mQueue; |
| + |
| + // A reusable runnable to quit the message loop. |
| + private final Runnable mQuitTask; |
| + |
| + // Indicates whether this message loop is currently running. |
| + private boolean mLoopRunning = false; |
| + |
| + // Indicates whether waiting for task has been interrupted. The loop cannot |
| + // be safely started if it has been interrupted. Because this might cause |
| + // the loop to terminate immediately if there is a quit task enqueued. |
| + private boolean mWaitingInterrupted = false; |
| + |
| + public MessageLoop() { |
| + mQueue = new LinkedBlockingQueue<Runnable>(); |
| + mQuitTask = new Runnable() { |
| + @Override |
| + public void run() { |
| + mLoopRunning = false; |
| + } |
| + }; |
| + } |
| + |
| + /** |
| + * Runs the message loop. Be sure to call {@link MessageLoop#postQuitTask()} |
| + * to end the loop. If an interruptedException occurs, the loop cannot be |
| + * started again (see {@link #mWaitingInterrupted}). |
| + * @throws IOException |
| + */ |
| + public void loop() throws IOException { |
| + if (mWaitingInterrupted) { |
| + throw new IllegalStateException( |
| + "Cannot run loop as the loop has been terminated."); |
| + } |
| + if (mLoopRunning) { |
| + throw new IllegalStateException( |
| + "Cannot run loop when it is already running."); |
| + } |
| + mLoopRunning = true; |
| + while (mLoopRunning) { |
| + try { |
| + Runnable task = mQueue.take(); // Blocks if the queue is empty. |
| + task.run(); |
| + } catch (InterruptedException | RuntimeException e) { |
| + mLoopRunning = false; |
| + if (e instanceof InterruptedException) { |
| + mWaitingInterrupted = true; |
| + throw new IOException(e); |
| + } else if (e instanceof RuntimeException) { |
|
xunjieli
2014/12/03 19:03:30
Not sure if this is what you have in mind..
mmenke
2014/12/03 20:05:39
I'd like to prevent running the loop again here, t
xunjieli
2014/12/03 20:46:52
Done. I added a new test for this as well.
|
| + throw (RuntimeException) e; |
| + } |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * Posts a reusable runnable, {@link #mQuitTask} to quit the loop. This |
| + * causes the {@link #loop()} to stop after processing all currently |
| + * enqueued messages. |
| + */ |
| + public void postQuitTask() { |
| + execute(mQuitTask); |
| + } |
| + |
| + /** |
| + * Posts a task to the message loop. |
| + */ |
| + @Override |
| + public void execute(Runnable task) throws RejectedExecutionException { |
| + if (task == null) { |
| + throw new IllegalArgumentException(); |
| + } |
| + try { |
| + mQueue.put(task); |
| + } catch (InterruptedException e) { |
| + // In theory this exception won't happen, since we have an blocking |
| + // queue with Integer.MAX_Value, put() call will not block. |
| + throw new RejectedExecutionException(e); |
| + } |
| + } |
| + |
| + /** |
| + * Returns whether the loop is currently running. Used in testing. |
| + */ |
| + public boolean isRunning() { |
| + return mLoopRunning; |
| + } |
| + |
| + /** |
| + * Returns whether waiting for a task has been interrupted. Used in testing. |
| + */ |
| + public boolean isWaitingInterrupted() { |
| + return mWaitingInterrupted; |
| + } |
| +} |