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

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

Issue 849903002: [Cronet] Upload support for async APIs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Move mDataProvider.read() outside of lock Created 5 years, 10 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.net;
6
7 import org.chromium.base.CalledByNative;
8 import org.chromium.base.JNINamespace;
9
10 import java.nio.ByteBuffer;
11 import java.util.concurrent.Executor;
12
13 /**
14 * Pass an upload body to a UrlRequest using an UploadDataProvider.
15 */
16 @JNINamespace("cronet")
17 public class CronetUploadDataStream implements UploadDataSink {
18 // These are never changed, once a request starts.
19 private final Executor mExecutor;
20 private final UploadDataProvider mDataProvider;
21 private final long mLength;
22 private CronetUrlRequest mRequest;
23
24 // Reusable read task, to reduce redundant memory allocation.
25 private final Runnable mReadTask = new Runnable() {
26 @Override
27 public void run() {
28 synchronized (mLock) {
29 if (mReading || mRewinding || mByteBuffer == null
30 || mUploadDataStreamDelegate == 0) {
31 throw new IllegalStateException(
32 "Unexpected readData call.");
33 }
34 mReading = true;
35 }
36
37 try {
38 mDataProvider.read(CronetUploadDataStream.this, mByteBuffer);
39 } catch (Exception exception) {
40 onError(exception);
41 }
42 }
43 };
44
45 // ByteBuffer created in the native code and is passed to
46 // UploadDataProvider for reading. It is only valid from the
47 // call to mDataProvider.read until onError or onReadSucceeded.
48 private ByteBuffer mByteBuffer = null;
49
50 // Lock that protects all subsequent variables. The delegate has to be
51 // protected to ensure safe shutdown, mReading and mRewinding are protected
52 // to robustly detect getting read/rewind results more often than expected.
53 private final Object mLock = new Object();
54
55 // Native adapter delegate object, owned by the CronetUploadDataStream.
56 // It's only deleted after the native UploadDataStream object is destroyed.
57 // All access to the delegate is synchronized, for safe usage and cleanup.
58 private long mUploadDataStreamDelegate;
59
60 private boolean mReading = false;
61 private boolean mRewinding = false;
62 private boolean mDestroyDelegatePostponed = false;
63
64 public CronetUploadDataStream(UploadDataProvider dataProvider,
mmenke 2015/02/10 19:37:09 Most of these methods should have a little documen
xunjieli 2015/02/10 20:28:30 Done.
65 Executor executor) {
66 mExecutor = executor;
67 mDataProvider = dataProvider;
68 mLength = mDataProvider.getLength();
69 }
70
71 @SuppressWarnings("unused")
72 @CalledByNative
73 void readData(ByteBuffer byteBuffer) {
74 mByteBuffer = byteBuffer;
75 mExecutor.execute(mReadTask);
76 }
77
78 // TODO(mmenke): Consider implementing a cancel method.
79 // currently wait for any pending read to complete.
80
81 @SuppressWarnings("unused")
82 @CalledByNative
83 void rewind() {
84 Runnable task = new Runnable() {
85 @Override
86 public void run() {
87 synchronized (mLock) {
88 if (mReading || mRewinding
89 || mUploadDataStreamDelegate == 0) {
90 throw new IllegalStateException(
91 "Unexpected rewind call.");
92 }
93 mRewinding = true;
94 }
95 try {
96 mDataProvider.rewind(CronetUploadDataStream.this);
97 } catch (Exception exception) {
98 onError(exception);
99 }
100 }
101 };
102
103 mExecutor.execute(task);
104 }
105
106 @SuppressWarnings("unused")
107 @CalledByNative
108 void onAdapterDestroyed() {
109 Runnable task = new Runnable() {
110 @Override
111 public void run() {
112 destroyDelegate();
113 }
114 };
115
116 mExecutor.execute(task);
117 }
118
119 private void onError(Exception exception) {
120 synchronized (mLock) {
121 if (!mReading && !mRewinding) {
122 throw new IllegalStateException(
123 "There is no read or rewind in progress.");
124 }
125 mReading = false;
126 mRewinding = false;
127 mByteBuffer = null;
128 destroyDelegateIfPostponed();
129 }
130
131 // Just fail the request - simpler to fail directly, and
132 // UploadDataStream only supports failing during initialization, not
133 // while reading. This should be safe, even if we deleted the adapter,
134 // because in that case, the request has already been cancelled.
135 mRequest.onUploadException(exception);
136 }
137
138 @Override
139 public void onReadSucceeded(boolean lastChunk) {
140 synchronized (mLock) {
141 if (!mReading) {
142 throw new IllegalStateException("Non-existent read succeeded.");
143 }
144 if (lastChunk && mLength >= 0) {
145 throw new IllegalArgumentException(
146 "Non-chunked upload can't have last chunk");
147 }
148 int bytesRead = mByteBuffer.position();
149
150 mByteBuffer = null;
151 mReading = false;
152
153 destroyDelegateIfPostponed();
154 // Request may been canceled already.
155 if (mUploadDataStreamDelegate == 0) {
156 return;
157 }
mmenke 2015/02/10 19:37:09 This can no longer happen. Throw if it does?
xunjieli 2015/02/10 20:28:30 Why this can't happen? destroyDelegateIfPostponed(
mmenke 2015/02/10 20:44:07 Oops - was thinking this was before the new destru
xunjieli 2015/02/10 21:47:08 Acknowledged, I also removed two unnecessary check
158 nativeOnReadSucceeded(mUploadDataStreamDelegate, bytesRead,
159 lastChunk);
160 }
161 }
162
163 @Override
164 public void onReadError(Exception exception) {
165 synchronized (mLock) {
166 if (!mReading) {
167 throw new IllegalStateException("Non-existent read failed.");
168 }
169 // Request may been canceled already.
170 if (mUploadDataStreamDelegate == 0) {
mmenke 2015/02/10 19:37:09 This can no longer happen. Throw if it does?
xunjieli 2015/02/10 20:28:30 Done.
171 return;
172 }
173 onError(exception);
174 }
175 }
176
177 @Override
178 public void onRewindSucceeded() {
179 synchronized (mLock) {
180 if (!mRewinding) {
181 throw new IllegalStateException(
182 "Non-existent rewind succeeded.");
183 }
184 mRewinding = false;
185 // Request may been canceled already.
186 if (mUploadDataStreamDelegate == 0) {
187 return;
188 }
189 nativeOnRewindSucceeded(mUploadDataStreamDelegate);
190 }
191 }
192
193 @Override
194 public void onRewindError(Exception exception) {
195 synchronized (mLock) {
196 if (!mRewinding) {
197 throw new IllegalStateException("Non-existent rewind failed.");
198 }
199 // Request may been canceled already.
200 if (mUploadDataStreamDelegate == 0) {
201 return;
202 }
203 onError(exception);
204 }
205 }
206
207 /**
208 * The delegate is owned by the CronetUploadDataStream, so it can be
209 * destroyed safely when there is no pending read; however, destruction is
210 * initiated by the destruction of the native UploadDataStream.
211 */
212 private void destroyDelegate() {
213 synchronized (mLock) {
214 if (mReading) {
215 // Wait for the read to complete before destroy the delegate.
216 mDestroyDelegatePostponed = true;
217 return;
218 }
219 if (mUploadDataStreamDelegate == 0) {
220 return;
221 }
222 nativeDestroyDelegate(mUploadDataStreamDelegate);
223 mUploadDataStreamDelegate = 0;
224 }
225 }
226
227 /**
228 * Destroy the native delegate if the destruction is postponed due to a
229 * pending read, which has since completed. Caller needs to be on executor
230 * thread and acquire mLock.
231 */
232 private void destroyDelegateIfPostponed() {
233 if (mReading) {
mmenke 2015/02/10 19:37:09 Should put this in a synchronized block, I believe
xunjieli 2015/02/10 20:28:30 Done. The caller aquired mLock. I'll use nested ac
234 throw new IllegalStateException(
235 "Method should not be called when read has not completed.");
236 }
237 if (mDestroyDelegatePostponed) {
238 destroyDelegate();
239 }
240 }
241
242 /**
243 * Creates native objects and attaches them to the underlying request
244 * adapter object.
245 * TODO(mmenke): If more types of native upload streams are needed, create
246 * an interface with just this method, to minimize CronetURLRequest's
247 * dependencies on each upload stream type.
248 */
249 void attachToRequest(CronetUrlRequest request, long requestAdapter) {
250 if (mLength < 0) {
251 // TODO(mmenke): Add tests and remove this line.
252 throw new IllegalArgumentException(
253 "Chunked uploads not supported.");
254 }
255 mRequest = request;
256 mUploadDataStreamDelegate =
257 nativeAttachUploadDataToRequest(requestAdapter, mLength);
258 }
259
260 /**
261 * Creates a native UploadDataStreamDelegate and UploadDataStreamAdapter
262 * for testing.
263 * @return the address of the native CronetUploadDataStreamAdapter object
264 */
265 public long createAdapterForTesting() {
266 mUploadDataStreamDelegate = nativeCreateDelegateForTesting();
267 return nativeCreateAdapterForTesting(mLength, mUploadDataStreamDelegate) ;
268 }
269
270 // Native methods are implemented in upload_data_stream_adapter.cc.
271
272 private native long nativeAttachUploadDataToRequest(long urlRequestAdapter,
273 long length);
274
275 private native long nativeCreateDelegateForTesting();
276
277 private native long nativeCreateAdapterForTesting(long length,
278 long delegate);
279
280 private native void nativeOnReadSucceeded(long uploadDataStreamDelegate,
281 int bytesRead, boolean finalChunk);
282
283 private native void nativeOnRewindSucceeded(long uploadDataStreamDelegate);
284
285 private static native void nativeDestroyDelegate(
286 long uploadDataStreamDelegate);
287 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698