diff options
author | stuartmorgan <stuartmorgan@chromium.org> | 2015-05-26 17:29:16 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-27 00:29:47 +0000 |
commit | fe0d591aa498986a405b8ce1ec205a039bcb39a3 (patch) | |
tree | 69417b7e7997265882f0aed4495e5a4aba8cef7a /ios | |
parent | b21df31570795292bbb644f3a8c43b14270435a4 (diff) | |
download | chromium_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/DEPS | 4 | ||||
-rw-r--r-- | ios/web/ios_web.gyp | 81 | ||||
-rw-r--r-- | ios/web/public/test/test_web_thread.h | 9 | ||||
-rw-r--r-- | ios/web/public/web_thread.h | 27 | ||||
-rw-r--r-- | ios/web/public/web_thread_delegate.h | 39 | ||||
-rw-r--r-- | ios/web/test/test_web_thread.cc | 28 | ||||
-rw-r--r-- | ios/web/test/test_web_thread_adapter.cc | 65 | ||||
-rw-r--r-- | ios/web/test/test_web_thread_bundle.cc | 140 | ||||
-rw-r--r-- | ios/web/test/test_web_thread_bundle_adapter.cc | 56 | ||||
-rw-r--r-- | ios/web/web_thread.cc | 83 | ||||
-rw-r--r-- | ios/web/web_thread_adapter.cc | 179 | ||||
-rw-r--r-- | ios/web/web_thread_adapter.h | 19 | ||||
-rw-r--r-- | ios/web/web_thread_impl.cc | 507 | ||||
-rw-r--r-- | ios/web/web_thread_impl.h | 77 |
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(¤t_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 |