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

Side by Side Diff: components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java

Issue 2613393002: [Cronet] In JavaUrlRequest avoid thread-unsafe access to HttpURLConnection (Closed)
Patch Set: ignore RejectedExecutionException Created 3 years, 11 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 unified diff | Download patch
« no previous file with comments | « components/cronet/android/BUILD.gn ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.net.impl; 5 package org.chromium.net.impl;
6 6
7 import android.annotation.SuppressLint; 7 import android.annotation.SuppressLint;
8 import android.annotation.TargetApi; 8 import android.annotation.TargetApi;
9 import android.net.TrafficStats; 9 import android.net.TrafficStats;
10 import android.os.Build; 10 import android.os.Build;
(...skipping 10 matching lines...) Expand all
21 import java.io.IOException; 21 import java.io.IOException;
22 import java.io.OutputStream; 22 import java.io.OutputStream;
23 import java.net.HttpURLConnection; 23 import java.net.HttpURLConnection;
24 import java.net.URI; 24 import java.net.URI;
25 import java.net.URL; 25 import java.net.URL;
26 import java.nio.ByteBuffer; 26 import java.nio.ByteBuffer;
27 import java.nio.channels.Channels; 27 import java.nio.channels.Channels;
28 import java.nio.channels.ReadableByteChannel; 28 import java.nio.channels.ReadableByteChannel;
29 import java.nio.channels.WritableByteChannel; 29 import java.nio.channels.WritableByteChannel;
30 import java.util.AbstractMap.SimpleEntry; 30 import java.util.AbstractMap.SimpleEntry;
31 import java.util.ArrayDeque;
31 import java.util.ArrayList; 32 import java.util.ArrayList;
32 import java.util.Collections; 33 import java.util.Collections;
33 import java.util.List; 34 import java.util.List;
34 import java.util.Map; 35 import java.util.Map;
35 import java.util.TreeMap; 36 import java.util.TreeMap;
36 import java.util.concurrent.Executor; 37 import java.util.concurrent.Executor;
37 import java.util.concurrent.RejectedExecutionException; 38 import java.util.concurrent.RejectedExecutionException;
38 import java.util.concurrent.atomic.AtomicBoolean; 39 import java.util.concurrent.atomic.AtomicBoolean;
39 import java.util.concurrent.atomic.AtomicReference; 40 import java.util.concurrent.atomic.AtomicReference;
40 41
42 import javax.annotation.concurrent.GuardedBy;
43
41 /** 44 /**
42 * Pure java UrlRequest, backed by {@link HttpURLConnection}. 45 * Pure java UrlRequest, backed by {@link HttpURLConnection}.
43 */ 46 */
44 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) // TrafficStats only availabl e on ICS 47 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) // TrafficStats only availabl e on ICS
45 final class JavaUrlRequest extends UrlRequestBase { 48 final class JavaUrlRequest extends UrlRequestBase {
46 private static final String X_ANDROID = "X-Android"; 49 private static final String X_ANDROID = "X-Android";
47 private static final String X_ANDROID_SELECTED_TRANSPORT = "X-Android-Select ed-Transport"; 50 private static final String X_ANDROID_SELECTED_TRANSPORT = "X-Android-Select ed-Transport";
48 private static final String TAG = "JavaUrlConnection"; 51 private static final String TAG = "JavaUrlConnection";
49 private static final int DEFAULT_UPLOAD_BUFFER_SIZE = 8192; 52 private static final int DEFAULT_UPLOAD_BUFFER_SIZE = 8192;
50 private static final int DEFAULT_CHUNK_LENGTH = DEFAULT_UPLOAD_BUFFER_SIZE; 53 private static final int DEFAULT_CHUNK_LENGTH = DEFAULT_UPLOAD_BUFFER_SIZE;
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 STARTED, 122 STARTED,
120 REDIRECT_RECEIVED, 123 REDIRECT_RECEIVED,
121 AWAITING_FOLLOW_REDIRECT, 124 AWAITING_FOLLOW_REDIRECT,
122 AWAITING_READ, 125 AWAITING_READ,
123 READING, 126 READING,
124 ERROR, 127 ERROR,
125 COMPLETE, 128 COMPLETE,
126 CANCELLED, 129 CANCELLED,
127 } 130 }
128 131
132 // Executor that runs one task at a time on an underlying Executor.
133 private final class SeriaizingExecutor implements Executor {
134 private final Executor mUnderlyingExecutor;
135 // Queue of tasks to run. First element is the task currently executing .
136 // Task processing loop is running if queue is not empty. Synchronized on itself.
137 @GuardedBy("mTaskQueue")
138 private final ArrayDeque<Runnable> mTaskQueue = new ArrayDeque<>();
139
140 SeriaizingExecutor(Executor underlyingExecutor) {
141 mUnderlyingExecutor = underlyingExecutor;
142 }
143
144 private void runTask(final Runnable task) {
145 try {
146 mUnderlyingExecutor.execute(new Runnable() {
147 @Override
148 public void run() {
149 try {
150 task.run();
151 } finally {
152 Runnable nextTask;
153 synchronized (mTaskQueue) {
154 mTaskQueue.removeFirst();
155 nextTask = mTaskQueue.peekFirst();
156 }
157 if (nextTask != null) {
158 runTask(nextTask);
159 }
160 }
161 }
162 });
163 } catch (RejectedExecutionException e) {
164 // This can happen if JavaCronetEngine.shutdown() was called.
165 // Ignore and cease processing this request.
166 }
167 }
168
169 @Override
170 public void execute(Runnable command) {
171 synchronized (mTaskQueue) {
172 mTaskQueue.addLast(command);
173 // Task processing loop is already running if there are other it ems in mTaskQueue.
174 if (mTaskQueue.size() > 1) {
175 return;
176 }
177 }
178 runTask(command);
179 };
180 }
181
129 /** 182 /**
130 * @param executor The executor used for reading and writing from sockets 183 * @param executor The executor used for reading and writing from sockets
131 * @param userExecutor The executor used to dispatch to {@code callback} 184 * @param userExecutor The executor used to dispatch to {@code callback}
132 */ 185 */
133 JavaUrlRequest(Callback callback, final Executor executor, Executor userExec utor, String url, 186 JavaUrlRequest(Callback callback, final Executor executor, Executor userExec utor, String url,
134 String userAgent, boolean allowDirectExecutor) { 187 String userAgent, boolean allowDirectExecutor) {
135 if (url == null) { 188 if (url == null) {
136 throw new NullPointerException("URL is required"); 189 throw new NullPointerException("URL is required");
137 } 190 }
138 if (callback == null) { 191 if (callback == null) {
139 throw new NullPointerException("Listener is required"); 192 throw new NullPointerException("Listener is required");
140 } 193 }
141 if (executor == null) { 194 if (executor == null) {
142 throw new NullPointerException("Executor is required"); 195 throw new NullPointerException("Executor is required");
143 } 196 }
144 if (userExecutor == null) { 197 if (userExecutor == null) {
145 throw new NullPointerException("userExecutor is required"); 198 throw new NullPointerException("userExecutor is required");
146 } 199 }
147 200
148 this.mAllowDirectExecutor = allowDirectExecutor; 201 this.mAllowDirectExecutor = allowDirectExecutor;
149 this.mCallbackAsync = new AsyncUrlRequestCallback(callback, userExecutor ); 202 this.mCallbackAsync = new AsyncUrlRequestCallback(callback, userExecutor );
150 this.mTrafficStatsTag = TrafficStats.getThreadStatsTag(); 203 this.mTrafficStatsTag = TrafficStats.getThreadStatsTag();
151 this.mExecutor = new Executor() { 204 this.mExecutor = new SeriaizingExecutor(new Executor() {
152 @Override 205 @Override
153 public void execute(final Runnable command) { 206 public void execute(final Runnable command) {
154 executor.execute(new Runnable() { 207 executor.execute(new Runnable() {
155 @Override 208 @Override
156 public void run() { 209 public void run() {
157 int oldTag = TrafficStats.getThreadStatsTag(); 210 int oldTag = TrafficStats.getThreadStatsTag();
158 TrafficStats.setThreadStatsTag(mTrafficStatsTag); 211 TrafficStats.setThreadStatsTag(mTrafficStatsTag);
159 try { 212 try {
160 command.run(); 213 command.run();
161 } finally { 214 } finally {
162 TrafficStats.setThreadStatsTag(oldTag); 215 TrafficStats.setThreadStatsTag(oldTag);
163 } 216 }
164 } 217 }
165 }); 218 });
166 } 219 }
167 }; 220 });
168 this.mCurrentUrl = url; 221 this.mCurrentUrl = url;
169 this.mUserAgent = userAgent; 222 this.mUserAgent = userAgent;
170 } 223 }
171 224
172 @Override 225 @Override
173 public void setHttpMethod(String method) { 226 public void setHttpMethod(String method) {
174 checkNotStarted(); 227 checkNotStarted();
175 if (method == null) { 228 if (method == null) {
176 throw new NullPointerException("Method is required."); 229 throw new NullPointerException("Method is required.");
177 } 230 }
(...skipping 794 matching lines...) Expand 10 before | Expand all | Expand 10 after
972 // Can't throw directly from here, since the delegate execut or could catch this 1025 // Can't throw directly from here, since the delegate execut or could catch this
973 // exception. 1026 // exception.
974 mExecutedInline = new InlineExecutionProhibitedException(); 1027 mExecutedInline = new InlineExecutionProhibitedException();
975 return; 1028 return;
976 } 1029 }
977 mCommand.run(); 1030 mCommand.run();
978 } 1031 }
979 } 1032 }
980 } 1033 }
981 } 1034 }
OLDNEW
« no previous file with comments | « components/cronet/android/BUILD.gn ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698