Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(457)

Unified Diff: components/cronet/android/api/src/org/chromium/net/JavaUrlRequest.java

Issue 2283243002: Allow direct executors in cronet. (Closed)
Patch Set: Improve thread checking Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/cronet/android/api/src/org/chromium/net/JavaUrlRequest.java
diff --git a/components/cronet/android/api/src/org/chromium/net/JavaUrlRequest.java b/components/cronet/android/api/src/org/chromium/net/JavaUrlRequest.java
index aafc1309d01d96f7c2d6c81036f8287fb58a38d2..b07ad04843ce12ad2ca4caf26e19e84af86e2341 100644
--- a/components/cronet/android/api/src/org/chromium/net/JavaUrlRequest.java
+++ b/components/cronet/android/api/src/org/chromium/net/JavaUrlRequest.java
@@ -56,6 +56,7 @@ final class JavaUrlRequest implements UrlRequest {
* exception), or cancellation.
*/
private final AtomicReference<State> mState = new AtomicReference<>(State.NOT_STARTED);
+ private final AtomicBoolean mUploadProviderClosed = new AtomicBoolean(false);
/**
* Traffic stats tag to associate this requests' data use with. It's captured when the request
@@ -63,12 +64,12 @@ final class JavaUrlRequest implements UrlRequest {
* that data use.
*/
private final int mTrafficStatsTag;
+ private final boolean mAllowDirectExecutor;
/* These don't change with redirects */
private String mInitialMethod;
private UploadDataProvider mUploadDataProvider;
private Executor mUploadExecutor;
- private AtomicBoolean mUploadProviderClosed = new AtomicBoolean(false);
/**
* Holds a subset of StatusValues - {@link State#STARTED} can represent
@@ -120,7 +121,7 @@ final class JavaUrlRequest implements UrlRequest {
* @param userExecutor The executor used to dispatch to {@code callback}
*/
JavaUrlRequest(Callback callback, final Executor executor, Executor userExecutor, String url,
- String userAgent) {
+ String userAgent, boolean allowDirectExecutor) {
if (url == null) {
throw new NullPointerException("URL is required");
}
@@ -133,6 +134,8 @@ final class JavaUrlRequest implements UrlRequest {
if (userExecutor == null) {
throw new NullPointerException("userExecutor is required");
}
+
+ this.mAllowDirectExecutor = allowDirectExecutor;
this.mCallbackAsync = new AsyncUrlRequestCallback(callback, userExecutor);
this.mTrafficStatsTag = TrafficStats.getThreadStatsTag();
this.mExecutor = new Executor() {
@@ -237,7 +240,11 @@ final class JavaUrlRequest implements UrlRequest {
mInitialMethod = "POST";
}
this.mUploadDataProvider = uploadDataProvider;
- this.mUploadExecutor = executor;
+ if (mAllowDirectExecutor) {
+ this.mUploadExecutor = executor;
+ } else {
+ this.mUploadExecutor = new DirectPreventingExecutor(executor);
+ }
}
private enum SinkState {
@@ -249,7 +256,7 @@ final class JavaUrlRequest implements UrlRequest {
private final class OutputStreamDataSink implements UploadDataSink {
final AtomicReference<SinkState> mSinkState = new AtomicReference<>(SinkState.NOT_STARTED);
- final Executor mUserExecutor;
+ final Executor mUserUploadExecutor;
final Executor mExecutor;
final HttpURLConnection mUrlConnection;
WritableByteChannel mOutputChannel;
@@ -262,7 +269,7 @@ final class JavaUrlRequest implements UrlRequest {
OutputStreamDataSink(final Executor userExecutor, Executor executor,
HttpURLConnection urlConnection, UploadDataProvider provider) {
- this.mUserExecutor = new Executor() {
+ this.mUserUploadExecutor = new Executor() {
@Override
public void execute(Runnable runnable) {
try {
@@ -299,12 +306,12 @@ final class JavaUrlRequest implements UrlRequest {
if (mWrittenBytes < mTotalBytes || (mTotalBytes == -1 && !finalChunk)) {
mBuffer.clear();
mSinkState.set(SinkState.AWAITING_READ_RESULT);
- mUserExecutor.execute(uploadErrorSetting(new CheckedRunnable() {
+ executeOnUploadExecutor(new CheckedRunnable() {
@Override
public void run() throws Exception {
mUploadProvider.read(OutputStreamDataSink.this, mBuffer);
}
- }));
+ });
} else if (mTotalBytes == -1) {
finish();
} else if (mTotalBytes == mWrittenBytes) {
@@ -347,16 +354,24 @@ final class JavaUrlRequest implements UrlRequest {
mOutputChannel = Channels.newChannel(mUrlConnection.getOutputStream());
}
mSinkState.set(SinkState.AWAITING_READ_RESULT);
- mUserExecutor.execute(uploadErrorSetting(new CheckedRunnable() {
+ executeOnUploadExecutor(new CheckedRunnable() {
@Override
public void run() throws Exception {
mUploadProvider.read(OutputStreamDataSink.this, mBuffer);
}
- }));
+ });
}
}));
}
+ private void executeOnUploadExecutor(CheckedRunnable runnable) {
+ try {
+ mUserUploadExecutor.execute(uploadErrorSetting(runnable));
+ } catch (RejectedExecutionException e) {
+ enterUploadErrorState(e);
+ }
+ }
+
void finish() throws IOException {
if (mOutputChannel != null) {
mOutputChannel.close();
@@ -365,7 +380,7 @@ final class JavaUrlRequest implements UrlRequest {
}
void start(final boolean firstTime) {
- mUserExecutor.execute(uploadErrorSetting(new CheckedRunnable() {
+ executeOnUploadExecutor(new CheckedRunnable() {
@Override
public void run() throws Exception {
mTotalBytes = mUploadProvider.getLength();
@@ -401,7 +416,7 @@ final class JavaUrlRequest implements UrlRequest {
}
}
}
- }));
+ });
}
}
@@ -744,10 +759,17 @@ final class JavaUrlRequest implements UrlRequest {
private final class AsyncUrlRequestCallback {
final UrlRequest.Callback mCallback;
final Executor mUserExecutor;
+ final Executor mFallbackExecutor;
AsyncUrlRequestCallback(Callback callback, final Executor userExecutor) {
this.mCallback = callback;
- this.mUserExecutor = userExecutor;
+ if (mAllowDirectExecutor) {
+ this.mUserExecutor = userExecutor;
+ this.mFallbackExecutor = null;
+ } else {
+ mUserExecutor = new DirectPreventingExecutor(userExecutor);
+ mFallbackExecutor = userExecutor;
+ }
}
void sendStatus(final StatusListener listener, final int status) {
@@ -763,7 +785,8 @@ final class JavaUrlRequest implements UrlRequest {
try {
mUserExecutor.execute(userErrorSetting(currentState, runnable));
} catch (RejectedExecutionException e) {
- enterUserErrorState(currentState, e);
+ enterErrorState(currentState,
+ new UrlRequestException("Exception posting task to executor", e));
}
}
@@ -777,25 +800,25 @@ final class JavaUrlRequest implements UrlRequest {
}
void onResponseStarted(UrlResponseInfo info) {
- execute(State.AWAITING_READ, new CheckedRunnable() {
- @Override
- public void run() throws Exception {
- if (mState.compareAndSet(State.STARTED, State.AWAITING_READ)) {
+ if (mState.compareAndSet(State.STARTED, State.AWAITING_READ)) {
+ execute(State.AWAITING_READ, new CheckedRunnable() {
+ @Override
+ public void run() throws Exception {
mCallback.onResponseStarted(JavaUrlRequest.this, mUrlResponseInfo);
}
- }
- });
+ });
+ }
}
void onReadCompleted(final UrlResponseInfo info, final ByteBuffer byteBuffer) {
- execute(State.AWAITING_READ, new CheckedRunnable() {
- @Override
- public void run() throws Exception {
- if (mState.compareAndSet(State.READING, State.AWAITING_READ)) {
+ if (mState.compareAndSet(State.READING, State.AWAITING_READ)) {
+ execute(State.AWAITING_READ, new CheckedRunnable() {
+ @Override
+ public void run() throws Exception {
mCallback.onReadCompleted(JavaUrlRequest.this, info, byteBuffer);
}
- }
- });
+ });
+ }
}
void onCanceled(final UrlResponseInfo info) {
@@ -827,7 +850,7 @@ final class JavaUrlRequest implements UrlRequest {
void onFailed(final UrlResponseInfo urlResponseInfo, final UrlRequestException e) {
closeResponseChannel();
- mUserExecutor.execute(new Runnable() {
+ Runnable runnable = new Runnable() {
@Override
public void run() {
try {
@@ -836,7 +859,14 @@ final class JavaUrlRequest implements UrlRequest {
Log.e(TAG, "Exception in onFailed method", exception);
}
}
- });
+ };
+ try {
+ mUserExecutor.execute(runnable);
+ } catch (InlineExecutionProhibitedException wasDirect) {
+ if (mFallbackExecutor != null) {
+ mFallbackExecutor.execute(runnable);
+ }
+ }
}
}
@@ -857,4 +887,54 @@ final class JavaUrlRequest implements UrlRequest {
}
});
}
+
+ /**
+ * Executor that detects and throws if its mDelegate runs a submitted runnable inline.
+ */
+ static final class DirectPreventingExecutor implements Executor {
+ private final Executor mDelegate;
+
+ DirectPreventingExecutor(Executor delegate) {
+ this.mDelegate = delegate;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ Thread currentThread = Thread.currentThread();
+ InlineCheckingRunnable runnable = new InlineCheckingRunnable(command, currentThread);
+ mDelegate.execute(runnable);
+ if (runnable.mExecutedInline != null) {
+ throw runnable.mExecutedInline;
+ } else {
+ // It's possible that this method is being called on an executor, and the runnable
+ // that
+ // was just queued will run on this thread after the current runnable returns. By
+ // nulling out the mCallingThread field, the InlineCheckingRunnable's current thread
+ // comparison will not fire.
+ runnable.mCallingThread = null;
+ }
+ }
+
+ private static final class InlineCheckingRunnable implements Runnable {
+ private final Runnable mCommand;
+ private Thread mCallingThread;
+ private InlineExecutionProhibitedException mExecutedInline = null;
+
+ private InlineCheckingRunnable(Runnable command, Thread callingThread) {
+ this.mCommand = command;
+ this.mCallingThread = callingThread;
+ }
+
+ @Override
+ public void run() {
+ if (Thread.currentThread() == mCallingThread) {
+ // Can't throw directly from here, since the delegate executor could catch this
+ // exception.
+ mExecutedInline = new InlineExecutionProhibitedException();
+ return;
+ }
+ mCommand.run();
+ }
+ }
+ }
}

Powered by Google App Engine
This is Rietveld 408576698