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

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: Addressed Misha's comments 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 {
pauljensen 2015/02/12 17:15:40 Why is this public? Should it be final like Cronet
xunjieli 2015/02/12 20:56:27 I think it is public is because we used it in test
xunjieli 2015/02/13 21:58:34 Alright, my renaming CL is landed. I have rebased,
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 = 0;
59
60 private boolean mReading = false;
61 private boolean mRewinding = false;
62 private boolean mDestroyDelegatePostponed = false;
63
64 /**
65 * Constructs a CronetUploadDataStream.
66 * @param dataProvider the UploadDataProvider to read data from.
67 * @param executor the Executor to execute UploadDataProvider tasks.
68 */
69 public CronetUploadDataStream(UploadDataProvider dataProvider,
70 Executor executor) {
71 mExecutor = executor;
72 mDataProvider = dataProvider;
73 mLength = mDataProvider.getLength();
74 }
75
76 /**
77 * Called by native code to make the UploadDataProvider read data into
78 * {@code byteBuffer}.
79 */
80 @SuppressWarnings("unused")
81 @CalledByNative
82 void readData(ByteBuffer byteBuffer) {
83 mByteBuffer = byteBuffer;
84 mExecutor.execute(mReadTask);
85 }
86
87 // TODO(mmenke): Consider implementing a cancel method.
88 // currently wait for any pending read to complete.
89
90 /**
91 * Called by native code to make the UploadDataProvider rewind upload data.
92 */
93 @SuppressWarnings("unused")
94 @CalledByNative
95 void rewind() {
96 Runnable task = new Runnable() {
97 @Override
98 public void run() {
99 synchronized (mLock) {
100 if (mReading || mRewinding
101 || mUploadDataStreamDelegate == 0) {
102 throw new IllegalStateException(
103 "Unexpected rewind call.");
104 }
105 mRewinding = true;
106 }
pauljensen 2015/02/12 17:15:40 nit: you have a new line on line 36 but not here.
xunjieli 2015/02/12 20:56:27 Done.
107 try {
108 mDataProvider.rewind(CronetUploadDataStream.this);
109 } catch (Exception exception) {
110 onError(exception);
111 }
112 }
113 };
114 mExecutor.execute(task);
115 }
116
117 /**
118 * Called by native code to destroy the native adapter delegate, when the
119 * adapter is destroyed.
120 */
121 @SuppressWarnings("unused")
122 @CalledByNative
123 void onAdapterDestroyed() {
124 Runnable task = new Runnable() {
125 @Override
126 public void run() {
127 destroyDelegate();
128 }
129 };
130
131 mExecutor.execute(task);
132 }
133
134 /**
135 * Helper method called when an exception occurred. This method resets
136 * states and propagates the error to the request.
137 */
138 private void onError(Exception exception) {
139 synchronized (mLock) {
140 if (!mReading && !mRewinding) {
141 throw new IllegalStateException(
142 "There is no read or rewind in progress.");
143 }
144 mReading = false;
145 mRewinding = false;
146 mByteBuffer = null;
147 destroyDelegateIfPostponed();
148 }
149
150 // Just fail the request - simpler to fail directly, and
151 // UploadDataStream only supports failing during initialization, not
152 // while reading. This should be safe, even if we deleted the adapter,
153 // because in that case, the request has already been cancelled.
154 mRequest.onUploadException(exception);
155 }
156
157 @Override
158 public void onReadSucceeded(boolean lastChunk) {
159 synchronized (mLock) {
160 if (!mReading) {
161 throw new IllegalStateException("Non-existent read succeeded.");
162 }
163 if (lastChunk && mLength >= 0) {
164 throw new IllegalArgumentException(
165 "Non-chunked upload can't have last chunk");
166 }
167 int bytesRead = mByteBuffer.position();
168
169 mByteBuffer = null;
170 mReading = false;
171
172 destroyDelegateIfPostponed();
173 // Request may been canceled already.
174 if (mUploadDataStreamDelegate == 0) {
175 return;
176 }
177 nativeOnReadSucceeded(mUploadDataStreamDelegate, bytesRead,
178 lastChunk);
179 }
180 }
181
182 @Override
183 public void onReadError(Exception exception) {
184 synchronized (mLock) {
185 if (!mReading) {
186 throw new IllegalStateException("Non-existent read failed.");
187 }
188 onError(exception);
189 }
190 }
191
192 @Override
193 public void onRewindSucceeded() {
194 synchronized (mLock) {
195 if (!mRewinding) {
196 throw new IllegalStateException(
197 "Non-existent rewind succeeded.");
198 }
199 mRewinding = false;
200 // Request may been canceled already.
201 if (mUploadDataStreamDelegate == 0) {
202 return;
203 }
204 nativeOnRewindSucceeded(mUploadDataStreamDelegate);
205 }
206 }
207
208 @Override
209 public void onRewindError(Exception exception) {
210 synchronized (mLock) {
211 if (!mRewinding) {
212 throw new IllegalStateException("Non-existent rewind failed.");
213 }
214 onError(exception);
215 }
216 }
217
218 /**
219 * The delegate is owned by the CronetUploadDataStream, so it can be
220 * destroyed safely when there is no pending read; however, destruction is
221 * initiated by the destruction of the native UploadDataStream.
222 */
223 private void destroyDelegate() {
224 synchronized (mLock) {
225 if (mReading) {
226 // Wait for the read to complete before destroy the delegate.
227 mDestroyDelegatePostponed = true;
228 return;
229 }
230 if (mUploadDataStreamDelegate == 0) {
231 return;
232 }
233 nativeDestroyDelegate(mUploadDataStreamDelegate);
234 mUploadDataStreamDelegate = 0;
235 }
236 }
237
238 /**
239 * Destroy the native delegate if the destruction is postponed due to a
240 * pending read, which has since completed. Caller needs to be on executor
241 * thread.
242 */
243 private void destroyDelegateIfPostponed() {
244 synchronized (mLock) {
245 if (mReading) {
246 throw new IllegalStateException(
247 "Method should not be called when read has not completed .");
248 }
249 if (mDestroyDelegatePostponed) {
250 destroyDelegate();
251 }
252 }
253 }
254
255 /**
256 * Creates native objects and attaches them to the underlying request
257 * adapter object.
258 * TODO(mmenke): If more types of native upload streams are needed, create
259 * an interface with just this method, to minimize CronetURLRequest's
260 * dependencies on each upload stream type.
261 */
262 void attachToRequest(CronetUrlRequest request, long requestAdapter) {
263 if (mLength < 0) {
264 // TODO(mmenke): Add tests and remove this line.
265 throw new IllegalArgumentException(
266 "Chunked uploads not supported.");
267 }
268 mRequest = request;
269 mUploadDataStreamDelegate =
270 nativeAttachUploadDataToRequest(requestAdapter, mLength);
271 }
272
273 /**
274 * Creates a native UploadDataStreamDelegate and UploadDataStreamAdapter
275 * for testing.
276 * @return the address of the native CronetUploadDataStreamAdapter object
277 */
278 public long createAdapterForTesting() {
279 mUploadDataStreamDelegate = nativeCreateDelegateForTesting();
280 return nativeCreateAdapterForTesting(mLength, mUploadDataStreamDelegate) ;
281 }
282
283 // Native methods are implemented in upload_data_stream_adapter.cc.
284
285 private native long nativeAttachUploadDataToRequest(long urlRequestAdapter,
286 long length);
287
288 private native long nativeCreateDelegateForTesting();
289
290 private native long nativeCreateAdapterForTesting(long length,
291 long delegate);
292
293 private native void nativeOnReadSucceeded(long uploadDataStreamDelegate,
294 int bytesRead, boolean finalChunk);
295
296 private native void nativeOnRewindSucceeded(long uploadDataStreamDelegate);
297
298 private static native void nativeDestroyDelegate(
299 long uploadDataStreamDelegate);
300 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698