123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- // 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_impl.h"
- #include <string>
- #include <utility>
- #include "base/atomicops.h"
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/compiler_specific.h"
- #include "base/lazy_instance.h"
- #include "base/run_loop.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/task/task_executor.h"
- #include "base/time/time.h"
- #include "ios/web/public/thread/web_task_traits.h"
- #include "ios/web/public/thread/web_thread_delegate.h"
- namespace web {
- namespace {
- // State of a given WebThread::ID.
- enum WebThreadState {
- // WebThread::ID does not exist.
- UNINITIALIZED = 0,
- // WebThread::ID is associated to a TaskRunner and is accepting tasks.
- RUNNING,
- // WebThread::ID no longer accepts tasks.
- SHUTDOWN,
- };
- struct WebThreadGlobals {
- WebThreadGlobals() {
- }
- // This lock protects |threads| and |states|. Do not read or modify those
- // arrays without holding this lock. Do not block while holding this lock.
- base::Lock lock;
- // This array is protected by |lock|. This array is filled as WebThreadImpls
- // are constructed and depopulated when they are destructed.
- scoped_refptr<base::SingleThreadTaskRunner>
- task_runners[WebThread::ID_COUNT] GUARDED_BY(lock);
- // This array is protected by |lock|. Holds the state of each WebThread::ID.
- WebThreadState states[WebThread::ID_COUNT] GUARDED_BY(lock) = {};
- };
- base::LazyInstance<WebThreadGlobals>::Leaky g_globals =
- LAZY_INSTANCE_INITIALIZER;
- bool PostTaskHelper(WebThread::ID identifier,
- const base::Location& from_here,
- base::OnceClosure task,
- base::TimeDelta delay,
- bool nestable) NO_THREAD_SAFETY_ANALYSIS {
- DCHECK_GE(identifier, 0);
- DCHECK_LT(identifier, WebThread::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 = WebThread::ID_COUNT;
- bool target_thread_outlives_current =
- WebThread::GetCurrentThreadIdentifier(¤t_thread) &&
- current_thread >= identifier;
- WebThreadGlobals& globals = g_globals.Get();
- if (!target_thread_outlives_current)
- globals.lock.Acquire();
- const bool accepting_tasks =
- globals.states[identifier] == WebThreadState::RUNNING;
- if (accepting_tasks) {
- base::SingleThreadTaskRunner* task_runner =
- globals.task_runners[identifier].get();
- DCHECK(task_runner);
- if (nestable) {
- task_runner->PostDelayedTask(from_here, std::move(task), delay);
- } else {
- task_runner->PostNonNestableDelayedTask(from_here, std::move(task),
- delay);
- }
- }
- if (!target_thread_outlives_current)
- globals.lock.Release();
- return accepting_tasks;
- }
- // An implementation of SingleThreadTaskRunner to be used in conjunction
- // with WebThread.
- class WebThreadTaskRunner : public base::SingleThreadTaskRunner {
- public:
- explicit WebThreadTaskRunner(WebThread::ID identifier) : id_(identifier) {}
- WebThreadTaskRunner(const WebThreadTaskRunner&) = delete;
- WebThreadTaskRunner& operator=(const WebThreadTaskRunner&) = delete;
- // SingleThreadTaskRunner implementation.
- bool PostDelayedTask(const base::Location& from_here,
- base::OnceClosure task,
- base::TimeDelta delay) override {
- return PostTaskHelper(id_, from_here, std::move(task), delay,
- true /* nestable */);
- }
- bool PostNonNestableDelayedTask(const base::Location& from_here,
- base::OnceClosure task,
- base::TimeDelta delay) override {
- return PostTaskHelper(id_, from_here, std::move(task), delay,
- false /* nestable */);
- }
- bool RunsTasksInCurrentSequence() const override {
- return WebThread::CurrentlyOn(id_);
- }
- protected:
- ~WebThreadTaskRunner() override {}
- private:
- WebThread::ID id_;
- };
- class WebThreadTaskExecutor : public base::TaskExecutor {
- public:
- WebThreadTaskExecutor() {}
- ~WebThreadTaskExecutor() override {}
- // base::TaskExecutor implementation.
- bool PostDelayedTask(const base::Location& from_here,
- const base::TaskTraits& traits,
- base::OnceClosure task,
- base::TimeDelta delay) override {
- return PostTaskHelper(
- GetWebThreadIdentifier(traits), from_here, std::move(task), delay,
- traits.GetExtension<WebTaskTraitsExtension>().nestable());
- }
- scoped_refptr<base::TaskRunner> CreateTaskRunner(
- const base::TaskTraits& traits) override {
- return GetTaskRunner(GetWebThreadIdentifier(traits), traits);
- }
- scoped_refptr<base::SequencedTaskRunner> CreateSequencedTaskRunner(
- const base::TaskTraits& traits) override {
- return GetTaskRunner(GetWebThreadIdentifier(traits), traits);
- }
- scoped_refptr<base::SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
- const base::TaskTraits& traits,
- base::SingleThreadTaskRunnerThreadMode thread_mode) override {
- // It's not possible to request DEDICATED access to a WebThread.
- DCHECK_EQ(thread_mode, base::SingleThreadTaskRunnerThreadMode::SHARED);
- return GetTaskRunner(GetWebThreadIdentifier(traits), traits);
- }
- scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
- WebThread::ID identifier,
- const base::TaskTraits& traits) const {
- // //TODO(crbug.com/1304248): Unlike content, iOS never honored
- // |traits.priority()|... but this is where it could.
- // Ref. content::BaseBrowserTaskExecutor::GetTaskRunner()
- switch (identifier) {
- case WebThread::UI:
- return ui_thread_task_runner_;
- case WebThread::IO:
- return io_thread_task_runner_;
- case WebThread::ID_COUNT:
- NOTREACHED();
- return nullptr;
- }
- }
- // A static getter that also verifies the instance was set and warns of steps
- // to take if not.
- static const WebThreadTaskExecutor* GetInstance() {
- DCHECK(g_instance)
- << "No web task executor created.\nHint: if this is in a unit test, "
- "you're likely missing a WebTaskEnvironment member in your "
- "fixture.";
- return g_instance;
- }
- // Creates the WebThreadTaskExecutor instance. It is intentionally leaked on
- // shutdown, except in unit tests which can reset it via
- // ResetInstanceForTesting().
- static void CreateInstance() {
- DCHECK(!g_instance);
- g_instance = new WebThreadTaskExecutor();
- base::RegisterTaskExecutor(WebTaskTraitsExtension::kExtensionId,
- g_instance);
- }
- static void ResetInstanceForTesting() {
- DCHECK(g_instance);
- base::UnregisterTaskExecutorForTesting(
- WebTaskTraitsExtension::kExtensionId);
- delete g_instance;
- g_instance = nullptr;
- }
- private:
- WebThread::ID GetWebThreadIdentifier(const base::TaskTraits& traits) {
- DCHECK_EQ(traits.extension_id(), WebTaskTraitsExtension::kExtensionId);
- const WebThread::ID id =
- traits.GetExtension<WebTaskTraitsExtension>().web_thread();
- DCHECK_LT(id, WebThread::ID_COUNT);
- // TODO(crbug.com/872372): Support shutdown behavior on UI/IO threads.
- if (traits.shutdown_behavior_set_explicitly()) {
- if (id == WebThread::UI) {
- DCHECK_EQ(base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
- traits.shutdown_behavior())
- << "Only SKIP_ON_SHUTDOWN is supported on UI thread.";
- } else if (id == WebThread::IO) {
- DCHECK_EQ(base::TaskShutdownBehavior::BLOCK_SHUTDOWN,
- traits.shutdown_behavior())
- << "Only BLOCK_SHUTDOWN is supported on IO thread.";
- }
- }
- return id;
- }
- static WebThreadTaskExecutor* g_instance;
- scoped_refptr<WebThreadTaskRunner> ui_thread_task_runner_ =
- base::MakeRefCounted<WebThreadTaskRunner>(WebThread::UI);
- scoped_refptr<WebThreadTaskRunner> io_thread_task_runner_ =
- base::MakeRefCounted<WebThreadTaskRunner>(WebThread::IO);
- };
- // static
- WebThreadTaskExecutor* WebThreadTaskExecutor::g_instance = nullptr;
- } // namespace
- scoped_refptr<base::SingleThreadTaskRunner> GetUIThreadTaskRunner(
- const WebTaskTraits& traits) {
- return WebThreadTaskExecutor::GetInstance()->GetTaskRunner(WebThread::UI,
- traits);
- }
- scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner(
- const WebTaskTraits& traits) {
- return WebThreadTaskExecutor::GetInstance()->GetTaskRunner(WebThread::IO,
- traits);
- }
- WebThreadImpl::WebThreadImpl(
- ID identifier,
- scoped_refptr<base::SingleThreadTaskRunner> task_runner)
- : identifier_(identifier) {
- DCHECK(task_runner);
- WebThreadGlobals& globals = g_globals.Get();
- base::AutoLock lock(globals.lock);
- DCHECK_GE(identifier_, 0);
- DCHECK_LT(identifier_, ID_COUNT);
- DCHECK_EQ(globals.states[identifier_], WebThreadState::UNINITIALIZED);
- globals.states[identifier_] = WebThreadState::RUNNING;
- DCHECK(!globals.task_runners[identifier_]);
- globals.task_runners[identifier_] = std::move(task_runner);
- }
- WebThreadImpl::~WebThreadImpl() {
- WebThreadGlobals& globals = g_globals.Get();
- base::AutoLock lock(globals.lock);
- DCHECK_EQ(globals.states[identifier_], WebThreadState::RUNNING);
- globals.states[identifier_] = WebThreadState::SHUTDOWN;
- }
- // static
- void WebThreadImpl::ResetGlobalsForTesting(WebThread::ID identifier) {
- WebThreadGlobals& globals = g_globals.Get();
- base::AutoLock lock(globals.lock);
- DCHECK_EQ(globals.states[identifier], WebThreadState::SHUTDOWN);
- globals.states[identifier] = WebThreadState::UNINITIALIZED;
- globals.task_runners[identifier] = nullptr;
- }
- // Friendly names for the well-known threads.
- // static
- const char* WebThreadImpl::GetThreadName(WebThread::ID thread) {
- static const char* const kWebThreadNames[WebThread::ID_COUNT] = {
- "Web_UIThread", // UI
- "Web_IOThread", // IO
- };
- if (WebThread::UI <= thread && thread < WebThread::ID_COUNT)
- return kWebThreadNames[thread];
- return "Unknown Thread";
- }
- // static
- bool WebThread::IsThreadInitialized(ID identifier) {
- if (!g_globals.IsCreated())
- return false;
- WebThreadGlobals& globals = g_globals.Get();
- base::AutoLock lock(globals.lock);
- DCHECK_GE(identifier, 0);
- DCHECK_LT(identifier, ID_COUNT);
- return globals.states[identifier] == WebThreadState::RUNNING;
- }
- // static
- bool WebThread::CurrentlyOn(ID identifier) {
- WebThreadGlobals& globals = g_globals.Get();
- base::AutoLock lock(globals.lock);
- DCHECK_GE(identifier, 0);
- DCHECK_LT(identifier, ID_COUNT);
- return globals.task_runners[identifier] &&
- globals.task_runners[identifier]->BelongsToCurrentThread();
- }
- // static
- std::string WebThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
- std::string actual_name = base::PlatformThread::GetName();
- if (actual_name.empty())
- actual_name = "Unknown Thread";
- std::string result = "Must be called on ";
- result += WebThreadImpl::GetThreadName(expected);
- result += "; actually called on ";
- result += actual_name;
- result += ".";
- return result;
- }
- // static
- bool WebThread::GetCurrentThreadIdentifier(ID* identifier) {
- if (!g_globals.IsCreated())
- return false;
- WebThreadGlobals& globals = g_globals.Get();
- base::AutoLock lock(globals.lock);
- for (int i = 0; i < ID_COUNT; ++i) {
- if (globals.task_runners[i] &&
- globals.task_runners[i]->BelongsToCurrentThread()) {
- *identifier = static_cast<ID>(i);
- return true;
- }
- }
- return false;
- }
- // static
- scoped_refptr<base::SingleThreadTaskRunner> WebThread::GetTaskRunnerForThread(
- ID identifier) {
- DCHECK_GE(identifier, 0);
- DCHECK_LT(identifier, ID_COUNT);
- switch (identifier) {
- case UI:
- return GetUIThreadTaskRunner({});
- case IO:
- return GetIOThreadTaskRunner({});
- case ID_COUNT:
- NOTREACHED();
- return nullptr;
- }
- }
- // static
- void WebThreadImpl::CreateTaskExecutor() {
- WebThreadTaskExecutor::CreateInstance();
- }
- // static
- void WebThreadImpl::ResetTaskExecutorForTesting() {
- WebThreadTaskExecutor::ResetInstanceForTesting();
- }
- } // namespace web
|