summaryrefslogtreecommitdiffstats
path: root/ios
diff options
context:
space:
mode:
authorstuartmorgan <stuartmorgan@chromium.org>2015-05-26 17:29:16 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-27 00:29:47 +0000
commitfe0d591aa498986a405b8ce1ec205a039bcb39a3 (patch)
tree69417b7e7997265882f0aed4495e5a4aba8cef7a /ios
parentb21df31570795292bbb644f3a8c43b14270435a4 (diff)
downloadchromium_src-fe0d591aa498986a405b8ce1ec205a039bcb39a3.zip
chromium_src-fe0d591aa498986a405b8ce1ec205a039bcb39a3.tar.gz
chromium_src-fe0d591aa498986a405b8ce1ec205a039bcb39a3.tar.bz2
Add an actual WebThread implementation
Currently, WebThread is implemented as a passthrough to BrowserThread; this is necessary for Chrome for iOS as it uses content startup code, which creates those threads. Non-legacy code should not require content threads though, so this introduces an alternate implementation (based very heavily on BrowserThread and BrowserProcessSubThread). Embedders can choose to link in either target; this allows Chrome to continue to use the content version (now renamed to web_thread_adapter.cc), while everything else can use actual web implementations of threads. While this makes the targets more complex for now, most of this is abstracted since the existing test-support target automatically uses the full thread implementation. BUG=None Review URL: https://codereview.chromium.org/1159463003 Cr-Commit-Position: refs/heads/master@{#331491}
Diffstat (limited to 'ios')
-rw-r--r--ios/web/DEPS4
-rw-r--r--ios/web/ios_web.gyp81
-rw-r--r--ios/web/public/test/test_web_thread.h9
-rw-r--r--ios/web/public/web_thread.h27
-rw-r--r--ios/web/public/web_thread_delegate.h39
-rw-r--r--ios/web/test/test_web_thread.cc28
-rw-r--r--ios/web/test/test_web_thread_adapter.cc65
-rw-r--r--ios/web/test/test_web_thread_bundle.cc140
-rw-r--r--ios/web/test/test_web_thread_bundle_adapter.cc56
-rw-r--r--ios/web/web_thread.cc83
-rw-r--r--ios/web/web_thread_adapter.cc179
-rw-r--r--ios/web/web_thread_adapter.h19
-rw-r--r--ios/web/web_thread_impl.cc507
-rw-r--r--ios/web/web_thread_impl.h77
14 files changed, 1154 insertions, 160 deletions
diff --git a/ios/web/DEPS b/ios/web/DEPS
index dd6fcbb..a956ff6 100644
--- a/ios/web/DEPS
+++ b/ios/web/DEPS
@@ -14,10 +14,10 @@ include_rules = [
specific_include_rules = {
# Allow browser_thread.h and test_browser_thread.h until a web/ replacement is
# built out. Note that headers in public/ are not subject to this exception.
- "^web_thread(\.cc|_impl\.(cc|h))$": [
+ "^web_thread(\.cc|(_impl|_adapter)\.(cc|h))$": [
"+content/public/browser/browser_thread.h",
],
- "^test_web_thread(_bundle)?\.cc$": [
+ "^test_web_thread(_bundle)?(_adapter)?\.cc$": [
"+content/public/test/test_browser_thread.h",
"+content/public/test/test_browser_thread_bundle.h",
],
diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp
index e4ef646..030d9ada 100644
--- a/ios/web/ios_web.gyp
+++ b/ios/web/ios_web.gyp
@@ -7,6 +7,10 @@
'chromium_code': 1,
},
'targets': [
+ # Note: any embedder using ios_web will for now need to include either
+ # ios_web_thread (any new embedder) or ios_web_content_thread_shim (Chrome).
+ # This will become unnecessary once Chrome switches to using ios_web_thread,
+ # at which point that will be folded into this target.
{
'target_name': 'ios_web',
'type': 'static_library',
@@ -159,6 +163,7 @@
'public/web_state/web_state_observer_bridge.h',
'public/web_state/web_state_user_data.h',
'public/web_thread.h',
+ 'public/web_thread_delegate.h',
'public/web_ui_ios_data_source.h',
'public/web_view_type.h',
'public/web_view_util.h',
@@ -235,9 +240,6 @@
'web_state/web_view_creation_utils.mm',
'web_state/wk_web_view_ssl_error_util.h',
'web_state/wk_web_view_ssl_error_util.mm',
- 'web_thread.cc',
- 'web_thread_impl.cc',
- 'web_thread_impl.h',
'web_view_util.mm',
'webui/crw_web_ui_manager.h',
'webui/crw_web_ui_manager.mm',
@@ -269,6 +271,42 @@
},
},
},
+ # Target that builds the actual WebThread implementation. This is a
+ # separate target since it can't yet be used by Chrome (see comment below).
+ {
+ 'target_name': 'ios_web_thread',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../net/net.gyp:net',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'web_thread_impl.h',
+ 'web_thread_impl.cc',
+ ],
+ },
+ # Target that builds the files that shim WebThread functions to their
+ # corresponding content equivalents. This is a separate target since it
+ # is needed by Chrome, which still uses content startup (which creates
+ # content threads), but isn't used by web_shell.
+ {
+ 'target_name': 'ios_web_content_thread_shim',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../content/content.gyp:content_browser',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'web_thread_adapter.h',
+ 'web_thread_adapter.cc',
+ ],
+ },
# Target shared by ios_web and CrNet.
{
'target_name': 'ios_web_core',
@@ -373,6 +411,41 @@
'target_name': 'test_support_ios_web',
'type': 'static_library',
'dependencies': [
+ 'ios_web_thread',
+ 'test_support_ios_web_without_threads',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'test/test_web_thread.cc',
+ 'test/test_web_thread_bundle.cc',
+ ],
+ },
+ {
+ 'target_name': 'test_support_ios_web_with_content_thread_shim',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'ios_web_content_thread_shim',
+ 'test_support_ios_web_without_threads',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'test/test_web_thread_adapter.cc',
+ 'test/test_web_thread_bundle_adapter.cc',
+ ],
+ },
+ # A test support target that does not include TestWebThread. This is
+ # separate because tests that rely on the the shim thread implementation
+ # can't use TestWebThread/TestWebThreadBundle.
+ # TODO(stuartmorgan): Fold this into test_support_ios_web once
+ # the WebThread-to-BrowserThread shim is gone.
+ {
+ 'target_name': 'test_support_ios_web_without_threads',
+ 'type': 'static_library',
+ 'dependencies': [
'ios_web',
'../../content/content_shell_and_tests.gyp:test_support_content',
'../../ios/testing/ios_testing.gyp:ocmock_support',
@@ -410,8 +483,6 @@
'public/test/web_test_util.h',
'test/crw_fake_web_controller_observer.h',
'test/crw_fake_web_controller_observer.mm',
- 'test/test_web_thread.cc',
- 'test/test_web_thread_bundle.cc',
'test/web_test.h',
'test/web_test.mm',
'test/web_test_suite.cc',
diff --git a/ios/web/public/test/test_web_thread.h b/ios/web/public/test/test_web_thread.h
index f603f17..4ad9eb7 100644
--- a/ios/web/public/test/test_web_thread.h
+++ b/ios/web/public/test/test_web_thread.h
@@ -17,11 +17,8 @@ namespace web {
class TestWebThreadImpl;
-// TODO(ios): once WebThread is no longer implemented in term of BrowserThread,
-// introduces TestWebThreadBundle and deprecate TestWebThread
-// http://crbug.com/272091
-//
-// A WebThread for unit tests; this lets unit tests in ios/chrome create
+// DEPRECATED: use TestWebThreadBundle instead.
+// A WebThread for unit tests; this lets unit tests outside of web create
// WebThread instances.
class TestWebThread {
public:
@@ -34,7 +31,7 @@ class TestWebThread {
// certain unit tests. To avoid a stronger dependency of the internals of
// WebThread, do no provide the full Thread interface.
- // Starts the thread with a generci message loop.
+ // Starts the thread with a generic message loop.
bool Start();
// Starts the thread with an IOThread message loop.
diff --git a/ios/web/public/web_thread.h b/ios/web/public/web_thread.h
index f820fb7..1f44d63 100644
--- a/ios/web/public/web_thread.h
+++ b/ios/web/public/web_thread.h
@@ -104,6 +104,14 @@ class WebThread {
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay);
+ static bool PostNonNestableTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task);
+ static bool PostNonNestableDelayedTask(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay);
static bool PostTaskAndReply(ID identifier,
const tracked_objects::Location& from_here,
@@ -153,6 +161,10 @@ class WebThread {
// runner.
static bool PostBlockingPoolTask(const tracked_objects::Location& from_here,
const base::Closure& task);
+ static bool PostBlockingPoolTaskAndReply(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const base::Closure& reply);
static bool PostBlockingPoolSequencedTask(
const std::string& sequence_token_name,
const tracked_objects::Location& from_here,
@@ -172,11 +184,24 @@ class WebThread {
// delete the pointer.
static base::MessageLoop* UnsafeGetMessageLoopForThread(ID identifier);
+ // Callable on any thread. Returns whether the given well-known thread is
+ // initialized.
+ static bool IsThreadInitialized(ID identifier) WARN_UNUSED_RESULT;
+
// Callable on any thread. Returns whether execution is currently on the
// given thread. To DCHECK this, use the DCHECK_CURRENTLY_ON_WEB_THREAD()
// macro above.
static bool CurrentlyOn(ID identifier) WARN_UNUSED_RESULT;
+ // Callable on any thread. Returns whether the threads message loop is valid.
+ // If this returns false it means the thread is in the process of shutting
+ // down.
+ static bool IsMessageLoopValid(ID identifier) WARN_UNUSED_RESULT;
+
+ // If the current message loop is one of the known threads, returns true and
+ // sets identifier to its ID.
+ static bool GetCurrentThreadIdentifier(ID* identifier) WARN_UNUSED_RESULT;
+
// Callers can hold on to a refcounted MessageLoopProxy beyond the lifetime
// of the thread.
static scoped_refptr<base::MessageLoopProxy> GetMessageLoopProxyForThread(
@@ -187,6 +212,8 @@ class WebThread {
static std::string GetDCheckCurrentlyOnErrorMessage(ID expected);
private:
+ friend class WebThreadImpl;
+
WebThread() {}
DISALLOW_COPY_AND_ASSIGN(WebThread);
};
diff --git a/ios/web/public/web_thread_delegate.h b/ios/web/public/web_thread_delegate.h
new file mode 100644
index 0000000..2810005
--- /dev/null
+++ b/ios/web/public/web_thread_delegate.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_PUBLIC_WEB_THREAD_DELEGATE_H_
+#define IOS_WEB_PUBLIC_WEB_THREAD_DELEGATE_H_
+
+namespace web {
+
+// A class with this type may be registered via WebThread::SetDelegate.
+// TODO(stuartmorgan): Currently the above is not actually true; because
+// web can currently be build with either its own thread implementation, or
+// the content thread implementation, WebThread doesn't have SetDelegate
+// (since it can't be easily be passed through to BrowserThread::SetDelegate).
+// Once BrowserThread isn't being used by Chrome, SetDelegate will become
+// public.
+//
+// If registered as such, it will schedule to run Init() before the
+// message loop begins and the schedule InitAsync() as the first
+// task on its message loop (after the WebThread has done its own
+// initialization), and receive a CleanUp call right after the message
+// loop ends (and before the WebThread has done its own clean-up).
+class WebThreadDelegate {
+ public:
+ virtual ~WebThreadDelegate() {}
+
+ // Called prior to starting the message loop
+ virtual void Init() = 0;
+
+ // Called as the first task on the thread's message loop.
+ virtual void InitAsync() = 0;
+
+ // Called just after the message loop ends.
+ virtual void CleanUp() = 0;
+};
+
+} // namespace web
+
+#endif // IOS_WEB_PUBLIC_WEB_THREAD_DELEGATE_H_
diff --git a/ios/web/test/test_web_thread.cc b/ios/web/test/test_web_thread.cc
index 6318e71..06322bd 100644
--- a/ios/web/test/test_web_thread.cc
+++ b/ios/web/test/test_web_thread.cc
@@ -4,34 +4,21 @@
#include "ios/web/public/test/test_web_thread.h"
-#include "content/public/test/test_browser_thread.h"
#include "ios/web/web_thread_impl.h"
namespace web {
-// TestWebThreadImpl delegates to content::TestBrowserThread until WebThread
-// implementation is indenpendent of content::BrowserThread.
-class TestWebThreadImpl {
+class TestWebThreadImpl : public WebThreadImpl {
public:
- TestWebThreadImpl(WebThread::ID identifier)
- : test_browser_thread_(BrowserThreadIDFromWebThreadID(identifier)) {}
+ TestWebThreadImpl(WebThread::ID identifier) : WebThreadImpl(identifier) {}
TestWebThreadImpl(WebThread::ID identifier, base::MessageLoop* message_loop)
- : test_browser_thread_(BrowserThreadIDFromWebThreadID(identifier),
- message_loop) {}
+ : WebThreadImpl(identifier, message_loop) {}
- ~TestWebThreadImpl() { Stop(); }
-
- bool Start() { return test_browser_thread_.Start(); }
-
- bool StartIOThread() { return test_browser_thread_.StartIOThread(); }
-
- void Stop() { test_browser_thread_.Stop(); }
-
- bool IsRunning() { return test_browser_thread_.IsRunning(); }
+ ~TestWebThreadImpl() override { Stop(); }
private:
- content::TestBrowserThread test_browser_thread_;
+ DISALLOW_COPY_AND_ASSIGN(TestWebThreadImpl);
};
TestWebThread::TestWebThread(WebThread::ID identifier)
@@ -44,6 +31,7 @@ TestWebThread::TestWebThread(WebThread::ID identifier,
}
TestWebThread::~TestWebThread() {
+ Stop();
}
bool TestWebThread::Start() {
@@ -51,7 +39,9 @@ bool TestWebThread::Start() {
}
bool TestWebThread::StartIOThread() {
- return impl_->StartIOThread();
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ return impl_->StartWithOptions(options);
}
void TestWebThread::Stop() {
diff --git a/ios/web/test/test_web_thread_adapter.cc b/ios/web/test/test_web_thread_adapter.cc
new file mode 100644
index 0000000..e715587
--- /dev/null
+++ b/ios/web/test/test_web_thread_adapter.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/public/test/test_web_thread.h"
+
+#include "content/public/test/test_browser_thread.h"
+#include "ios/web/web_thread_adapter.h"
+
+namespace web {
+
+// TestWebThreadImpl delegates to content::TestBrowserThread until WebThread
+// implementation is indenpendent of content::BrowserThread.
+class TestWebThreadImpl {
+ public:
+ TestWebThreadImpl(WebThread::ID identifier)
+ : test_browser_thread_(BrowserThreadIDFromWebThreadID(identifier)) {}
+
+ TestWebThreadImpl(WebThread::ID identifier, base::MessageLoop* message_loop)
+ : test_browser_thread_(BrowserThreadIDFromWebThreadID(identifier),
+ message_loop) {}
+
+ ~TestWebThreadImpl() { Stop(); }
+
+ bool Start() { return test_browser_thread_.Start(); }
+
+ bool StartIOThread() { return test_browser_thread_.StartIOThread(); }
+
+ void Stop() { test_browser_thread_.Stop(); }
+
+ bool IsRunning() { return test_browser_thread_.IsRunning(); }
+
+ private:
+ content::TestBrowserThread test_browser_thread_;
+};
+
+TestWebThread::TestWebThread(WebThread::ID identifier)
+ : impl_(new TestWebThreadImpl(identifier)) {
+}
+
+TestWebThread::TestWebThread(WebThread::ID identifier,
+ base::MessageLoop* message_loop)
+ : impl_(new TestWebThreadImpl(identifier, message_loop)) {
+}
+
+TestWebThread::~TestWebThread() {
+}
+
+bool TestWebThread::Start() {
+ return impl_->Start();
+}
+
+bool TestWebThread::StartIOThread() {
+ return impl_->StartIOThread();
+}
+
+void TestWebThread::Stop() {
+ impl_->Stop();
+}
+
+bool TestWebThread::IsRunning() {
+ return impl_->IsRunning();
+}
+
+} // namespace web
diff --git a/ios/web/test/test_web_thread_bundle.cc b/ios/web/test/test_web_thread_bundle.cc
index 15745da..0dd3078 100644
--- a/ios/web/test/test_web_thread_bundle.cc
+++ b/ios/web/test/test_web_thread_bundle.cc
@@ -4,46 +4,130 @@
#include "ios/web/public/test/test_web_thread_bundle.h"
-#include "content/public/test/test_browser_thread_bundle.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "ios/web/public/test/test_web_thread.h"
+#include "ios/web/web_thread_impl.h"
namespace web {
-namespace {
-
-int TestBrowserThreadBundleOptionsFromTestWebThreadBundleOptions(int options) {
- int result = 0;
- if (options & TestWebThreadBundle::IO_MAINLOOP)
- result |= content::TestBrowserThreadBundle::IO_MAINLOOP;
- if (options & TestWebThreadBundle::REAL_DB_THREAD)
- result |= content::TestBrowserThreadBundle::REAL_DB_THREAD;
- if (options & TestWebThreadBundle::REAL_FILE_THREAD)
- result |= content::TestBrowserThreadBundle::REAL_FILE_THREAD;
- if (options & TestWebThreadBundle::REAL_FILE_USER_BLOCKING_THREAD)
- result |= content::TestBrowserThreadBundle::REAL_FILE_USER_BLOCKING_THREAD;
- if (options & TestWebThreadBundle::REAL_CACHE_THREAD)
- result |= content::TestBrowserThreadBundle::REAL_CACHE_THREAD;
- if (options & TestWebThreadBundle::REAL_IO_THREAD)
- result |= content::TestBrowserThreadBundle::REAL_IO_THREAD;
- return result;
-}
-
-} // namespace
-
+// Implmentation of TestWebThreadBundle using TestWebThreads.
+// TODO(stuartmorgan): The only reason this is a separate impl class is to
+// keep the implementation details out of the header so that it can be shared
+// with the adapter implementation that uses TestBrowserThreadBundle. Once that
+// version is gone, fold this into TestWebThreadBundle.
class TestWebThreadBundleImpl {
public:
- explicit TestWebThreadBundleImpl(int options)
- : test_browser_thread_bundle_(
- TestBrowserThreadBundleOptionsFromTestWebThreadBundleOptions(
- options)) {}
+ explicit TestWebThreadBundleImpl();
+ explicit TestWebThreadBundleImpl(int options);
+
+ ~TestWebThreadBundleImpl();
private:
- content::TestBrowserThreadBundle test_browser_thread_bundle_;
+ void Init(int options);
+
+ scoped_ptr<base::MessageLoop> message_loop_;
+ scoped_ptr<TestWebThread> ui_thread_;
+ scoped_ptr<TestWebThread> db_thread_;
+ scoped_ptr<TestWebThread> file_thread_;
+ scoped_ptr<TestWebThread> file_user_blocking_thread_;
+ scoped_ptr<TestWebThread> cache_thread_;
+ scoped_ptr<TestWebThread> io_thread_;
DISALLOW_COPY_AND_ASSIGN(TestWebThreadBundleImpl);
};
+TestWebThreadBundleImpl::TestWebThreadBundleImpl() {
+ Init(TestWebThreadBundle::DEFAULT);
+}
+
+TestWebThreadBundleImpl::TestWebThreadBundleImpl(int options) {
+ Init(options);
+}
+
+TestWebThreadBundleImpl::~TestWebThreadBundleImpl() {
+ // To avoid memory leaks, ensure that any tasks posted to the blocking pool
+ // via PostTaskAndReply are able to reply back to the originating thread, by
+ // flushing the blocking pool while the browser threads still exist.
+ base::RunLoop().RunUntilIdle();
+ WebThreadImpl::FlushThreadPoolHelperForTesting();
+
+ // To ensure a clean teardown, each thread's message loop must be flushed
+ // just before the thread is destroyed. But destroying a fake thread does not
+ // automatically flush the message loop, so do it manually.
+ // See http://crbug.com/247525 for discussion.
+ base::RunLoop().RunUntilIdle();
+ io_thread_.reset();
+ base::RunLoop().RunUntilIdle();
+ cache_thread_.reset();
+ base::RunLoop().RunUntilIdle();
+ file_user_blocking_thread_.reset();
+ base::RunLoop().RunUntilIdle();
+ file_thread_.reset();
+ base::RunLoop().RunUntilIdle();
+ db_thread_.reset();
+ base::RunLoop().RunUntilIdle();
+ // This is the point at which the thread pool is normally shut down. So flush
+ // it again in case any shutdown tasks have been posted to the pool from the
+ // threads above.
+ WebThreadImpl::FlushThreadPoolHelperForTesting();
+ base::RunLoop().RunUntilIdle();
+ ui_thread_.reset();
+ base::RunLoop().RunUntilIdle();
+}
+
+void TestWebThreadBundleImpl::Init(int options) {
+ if (options & TestWebThreadBundle::IO_MAINLOOP) {
+ message_loop_.reset(new base::MessageLoopForIO());
+ } else {
+ message_loop_.reset(new base::MessageLoopForUI());
+ }
+
+ ui_thread_.reset(new TestWebThread(WebThread::UI, message_loop_.get()));
+
+ if (options & TestWebThreadBundle::REAL_DB_THREAD) {
+ db_thread_.reset(new TestWebThread(WebThread::DB));
+ db_thread_->Start();
+ } else {
+ db_thread_.reset(new TestWebThread(WebThread::DB, message_loop_.get()));
+ }
+
+ if (options & TestWebThreadBundle::REAL_FILE_THREAD) {
+ file_thread_.reset(new TestWebThread(WebThread::FILE));
+ file_thread_->Start();
+ } else {
+ file_thread_.reset(new TestWebThread(WebThread::FILE, message_loop_.get()));
+ }
+
+ if (options & TestWebThreadBundle::REAL_FILE_USER_BLOCKING_THREAD) {
+ file_user_blocking_thread_.reset(
+ new TestWebThread(WebThread::FILE_USER_BLOCKING));
+ file_user_blocking_thread_->Start();
+ } else {
+ file_user_blocking_thread_.reset(
+ new TestWebThread(WebThread::FILE_USER_BLOCKING, message_loop_.get()));
+ }
+
+ if (options & TestWebThreadBundle::REAL_CACHE_THREAD) {
+ cache_thread_.reset(new TestWebThread(WebThread::CACHE));
+ cache_thread_->Start();
+ } else {
+ cache_thread_.reset(
+ new TestWebThread(WebThread::CACHE, message_loop_.get()));
+ }
+
+ if (options & TestWebThreadBundle::REAL_IO_THREAD) {
+ io_thread_.reset(new TestWebThread(WebThread::IO));
+ io_thread_->StartIOThread();
+ } else {
+ io_thread_.reset(new TestWebThread(WebThread::IO, message_loop_.get()));
+ }
+}
+
+#pragma mark - TestWebThreadBundle
+
TestWebThreadBundle::TestWebThreadBundle()
- : impl_(new TestWebThreadBundleImpl(DEFAULT)) {
+ : impl_(new TestWebThreadBundleImpl()) {
}
TestWebThreadBundle::TestWebThreadBundle(int options)
diff --git a/ios/web/test/test_web_thread_bundle_adapter.cc b/ios/web/test/test_web_thread_bundle_adapter.cc
new file mode 100644
index 0000000..15745da
--- /dev/null
+++ b/ios/web/test/test_web_thread_bundle_adapter.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/public/test/test_web_thread_bundle.h"
+
+#include "content/public/test/test_browser_thread_bundle.h"
+
+namespace web {
+
+namespace {
+
+int TestBrowserThreadBundleOptionsFromTestWebThreadBundleOptions(int options) {
+ int result = 0;
+ if (options & TestWebThreadBundle::IO_MAINLOOP)
+ result |= content::TestBrowserThreadBundle::IO_MAINLOOP;
+ if (options & TestWebThreadBundle::REAL_DB_THREAD)
+ result |= content::TestBrowserThreadBundle::REAL_DB_THREAD;
+ if (options & TestWebThreadBundle::REAL_FILE_THREAD)
+ result |= content::TestBrowserThreadBundle::REAL_FILE_THREAD;
+ if (options & TestWebThreadBundle::REAL_FILE_USER_BLOCKING_THREAD)
+ result |= content::TestBrowserThreadBundle::REAL_FILE_USER_BLOCKING_THREAD;
+ if (options & TestWebThreadBundle::REAL_CACHE_THREAD)
+ result |= content::TestBrowserThreadBundle::REAL_CACHE_THREAD;
+ if (options & TestWebThreadBundle::REAL_IO_THREAD)
+ result |= content::TestBrowserThreadBundle::REAL_IO_THREAD;
+ return result;
+}
+
+} // namespace
+
+class TestWebThreadBundleImpl {
+ public:
+ explicit TestWebThreadBundleImpl(int options)
+ : test_browser_thread_bundle_(
+ TestBrowserThreadBundleOptionsFromTestWebThreadBundleOptions(
+ options)) {}
+
+ private:
+ content::TestBrowserThreadBundle test_browser_thread_bundle_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWebThreadBundleImpl);
+};
+
+TestWebThreadBundle::TestWebThreadBundle()
+ : impl_(new TestWebThreadBundleImpl(DEFAULT)) {
+}
+
+TestWebThreadBundle::TestWebThreadBundle(int options)
+ : impl_(new TestWebThreadBundleImpl(options)) {
+}
+
+TestWebThreadBundle::~TestWebThreadBundle() {
+}
+
+} // namespace web
diff --git a/ios/web/web_thread.cc b/ios/web/web_thread.cc
deleted file mode 100644
index 7fa4c06..0000000
--- a/ios/web/web_thread.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/web/public/web_thread.h"
-
-#include "content/public/browser/browser_thread.h"
-#include "ios/web/web_thread_impl.h"
-
-namespace web {
-
-// static
-bool WebThread::PostTask(ID identifier,
- const tracked_objects::Location& from_here,
- const base::Closure& task) {
- return content::BrowserThread::PostTask(
- BrowserThreadIDFromWebThreadID(identifier), from_here, task);
-}
-
-// static
-bool WebThread::PostDelayedTask(ID identifier,
- const tracked_objects::Location& from_here,
- const base::Closure& task,
- base::TimeDelta delay) {
- return content::BrowserThread::PostDelayedTask(
- BrowserThreadIDFromWebThreadID(identifier), from_here, task, delay);
-}
-
-// static
-bool WebThread::PostTaskAndReply(ID identifier,
- const tracked_objects::Location& from_here,
- const base::Closure& task,
- const base::Closure& reply) {
- return content::BrowserThread::PostTaskAndReply(
- BrowserThreadIDFromWebThreadID(identifier), from_here, task, reply);
-}
-
-// static
-bool WebThread::PostBlockingPoolTask(const tracked_objects::Location& from_here,
- const base::Closure& task) {
- return content::BrowserThread::PostBlockingPoolTask(from_here, task);
-}
-
-// static
-bool WebThread::PostBlockingPoolSequencedTask(
- const std::string& sequence_token_name,
- const tracked_objects::Location& from_here,
- const base::Closure& task) {
- return content::BrowserThread::PostBlockingPoolSequencedTask(
- sequence_token_name, from_here, task);
-}
-
-// static
-base::SequencedWorkerPool* WebThread::GetBlockingPool() {
- return content::BrowserThread::GetBlockingPool();
-}
-
-// static
-base::MessageLoop* WebThread::UnsafeGetMessageLoopForThread(ID identifier) {
- return content::BrowserThread::UnsafeGetMessageLoopForThread(
- BrowserThreadIDFromWebThreadID(identifier));
-}
-
-// static
-bool WebThread::CurrentlyOn(ID identifier) {
- return content::BrowserThread::CurrentlyOn(
- BrowserThreadIDFromWebThreadID(identifier));
-}
-
-// static
-scoped_refptr<base::MessageLoopProxy> WebThread::GetMessageLoopProxyForThread(
- ID identifier) {
- return content::BrowserThread::GetMessageLoopProxyForThread(
- BrowserThreadIDFromWebThreadID(identifier));
-}
-
-// static
-std::string WebThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
- return content::BrowserThread::GetDCheckCurrentlyOnErrorMessage(
- BrowserThreadIDFromWebThreadID(expected));
-}
-
-} // namespace web
diff --git a/ios/web/web_thread_adapter.cc b/ios/web/web_thread_adapter.cc
new file mode 100644
index 0000000..016d23d
--- /dev/null
+++ b/ios/web/web_thread_adapter.cc
@@ -0,0 +1,179 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/web_thread_adapter.h"
+
+#include "content/public/browser/browser_thread.h"
+
+namespace web {
+
+namespace {
+
+WebThread::ID WebThreadIDFromBrowserThreadID(
+ content::BrowserThread::ID identifier) {
+ switch (identifier) {
+ case content::BrowserThread::UI:
+ return WebThread::UI;
+ case content::BrowserThread::DB:
+ return WebThread::DB;
+ case content::BrowserThread::FILE:
+ return WebThread::FILE;
+ case content::BrowserThread::FILE_USER_BLOCKING:
+ return WebThread::FILE_USER_BLOCKING;
+ case content::BrowserThread::CACHE:
+ return WebThread::CACHE;
+ case content::BrowserThread::IO:
+ return WebThread::IO;
+ default:
+ NOTREACHED() << "Unknown content::BrowserThread::ID: " << identifier;
+ return WebThread::UI;
+ }
+}
+
+} // namespace
+
+content::BrowserThread::ID BrowserThreadIDFromWebThreadID(
+ WebThread::ID identifier) {
+ switch (identifier) {
+ case WebThread::UI:
+ return content::BrowserThread::UI;
+ case WebThread::DB:
+ return content::BrowserThread::DB;
+ case WebThread::FILE:
+ return content::BrowserThread::FILE;
+ case WebThread::FILE_USER_BLOCKING:
+ return content::BrowserThread::FILE_USER_BLOCKING;
+ case WebThread::CACHE:
+ return content::BrowserThread::CACHE;
+ case WebThread::IO:
+ return content::BrowserThread::IO;
+ default:
+ NOTREACHED() << "Unknown web::WebThread::ID: " << identifier;
+ return content::BrowserThread::UI;
+ }
+}
+
+#pragma mark - web_thread.h implementation
+
+// static
+bool WebThread::PostTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return content::BrowserThread::PostTask(
+ BrowserThreadIDFromWebThreadID(identifier), from_here, task);
+}
+
+// static
+bool WebThread::PostDelayedTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ return content::BrowserThread::PostDelayedTask(
+ BrowserThreadIDFromWebThreadID(identifier), from_here, task, delay);
+}
+
+// static
+bool WebThread::PostNonNestableTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return content::BrowserThread::PostNonNestableTask(
+ BrowserThreadIDFromWebThreadID(identifier), from_here, task);
+}
+
+// static
+bool WebThread::PostNonNestableDelayedTask(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ return content::BrowserThread::PostNonNestableDelayedTask(
+ BrowserThreadIDFromWebThreadID(identifier), from_here, task, delay);
+}
+
+// static
+bool WebThread::PostTaskAndReply(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const base::Closure& reply) {
+ return content::BrowserThread::PostTaskAndReply(
+ BrowserThreadIDFromWebThreadID(identifier), from_here, task, reply);
+}
+
+// static
+bool WebThread::PostBlockingPoolTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return content::BrowserThread::PostBlockingPoolTask(from_here, task);
+}
+
+// static
+bool WebThread::PostBlockingPoolTaskAndReply(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const base::Closure& reply) {
+ return content::BrowserThread::PostBlockingPoolTaskAndReply(from_here, task,
+ reply);
+}
+
+// static
+bool WebThread::PostBlockingPoolSequencedTask(
+ const std::string& sequence_token_name,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return content::BrowserThread::PostBlockingPoolSequencedTask(
+ sequence_token_name, from_here, task);
+}
+
+// static
+base::SequencedWorkerPool* WebThread::GetBlockingPool() {
+ return content::BrowserThread::GetBlockingPool();
+}
+
+// static
+base::MessageLoop* WebThread::UnsafeGetMessageLoopForThread(ID identifier) {
+ return content::BrowserThread::UnsafeGetMessageLoopForThread(
+ BrowserThreadIDFromWebThreadID(identifier));
+}
+
+// static
+bool WebThread::IsThreadInitialized(ID identifier) {
+ return content::BrowserThread::IsThreadInitialized(
+ BrowserThreadIDFromWebThreadID(identifier));
+}
+
+// static
+bool WebThread::CurrentlyOn(ID identifier) {
+ return content::BrowserThread::CurrentlyOn(
+ BrowserThreadIDFromWebThreadID(identifier));
+}
+
+// static
+bool WebThread::IsMessageLoopValid(ID identifier) {
+ return content::BrowserThread::IsMessageLoopValid(
+ BrowserThreadIDFromWebThreadID(identifier));
+}
+
+// static
+bool WebThread::GetCurrentThreadIdentifier(ID* identifier) {
+ content::BrowserThread::ID content_identifier;
+ bool result =
+ content::BrowserThread::GetCurrentThreadIdentifier(&content_identifier);
+ if (result)
+ *identifier = WebThreadIDFromBrowserThreadID(content_identifier);
+ return result;
+}
+
+// static
+scoped_refptr<base::MessageLoopProxy> WebThread::GetMessageLoopProxyForThread(
+ ID identifier) {
+ return content::BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThreadIDFromWebThreadID(identifier));
+}
+
+// static
+std::string WebThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
+ return content::BrowserThread::GetDCheckCurrentlyOnErrorMessage(
+ BrowserThreadIDFromWebThreadID(expected));
+}
+
+} // namespace web
diff --git a/ios/web/web_thread_adapter.h b/ios/web/web_thread_adapter.h
new file mode 100644
index 0000000..3a4e872
--- /dev/null
+++ b/ios/web/web_thread_adapter.h
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_WEB_THREAD_ADAPTER_H_
+#define IOS_WEB_WEB_THREAD_ADAPTER_H_
+
+#include "content/public/browser/browser_thread.h"
+#include "ios/web/public/web_thread.h"
+
+namespace web {
+
+// Converts web::WebThread::ID to content::BrowserThread::ID.
+content::BrowserThread::ID BrowserThreadIDFromWebThreadID(
+ WebThread::ID identifier);
+
+} // namespace web
+
+#endif // IOS_WEB_WEB_THREAD_ADAPTER_H_
diff --git a/ios/web/web_thread_impl.cc b/ios/web/web_thread_impl.cc
index 5f5b615..e329bb0 100644
--- a/ios/web/web_thread_impl.cc
+++ b/ios/web/web_thread_impl.cc
@@ -4,29 +4,510 @@
#include "ios/web/web_thread_impl.h"
-#include "base/logging.h"
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread_restrictions.h"
+#include "ios/web/public/web_thread_delegate.h"
+#include "net/disk_cache/simple/simple_backend_impl.h"
+#include "net/url_request/url_fetcher.h"
namespace web {
-content::BrowserThread::ID BrowserThreadIDFromWebThreadID(
- WebThread::ID identifier) {
- switch (identifier) {
+namespace {
+
+// Friendly names for the well-known threads.
+const char* g_web_thread_names[WebThread::ID_COUNT] = {
+ "Web_UIThread", // UI
+ "Web_DBThread", // DB
+ "Web_FileThread", // FILE
+ "Web_FileUserBlockingThread", // FILE_USER_BLOCKING
+ "Web_CacheThread", // CACHE
+ "Web_IOThread", // IO
+};
+
+// An implementation of MessageLoopProxy to be used in conjunction
+// with WebThread.
+class WebThreadMessageLoopProxy : public base::MessageLoopProxy {
+ public:
+ explicit WebThreadMessageLoopProxy(WebThread::ID identifier)
+ : id_(identifier) {}
+
+ // MessageLoopProxy implementation.
+ bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) override {
+ return WebThread::PostDelayedTask(id_, from_here, task, delay);
+ }
+
+ bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) override {
+ return WebThread::PostNonNestableDelayedTask(id_, from_here, task, delay);
+ }
+
+ bool RunsTasksOnCurrentThread() const override {
+ return WebThread::CurrentlyOn(id_);
+ }
+
+ protected:
+ ~WebThreadMessageLoopProxy() override {}
+
+ private:
+ WebThread::ID id_;
+ DISALLOW_COPY_AND_ASSIGN(WebThreadMessageLoopProxy);
+};
+
+// A separate helper is used just for the proxies, in order to avoid needing
+// to initialize the globals to create a proxy.
+struct WebThreadProxies {
+ WebThreadProxies() {
+ for (int i = 0; i < WebThread::ID_COUNT; ++i) {
+ proxies[i] = new WebThreadMessageLoopProxy(static_cast<WebThread::ID>(i));
+ }
+ }
+
+ scoped_refptr<base::MessageLoopProxy> proxies[WebThread::ID_COUNT];
+};
+
+base::LazyInstance<WebThreadProxies>::Leaky g_proxies =
+ LAZY_INSTANCE_INITIALIZER;
+
+struct WebThreadGlobals {
+ WebThreadGlobals()
+ : blocking_pool(new base::SequencedWorkerPool(3, "WebBlocking")) {
+ memset(threads, 0, WebThread::ID_COUNT * sizeof(threads[0]));
+ memset(thread_delegates, 0,
+ WebThread::ID_COUNT * sizeof(thread_delegates[0]));
+ }
+
+ // This lock protects |threads|. Do not read or modify that array
+ // without holding this lock. Do not block while holding this lock.
+ base::Lock lock;
+
+ // This array is protected by |lock|. The threads are not owned by this
+ // array. Typically, the threads are owned on the UI thread by
+ // WebMainLoop. WebThreadImpl objects remove themselves from this
+ // array upon destruction.
+ WebThreadImpl* threads[WebThread::ID_COUNT];
+
+ // Only atomic operations are used on this array. The delegates are not owned
+ // by this array, rather by whoever calls WebThread::SetDelegate.
+ WebThreadDelegate* thread_delegates[WebThread::ID_COUNT];
+
+ const scoped_refptr<base::SequencedWorkerPool> blocking_pool;
+};
+
+base::LazyInstance<WebThreadGlobals>::Leaky g_globals =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+WebThreadImpl::WebThreadImpl(ID identifier)
+ : Thread(g_web_thread_names[identifier]), identifier_(identifier) {
+ Initialize();
+}
+
+WebThreadImpl::WebThreadImpl(ID identifier, base::MessageLoop* message_loop)
+ : Thread(message_loop->thread_name()), identifier_(identifier) {
+ set_message_loop(message_loop);
+ Initialize();
+}
+
+// static
+void WebThreadImpl::ShutdownThreadPool() {
+ // The goal is to make it impossible to 'infinite loop' during shutdown,
+ // but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued during
+ // shutdown get run. There's nothing particularly scientific about the
+ // number chosen.
+ const int kMaxNewShutdownBlockingTasks = 1000;
+ WebThreadGlobals& globals = g_globals.Get();
+ globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks);
+}
+
+// static
+void WebThreadImpl::FlushThreadPoolHelperForTesting() {
+ // We don't want to create a pool if none exists.
+ if (g_globals == nullptr)
+ return;
+ g_globals.Get().blocking_pool->FlushForTesting();
+ disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
+}
+
+void WebThreadImpl::Init() {
+ WebThreadGlobals& globals = g_globals.Get();
+
+ using base::subtle::AtomicWord;
+ AtomicWord* storage =
+ reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
+ AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
+ WebThreadDelegate* delegate =
+ reinterpret_cast<WebThreadDelegate*>(stored_pointer);
+ if (delegate) {
+ delegate->Init();
+ message_loop()->PostTask(FROM_HERE,
+ base::Bind(&WebThreadDelegate::InitAsync,
+ // Delegate is expected to exist for the
+ // duration of the thread's lifetime
+ base::Unretained(delegate)));
+ }
+
+ if (WebThread::CurrentlyOn(WebThread::IO)) {
+ // Though this thread is called the "IO" thread, it actually just routes
+ // messages around; it shouldn't be allowed to perform any blocking disk
+ // I/O.
+ base::ThreadRestrictions::SetIOAllowed(false);
+ base::ThreadRestrictions::DisallowWaiting();
+ }
+}
+
+NOINLINE void WebThreadImpl::UIThreadRun(base::MessageLoop* message_loop) {
+ volatile int line_number = __LINE__;
+ Thread::Run(message_loop);
+ CHECK_GT(line_number, 0);
+}
+
+NOINLINE void WebThreadImpl::DBThreadRun(base::MessageLoop* message_loop) {
+ volatile int line_number = __LINE__;
+ Thread::Run(message_loop);
+ CHECK_GT(line_number, 0);
+}
+
+NOINLINE void WebThreadImpl::FileThreadRun(base::MessageLoop* message_loop) {
+ volatile int line_number = __LINE__;
+ Thread::Run(message_loop);
+ CHECK_GT(line_number, 0);
+}
+
+NOINLINE void WebThreadImpl::FileUserBlockingThreadRun(
+ base::MessageLoop* message_loop) {
+ volatile int line_number = __LINE__;
+ Thread::Run(message_loop);
+ CHECK_GT(line_number, 0);
+}
+
+NOINLINE void WebThreadImpl::CacheThreadRun(base::MessageLoop* message_loop) {
+ volatile int line_number = __LINE__;
+ Thread::Run(message_loop);
+ CHECK_GT(line_number, 0);
+}
+
+NOINLINE void WebThreadImpl::IOThreadRun(base::MessageLoop* message_loop) {
+ volatile int line_number = __LINE__;
+ Thread::Run(message_loop);
+ CHECK_GT(line_number, 0);
+}
+
+void WebThreadImpl::Run(base::MessageLoop* message_loop) {
+ WebThread::ID thread_id = ID_COUNT;
+ if (!GetCurrentThreadIdentifier(&thread_id))
+ return Thread::Run(message_loop);
+
+ switch (thread_id) {
case WebThread::UI:
- return content::BrowserThread::UI;
+ return UIThreadRun(message_loop);
case WebThread::DB:
- return content::BrowserThread::DB;
+ return DBThreadRun(message_loop);
case WebThread::FILE:
- return content::BrowserThread::FILE;
+ return FileThreadRun(message_loop);
case WebThread::FILE_USER_BLOCKING:
- return content::BrowserThread::FILE_USER_BLOCKING;
+ return FileUserBlockingThreadRun(message_loop);
case WebThread::CACHE:
- return content::BrowserThread::CACHE;
+ return CacheThreadRun(message_loop);
case WebThread::IO:
- return content::BrowserThread::IO;
- default:
- NOTREACHED() << "Unknown web::WebThread::ID: " << identifier;
- return content::BrowserThread::UI;
+ return IOThreadRun(message_loop);
+ case WebThread::ID_COUNT:
+ CHECK(false); // This shouldn't actually be reached!
+ break;
}
+ Thread::Run(message_loop);
+}
+
+void WebThreadImpl::CleanUp() {
+ if (WebThread::CurrentlyOn(WebThread::IO))
+ IOThreadPreCleanUp();
+
+ WebThreadGlobals& globals = g_globals.Get();
+
+ using base::subtle::AtomicWord;
+ AtomicWord* storage =
+ reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
+ AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
+ WebThreadDelegate* delegate =
+ reinterpret_cast<WebThreadDelegate*>(stored_pointer);
+
+ if (delegate)
+ delegate->CleanUp();
+}
+
+void WebThreadImpl::Initialize() {
+ WebThreadGlobals& globals = g_globals.Get();
+
+ base::AutoLock lock(globals.lock);
+ DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT);
+ DCHECK(globals.threads[identifier_] == nullptr);
+ globals.threads[identifier_] = this;
+}
+
+void WebThreadImpl::IOThreadPreCleanUp() {
+ // Kill all things that might be holding onto
+ // net::URLRequest/net::URLRequestContexts.
+
+ // Destroy all URLRequests started by URLFetchers.
+ net::URLFetcher::CancelAll();
+}
+
+WebThreadImpl::~WebThreadImpl() {
+ // All Thread subclasses must call Stop() in the destructor. This is
+ // doubly important here as various bits of code check they are on
+ // the right WebThread.
+ Stop();
+
+ WebThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
+ globals.threads[identifier_] = nullptr;
+#ifndef NDEBUG
+ // Double check that the threads are ordered correctly in the enumeration.
+ for (int i = identifier_ + 1; i < ID_COUNT; ++i) {
+ DCHECK(!globals.threads[i])
+ << "Threads must be listed in the reverse order that they die";
+ }
+#endif
+}
+
+// static
+bool WebThreadImpl::PostTaskHelper(WebThread::ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay,
+ bool nestable) {
+ DCHECK(identifier >= 0 && identifier < ID_COUNT);
+ // Optimization: to avoid unnecessary locks, we listed the ID enumeration in
+ // order of lifetime. So no need to lock if we know that the target thread
+ // outlives current thread.
+ // Note: since the array is so small, ok to loop instead of creating a map,
+ // which would require a lock because std::map isn't thread safe, defeating
+ // the whole purpose of this optimization.
+ WebThread::ID current_thread = ID_COUNT;
+ bool target_thread_outlives_current =
+ GetCurrentThreadIdentifier(&current_thread) &&
+ current_thread >= identifier;
+
+ WebThreadGlobals& globals = g_globals.Get();
+ if (!target_thread_outlives_current)
+ globals.lock.Acquire();
+
+ base::MessageLoop* message_loop =
+ globals.threads[identifier] ? globals.threads[identifier]->message_loop()
+ : nullptr;
+ if (message_loop) {
+ if (nestable) {
+ message_loop->PostDelayedTask(from_here, task, delay);
+ } else {
+ message_loop->PostNonNestableDelayedTask(from_here, task, delay);
+ }
+ }
+
+ if (!target_thread_outlives_current)
+ globals.lock.Release();
+
+ return !!message_loop;
+}
+
+// static
+bool WebThread::PostBlockingPoolTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task);
+}
+
+// static
+bool WebThread::PostBlockingPoolTaskAndReply(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const base::Closure& reply) {
+ return g_globals.Get().blocking_pool->PostTaskAndReply(from_here, task,
+ reply);
+}
+
+// static
+bool WebThread::PostBlockingPoolSequencedTask(
+ const std::string& sequence_token_name,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return g_globals.Get().blocking_pool->PostNamedSequencedWorkerTask(
+ sequence_token_name, from_here, task);
+}
+
+// static
+base::SequencedWorkerPool* WebThread::GetBlockingPool() {
+ return g_globals.Get().blocking_pool.get();
+}
+
+// static
+bool WebThread::IsThreadInitialized(ID identifier) {
+ if (g_globals == nullptr)
+ return false;
+
+ WebThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
+ DCHECK(identifier >= 0 && identifier < ID_COUNT);
+ return globals.threads[identifier] != nullptr;
+}
+
+// static
+bool WebThread::CurrentlyOn(ID identifier) {
+ // This shouldn't use MessageLoop::current() since it uses LazyInstance which
+ // may be deleted by ~AtExitManager when a WorkerPool thread calls this
+ // function.
+ // http://crbug.com/63678
+ base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
+ WebThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
+ DCHECK(identifier >= 0 && identifier < ID_COUNT);
+ return globals.threads[identifier] &&
+ globals.threads[identifier]->message_loop() ==
+ base::MessageLoop::current();
+}
+
+static const char* GetThreadName(WebThread::ID thread) {
+ if (WebThread::UI <= thread && thread < WebThread::ID_COUNT)
+ return g_web_thread_names[thread];
+ return "Unknown Thread";
+}
+
+// static
+std::string WebThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
+ const base::MessageLoop* message_loop = base::MessageLoop::current();
+ ID actual_web_thread;
+ const char* actual_name = "Unknown Thread";
+ if (message_loop && !message_loop->thread_name().empty()) {
+ actual_name = message_loop->thread_name().c_str();
+ } else if (GetCurrentThreadIdentifier(&actual_web_thread)) {
+ actual_name = GetThreadName(actual_web_thread);
+ }
+ std::string result = "Must be called on ";
+ result += GetThreadName(expected);
+ result += "; actually called on ";
+ result += actual_name;
+ result += ".";
+ return result;
+}
+
+// static
+bool WebThread::IsMessageLoopValid(ID identifier) {
+ if (g_globals == nullptr)
+ return false;
+
+ WebThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
+ DCHECK(identifier >= 0 && identifier < ID_COUNT);
+ return globals.threads[identifier] &&
+ globals.threads[identifier]->message_loop();
+}
+
+// static
+bool WebThread::PostTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return WebThreadImpl::PostTaskHelper(identifier, from_here, task,
+ base::TimeDelta(), true);
+}
+
+// static
+bool WebThread::PostDelayedTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ return WebThreadImpl::PostTaskHelper(identifier, from_here, task, delay,
+ true);
+}
+
+// static
+bool WebThread::PostNonNestableTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return WebThreadImpl::PostTaskHelper(identifier, from_here, task,
+ base::TimeDelta(), false);
+}
+
+// static
+bool WebThread::PostNonNestableDelayedTask(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ return WebThreadImpl::PostTaskHelper(identifier, from_here, task, delay,
+ false);
+}
+
+// static
+bool WebThread::PostTaskAndReply(ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const base::Closure& reply) {
+ return GetMessageLoopProxyForThread(identifier)
+ ->PostTaskAndReply(from_here, task, reply);
+}
+
+// static
+bool WebThread::GetCurrentThreadIdentifier(ID* identifier) {
+ if (g_globals == nullptr)
+ return false;
+
+ // This shouldn't use MessageLoop::current() since it uses LazyInstance which
+ // may be deleted by ~AtExitManager when a WorkerPool thread calls this
+ // function.
+ // http://crbug.com/63678
+ base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
+ base::MessageLoop* cur_message_loop = base::MessageLoop::current();
+ WebThreadGlobals& globals = g_globals.Get();
+ for (int i = 0; i < ID_COUNT; ++i) {
+ if (globals.threads[i] &&
+ globals.threads[i]->message_loop() == cur_message_loop) {
+ *identifier = globals.threads[i]->identifier_;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// static
+scoped_refptr<base::MessageLoopProxy> WebThread::GetMessageLoopProxyForThread(
+ ID identifier) {
+ return g_proxies.Get().proxies[identifier];
+}
+
+// static
+base::MessageLoop* WebThread::UnsafeGetMessageLoopForThread(ID identifier) {
+ if (g_globals == nullptr)
+ return nullptr;
+
+ WebThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
+ base::Thread* thread = globals.threads[identifier];
+ DCHECK(thread);
+ base::MessageLoop* loop = thread->message_loop();
+ return loop;
+}
+
+// static
+void WebThreadImpl::SetDelegate(ID identifier, WebThreadDelegate* delegate) {
+ using base::subtle::AtomicWord;
+ WebThreadGlobals& globals = g_globals.Get();
+ AtomicWord* storage =
+ reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier]);
+ AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange(
+ storage, reinterpret_cast<AtomicWord>(delegate));
+
+ // This catches registration when previously registered.
+ DCHECK(!delegate || !old_pointer);
}
} // namespace web
diff --git a/ios/web/web_thread_impl.h b/ios/web/web_thread_impl.h
index 03da4e7..4ed12fe 100644
--- a/ios/web/web_thread_impl.h
+++ b/ios/web/web_thread_impl.h
@@ -5,14 +5,83 @@
#ifndef IOS_WEB_WEB_THREAD_IMPL_H_
#define IOS_WEB_WEB_THREAD_IMPL_H_
-#include "content/public/browser/browser_thread.h"
+#include "base/threading/thread.h"
#include "ios/web/public/web_thread.h"
namespace web {
-// Converts web::WebThread::ID to content::BrowserThread::ID.
-content::BrowserThread::ID BrowserThreadIDFromWebThreadID(
- WebThread::ID identifier);
+class WebThreadDelegate;
+
+class WebThreadImpl : public WebThread, public base::Thread {
+ public:
+ // Construct a WebThreadImpl with the supplied identifier. It is an error
+ // to construct a WebThreadImpl that already exists.
+ explicit WebThreadImpl(WebThread::ID identifier);
+
+ // Special constructor for the main (UI) thread and unittests. If a
+ // |message_loop| is provied, we use a dummy thread here since the main
+ // thread already exists.
+ WebThreadImpl(WebThread::ID identifier, base::MessageLoop* message_loop);
+ ~WebThreadImpl() override;
+
+ static void ShutdownThreadPool();
+
+ // TODO(stuartmorgan): Move this to WebThread (where it belongs) once
+ // the alternate BrowserThread-backed-WebThread implementation goes away. See
+ // the note in web_thread_delegate.h.
+ //
+ // Sets the delegate for the specified WebThread.
+ //
+ // Only one delegate may be registered at a time. Delegates may be
+ // unregistered by providing a nullptr pointer.
+ //
+ // If the caller unregisters a delegate before CleanUp has been
+ // called, it must perform its own locking to ensure the delegate is
+ // not deleted while unregistering.
+ static void SetDelegate(ID identifier, WebThreadDelegate* delegate);
+
+ protected:
+ void Init() override;
+ void Run(base::MessageLoop* message_loop) override;
+ void CleanUp() override;
+
+ private:
+ // This class implements all the functionality of the public WebThread
+ // functions, but state is stored in the WebThreadImpl to keep
+ // the API cleaner. Therefore make WebThread a friend class.
+ friend class WebThread;
+
+ // The following are unique function names that makes it possible to tell
+ // the thread id from the callstack alone in crash dumps.
+ void UIThreadRun(base::MessageLoop* message_loop);
+ void DBThreadRun(base::MessageLoop* message_loop);
+ void FileThreadRun(base::MessageLoop* message_loop);
+ void FileUserBlockingThreadRun(base::MessageLoop* message_loop);
+ void CacheThreadRun(base::MessageLoop* message_loop);
+ void IOThreadRun(base::MessageLoop* message_loop);
+
+ static bool PostTaskHelper(WebThread::ID identifier,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay,
+ bool nestable);
+
+ // Common initialization code for the constructors.
+ void Initialize();
+
+ // Performs cleanup that needs to happen on the IO thread before calling the
+ // embedder's CleanUp function.
+ void IOThreadPreCleanUp();
+
+ // For testing.
+ friend class TestWebThreadBundle;
+ friend class TestWebThreadBundleImpl;
+ static void FlushThreadPoolHelperForTesting();
+
+ // The identifier of this thread. Only one thread can exist with a given
+ // identifier at a given time.
+ ID identifier_;
+};
} // namespace web