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

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 delegate destructor to implementation file to avoid chromium style error 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 import org.chromium.base.NativeClassQualifiedName;
10
11 import java.nio.ByteBuffer;
12 import java.util.concurrent.Executor;
13
14 /**
15 * Pass an upload body to a UrlRequest using an UploadDataProvider.
16 */
17 @JNINamespace("cronet")
18 final class CronetUploadDataStream implements UploadDataSink {
19 // These are never changed, once a request starts.
20 private final Executor mExecutor;
21 private final UploadDataProvider mDataProvider;
22 private final long mLength;
23 private CronetUrlRequest mRequest;
24
25 // Reusable read task, to reduce redundant memory allocation.
26 private final Runnable mReadTask = new Runnable() {
27 @Override
28 public void run() {
29 synchronized (mLock) {
30 if (mReading || mRewinding || mByteBuffer == null
31 || mUploadDataStreamDelegate == 0) {
32 throw new IllegalStateException(
33 "Unexpected readData call.");
34 }
35 mReading = true;
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 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 if (mLength < 0) {
75 // TODO(mmenke): Add tests and remove this line.
76 throw new IllegalArgumentException(
77 "Chunked uploads not supported.");
78 }
79 }
80
81 /**
82 * Called by native code to make the UploadDataProvider read data into
83 * {@code byteBuffer}.
84 */
85 @SuppressWarnings("unused")
86 @CalledByNative
87 void readData(ByteBuffer byteBuffer) {
88 mByteBuffer = byteBuffer;
89 mExecutor.execute(mReadTask);
90 }
91
92 // TODO(mmenke): Consider implementing a cancel method.
93 // currently wait for any pending read to complete.
94
95 /**
96 * Called by native code to make the UploadDataProvider rewind upload data.
97 */
98 @SuppressWarnings("unused")
99 @CalledByNative
100 void rewind() {
101 Runnable task = new Runnable() {
102 @Override
103 public void run() {
104 synchronized (mLock) {
105 if (mReading || mRewinding
106 || mUploadDataStreamDelegate == 0) {
107 throw new IllegalStateException(
108 "Unexpected rewind call.");
109 }
110 mRewinding = true;
111 }
112 try {
113 mDataProvider.rewind(CronetUploadDataStream.this);
114 } catch (Exception exception) {
115 onError(exception);
116 }
117 }
118 };
119 mExecutor.execute(task);
120 }
121
122 /**
123 * Called by native code to destroy the native adapter delegate, when the
124 * adapter is destroyed.
125 */
126 @SuppressWarnings("unused")
127 @CalledByNative
128 void onAdapterDestroyed() {
129 Runnable task = new Runnable() {
130 @Override
131 public void run() {
132 destroyDelegate();
133 }
134 };
135
136 mExecutor.execute(task);
137 }
138
139 /**
140 * Helper method called when an exception occurred. This method resets
141 * states and propagates the error to the request.
142 */
143 private void onError(Exception exception) {
144 synchronized (mLock) {
145 if (!mReading && !mRewinding) {
146 throw new IllegalStateException(
147 "There is no read or rewind in progress.");
148 }
149 mReading = false;
150 mRewinding = false;
151 mByteBuffer = null;
152 destroyDelegateIfPostponed();
153 }
154
155 // Just fail the request - simpler to fail directly, and
156 // UploadDataStream only supports failing during initialization, not
157 // while reading. This should be safe, even if we deleted the adapter,
158 // because in that case, the request has already been cancelled.
159 mRequest.onUploadException(exception);
160 }
161
162 @Override
163 public void onReadSucceeded(boolean lastChunk) {
164 synchronized (mLock) {
165 if (!mReading) {
166 throw new IllegalStateException("Non-existent read succeeded.");
167 }
168 if (lastChunk && mLength >= 0) {
169 throw new IllegalArgumentException(
170 "Non-chunked upload can't have last chunk");
171 }
172 int bytesRead = mByteBuffer.position();
173
174 mByteBuffer = null;
175 mReading = false;
176
177 destroyDelegateIfPostponed();
178 // Request may been canceled already.
179 if (mUploadDataStreamDelegate == 0) {
180 return;
181 }
182 nativeOnReadSucceeded(mUploadDataStreamDelegate, bytesRead,
183 lastChunk);
184 }
185 }
186
187 @Override
188 public void onReadError(Exception exception) {
189 synchronized (mLock) {
190 if (!mReading) {
191 throw new IllegalStateException("Non-existent read failed.");
192 }
193 onError(exception);
194 }
195 }
196
197 @Override
198 public void onRewindSucceeded() {
199 synchronized (mLock) {
200 if (!mRewinding) {
201 throw new IllegalStateException(
202 "Non-existent rewind succeeded.");
203 }
204 mRewinding = false;
205 // Request may been canceled already.
206 if (mUploadDataStreamDelegate == 0) {
207 return;
208 }
209 nativeOnRewindSucceeded(mUploadDataStreamDelegate);
210 }
211 }
212
213 @Override
214 public void onRewindError(Exception exception) {
215 synchronized (mLock) {
216 if (!mRewinding) {
217 throw new IllegalStateException("Non-existent rewind failed.");
218 }
219 onError(exception);
220 }
221 }
222
223 /**
224 * The delegate is owned by the CronetUploadDataStream, so it can be
225 * destroyed safely when there is no pending read; however, destruction is
226 * initiated by the destruction of the native UploadDataStream.
227 */
228 private void destroyDelegate() {
229 synchronized (mLock) {
230 if (mReading) {
231 // Wait for the read to complete before destroy the delegate.
232 mDestroyDelegatePostponed = true;
233 return;
234 }
235 if (mUploadDataStreamDelegate == 0) {
236 return;
237 }
238 nativeDestroyDelegate(mUploadDataStreamDelegate);
239 mUploadDataStreamDelegate = 0;
240 }
241 }
242
243 /**
244 * Destroy the native delegate if the destruction is postponed due to a
245 * pending read, which has since completed. Caller needs to be on executor
246 * thread.
247 */
248 private void destroyDelegateIfPostponed() {
249 synchronized (mLock) {
250 if (mReading) {
251 throw new IllegalStateException(
252 "Method should not be called when read has not completed .");
253 }
254 if (mDestroyDelegatePostponed) {
255 destroyDelegate();
256 }
257 }
258 }
259
260 /**
261 * Creates native objects and attaches them to the underlying request
262 * adapter object.
263 * TODO(mmenke): If more types of native upload streams are needed, create
264 * an interface with just this method, to minimize CronetURLRequest's
265 * dependencies on each upload stream type.
266 */
267 void attachToRequest(CronetUrlRequest request, long requestAdapter) {
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 @NativeClassQualifiedName("CronetUploadDataStreamDelegate")
294 private native void nativeOnReadSucceeded(long nativePtr,
295 int bytesRead, boolean finalChunk);
296
297 @NativeClassQualifiedName("CronetUploadDataStreamDelegate")
298 private native void nativeOnRewindSucceeded(long nativePtr);
299
300 private static native void nativeDestroyDelegate(
301 long uploadDataStreamDelegate);
302 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698