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

Unified Diff: android_webview/native/android_stream_reader_url_request_job.cc

Issue 11363123: [android_webview] Don't block the IO thread when reading from an InputStream. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix clang error Created 8 years, 1 month 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: android_webview/native/android_stream_reader_url_request_job.cc
diff --git a/android_webview/native/android_stream_reader_url_request_job.cc b/android_webview/native/android_stream_reader_url_request_job.cc
index a536ccd37478d7f993b99ca516d96cd1167fbfdf..96490773ff44fcbad6fdaa25a89065f888a77615 100644
--- a/android_webview/native/android_stream_reader_url_request_job.cc
+++ b/android_webview/native/android_stream_reader_url_request_job.cc
@@ -4,45 +4,41 @@
#include "android_webview/native/android_stream_reader_url_request_job.h"
+#include "android_webview/native/input_stream.h"
+#include "android_webview/native/input_stream_reader.h"
joth 2012/11/07 16:55:38 hmm interesting... if input_stream_reader could be
mkosiba (inactive) 2012/11/15 19:18:50 yup, it's just me being lazy. I'll do the move.
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
#include "base/message_loop.h"
+#include "base/threading/thread.h"
+#include "content/public/browser/browser_thread.h"
#include "net/base/io_buffer.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_error_job.h"
-#include "net/url_request/url_request_file_job.h"
#include "net/url_request/url_request_job_manager.h"
-// Disable "Warnings treated as errors" for input_stream_jni as it's a Java
-// system class and we have to generate C++ hooks for all methods in the class
-// even if they're unused.
-#pragma GCC diagnostic ignored "-Wunused-function"
-#include "jni/InputStream_jni.h"
+using android_webview::InputStream;
+using android_webview::InputStreamReader;
using base::android::AttachCurrentThread;
-using base::android::ClearException;
-using base::android::ConvertUTF8ToJavaString;
-using base::android::ScopedJavaGlobalRef;
-using base::android::ScopedJavaLocalRef;
-using JNI_InputStream::Java_InputStream_available;
-using JNI_InputStream::Java_InputStream_skip;
-using JNI_InputStream::Java_InputStream_readI_AB_I_I;
-
+using content::BrowserThread;
joth 2012/11/07 16:55:38 would be helpful to have a block comment explainin
mkosiba (inactive) 2012/11/15 19:18:50 simplified this significantly - you still think on
namespace {
-// Maximum number of bytes to be read in a single read.
-const int kBufferSize = 4096;
+class WorkerThread : public base::Thread {
+ public:
+ WorkerThread() : base::Thread("AndroidStreamReaderWorkerThread") {
joth 2012/11/07 16:55:38 nit: Worker seems spurious: AndroidStreamReaderThr
mkosiba (inactive) 2012/11/15 19:18:50 Done.
+ }
+};
-} // namespace
+static base::LazyInstance<WorkerThread> g_worker_thread =
+ LAZY_INSTANCE_INITIALIZER;
joth 2012/11/07 16:55:38 I'm not thrilled with another thread hanging out a
mkosiba (inactive) 2012/11/15 19:18:50 I didn't know about the PostBlockingPoolTask metho
-bool RegisterAndroidStreamReaderUrlRequestJob(JNIEnv* env) {
- return JNI_InputStream::RegisterNativesImpl(env);
-}
+} // namespace
AndroidStreamReaderURLRequestJob::AndroidStreamReaderURLRequestJob(
net::URLRequest* request,
@@ -50,151 +46,126 @@ AndroidStreamReaderURLRequestJob::AndroidStreamReaderURLRequestJob(
scoped_ptr<Delegate> delegate)
: URLRequestJob(request, network_delegate),
delegate_(delegate.Pass()),
+ input_stream_reader_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
DCHECK(delegate_.get());
}
AndroidStreamReaderURLRequestJob::~AndroidStreamReaderURLRequestJob() {
+ if (input_stream_reader_ != NULL) {
+ PostToWorkerThread(
+ FROM_HERE,
+ base::Bind(&base::DeletePointer<InputStreamReader>,
+ input_stream_reader_));
+ }
}
void AndroidStreamReaderURLRequestJob::Start() {
// Start reading asynchronously so that all error reporting and data
// callbacks happen as they would for network requests.
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&AndroidStreamReaderURLRequestJob::StartAsync,
- weak_factory_.GetWeakPtr()));
+ base::Bind(
+ &AndroidStreamReaderURLRequestJob::StartAsync,
+ weak_factory_.GetWeakPtr()));
+}
+
+void AndroidStreamReaderURLRequestJob::Kill() {
+ weak_factory_.InvalidateWeakPtrs();
+ URLRequestJob::Kill();
+}
+
+InputStreamReader* AndroidStreamReaderURLRequestJob::CreateStreamReader(
+ InputStream* stream) {
+ return new InputStreamReader(stream);
}
void AndroidStreamReaderURLRequestJob::StartAsync() {
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
- stream_.Reset(env, delegate_->OpenInputStream(env, request()).obj());
- if (!stream_.obj()) {
- NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_FAILED));
+ // TODO: This could be done in the InputStreamReader but would force more
joth 2012/11/07 16:55:38 is this really a TODO then? reads like a conscious
mkosiba (inactive) 2012/11/15 19:18:50 ok, removed TODO.
+ // complex synchronization in the delegate.
+ stream_ = delegate_->OpenInputStream(env, request());
+ if (!stream_) {
+ NotifyDone(
+ net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED));
return;
}
- if (VerifyRequestedRange(env) && SkipToRequestedRange(env))
- NotifyHeadersComplete();
+ DCHECK(!input_stream_reader_);
+ input_stream_reader_ = CreateStreamReader(stream_.get());
+ CHECK(input_stream_reader_);
joth 2012/11/07 16:55:38 nit dcheck
mkosiba (inactive) 2012/11/15 19:18:50 I wanted a CHECK since we'll end up crashing anywa
+
+ PostToWorkerThread(
+ FROM_HERE,
+ base::Bind(&InputStreamReader::Seek,
+ base::Unretained(input_stream_reader_),
+ byte_range_,
+ base::Bind(
+ &AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted,
+ weak_factory_.GetWeakPtr())));
}
-bool AndroidStreamReaderURLRequestJob::VerifyRequestedRange(JNIEnv* env) {
- int32_t size = Java_InputStream_available(env, stream_.obj());
- if (ClearException(env)) {
+void AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted(
+ int result) {
+ // Clear the IO_PENDING status
+ SetStatus(net::URLRequestStatus());
+ if (result >= 0) {
+ set_expected_content_size(result);
+ NotifyHeadersComplete();
+ } else {
+ // The InputStreamReader deletes itself on error.
+ input_stream_reader_ = NULL;
+
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_FAILED));
- return false;
+ result));
}
+}
- if (size <= 0)
- return true;
-
- // Check that the requested range was valid.
- if (!byte_range_.ComputeBounds(size)) {
+void AndroidStreamReaderURLRequestJob::OnReaderReadCompleted(
benm (inactive) 2012/11/15 17:39:01 Please add a comment to explain the differences be
+ int result) {
+ if (result > 0) {
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
+ } else if (result == 0) {
+ NotifyDone(net::URLRequestStatus());
+ } else {
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
- return false;
+ result));
}
- size = byte_range_.last_byte_position() -
- byte_range_.first_byte_position() + 1;
- DCHECK_GE(size, 0);
- set_expected_content_size(size);
-
- return true;
+ NotifyReadComplete(result);
}
-bool AndroidStreamReaderURLRequestJob::SkipToRequestedRange(JNIEnv* env) {
- // Skip to the start of the requested data. This has to be done in a loop
- // because the underlying InputStream is not guaranteed to skip the requested
- // number of bytes.
- if (byte_range_.IsValid() && byte_range_.first_byte_position() != 0) {
- int64_t skipped, bytes_to_skip = byte_range_.first_byte_position();
- do {
- skipped = Java_InputStream_skip(env, stream_.obj(), bytes_to_skip);
- if (ClearException(env)) {
- NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_FAILED));
- return false;
- }
- if (skipped <= 0) {
- NotifyDone(
- net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
- return false;
- }
- } while ((bytes_to_skip -= skipped) > 0);
+void AndroidStreamReaderURLRequestJob::PostToWorkerThread(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ if(!g_worker_thread.Get().message_loop()) {
+ bool thread_created = g_worker_thread.Get().StartWithOptions(
+ base::Thread::Options(MessageLoop::TYPE_IO, 0));
+ CHECK(thread_created);
benm (inactive) 2012/11/15 17:39:01 DCHECK?
mkosiba (inactive) 2012/11/15 19:18:50 removed the code.
}
- return true;
+ g_worker_thread.Get().message_loop()->PostTask(from_here, task);
}
bool AndroidStreamReaderURLRequestJob::ReadRawData(net::IOBuffer* dest,
int dest_size,
int *bytes_read) {
- DCHECK_NE(dest_size, 0);
- DCHECK(bytes_read);
- DCHECK(stream_.obj());
-
- JNIEnv* env = AttachCurrentThread();
- DCHECK(env);
-
- if (!buffer_.obj()) {
- // Allocate transfer buffer.
- buffer_.Reset(env, env->NewByteArray(kBufferSize));
- if (ClearException(env)) {
- NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_FAILED));
- return false;
- }
- }
-
- jbyteArray buffer = buffer_.obj();
- *bytes_read = 0;
- if (!dest_size)
- return true;
-
- // Read data in multiples of the buffer size.
- while (dest_size > 0) {
- int read_size = std::min(dest_size, kBufferSize);
- // TODO(skyostil): Make this non-blocking
- int32_t byte_count =
- Java_InputStream_readI_AB_I_I(env, stream_.obj(), buffer, 0, read_size);
- if (byte_count <= 0) {
- // net::URLRequestJob will call NotifyDone for us after the end of the
- // file is reached.
- break;
- }
+ DCHECK(input_stream_reader_);
- if (ClearException(env)) {
- NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_FAILED));
- return false;
- }
-
-#ifndef NDEBUG
- int32_t buffer_length = env->GetArrayLength(buffer);
- DCHECK_GE(read_size, byte_count);
- DCHECK_GE(buffer_length, byte_count);
-#endif // NDEBUG
-
- // Copy the data over to the provided C++ side buffer.
- DCHECK_GE(dest_size, byte_count);
- env->GetByteArrayRegion(buffer, 0, byte_count,
- reinterpret_cast<jbyte*>(dest->data() + *bytes_read));
-
- if (ClearException(env)) {
- NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_FAILED));
- return false;
- }
-
- *bytes_read += byte_count;
- dest_size -= byte_count;
- }
- return true;
+ PostToWorkerThread(
+ FROM_HERE,
+ base::Bind(&InputStreamReader::ReadRawData,
+ base::Unretained(input_stream_reader_),
+ base::Unretained(dest),
+ dest_size,
+ base::Bind(
+ &AndroidStreamReaderURLRequestJob::OnReaderReadCompleted,
+ weak_factory_.GetWeakPtr())));
+
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+ return false;
}
bool AndroidStreamReaderURLRequestJob::GetMimeType(
@@ -202,9 +173,12 @@ bool AndroidStreamReaderURLRequestJob::GetMimeType(
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
+ if (!stream_)
+ return false;
+
return delegate_->GetMimeType(env,
request(),
- stream_.obj(),
+ *stream_,
mime_type);
}
@@ -213,9 +187,12 @@ bool AndroidStreamReaderURLRequestJob::GetCharset(
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
+ if (!stream_)
+ return false;
+
return delegate_->GetCharset(env,
request(),
- stream_.obj(),
+ *stream_,
charset);
}

Powered by Google App Engine
This is Rietveld 408576698