web_thread_impl.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ios/web/web_thread_impl.h"
  5. #include <string>
  6. #include <utility>
  7. #include "base/atomicops.h"
  8. #include "base/bind.h"
  9. #include "base/callback.h"
  10. #include "base/compiler_specific.h"
  11. #include "base/lazy_instance.h"
  12. #include "base/run_loop.h"
  13. #include "base/task/single_thread_task_runner.h"
  14. #include "base/task/task_executor.h"
  15. #include "base/time/time.h"
  16. #include "ios/web/public/thread/web_task_traits.h"
  17. #include "ios/web/public/thread/web_thread_delegate.h"
  18. namespace web {
  19. namespace {
  20. // State of a given WebThread::ID.
  21. enum WebThreadState {
  22. // WebThread::ID does not exist.
  23. UNINITIALIZED = 0,
  24. // WebThread::ID is associated to a TaskRunner and is accepting tasks.
  25. RUNNING,
  26. // WebThread::ID no longer accepts tasks.
  27. SHUTDOWN,
  28. };
  29. struct WebThreadGlobals {
  30. WebThreadGlobals() {
  31. }
  32. // This lock protects |threads| and |states|. Do not read or modify those
  33. // arrays without holding this lock. Do not block while holding this lock.
  34. base::Lock lock;
  35. // This array is protected by |lock|. This array is filled as WebThreadImpls
  36. // are constructed and depopulated when they are destructed.
  37. scoped_refptr<base::SingleThreadTaskRunner>
  38. task_runners[WebThread::ID_COUNT] GUARDED_BY(lock);
  39. // This array is protected by |lock|. Holds the state of each WebThread::ID.
  40. WebThreadState states[WebThread::ID_COUNT] GUARDED_BY(lock) = {};
  41. };
  42. base::LazyInstance<WebThreadGlobals>::Leaky g_globals =
  43. LAZY_INSTANCE_INITIALIZER;
  44. bool PostTaskHelper(WebThread::ID identifier,
  45. const base::Location& from_here,
  46. base::OnceClosure task,
  47. base::TimeDelta delay,
  48. bool nestable) NO_THREAD_SAFETY_ANALYSIS {
  49. DCHECK_GE(identifier, 0);
  50. DCHECK_LT(identifier, WebThread::ID_COUNT);
  51. // Optimization: to avoid unnecessary locks, we listed the ID enumeration in
  52. // order of lifetime. So no need to lock if we know that the target thread
  53. // outlives current thread.
  54. // Note: since the array is so small, ok to loop instead of creating a map,
  55. // which would require a lock because std::map isn't thread safe, defeating
  56. // the whole purpose of this optimization.
  57. WebThread::ID current_thread = WebThread::ID_COUNT;
  58. bool target_thread_outlives_current =
  59. WebThread::GetCurrentThreadIdentifier(&current_thread) &&
  60. current_thread >= identifier;
  61. WebThreadGlobals& globals = g_globals.Get();
  62. if (!target_thread_outlives_current)
  63. globals.lock.Acquire();
  64. const bool accepting_tasks =
  65. globals.states[identifier] == WebThreadState::RUNNING;
  66. if (accepting_tasks) {
  67. base::SingleThreadTaskRunner* task_runner =
  68. globals.task_runners[identifier].get();
  69. DCHECK(task_runner);
  70. if (nestable) {
  71. task_runner->PostDelayedTask(from_here, std::move(task), delay);
  72. } else {
  73. task_runner->PostNonNestableDelayedTask(from_here, std::move(task),
  74. delay);
  75. }
  76. }
  77. if (!target_thread_outlives_current)
  78. globals.lock.Release();
  79. return accepting_tasks;
  80. }
  81. // An implementation of SingleThreadTaskRunner to be used in conjunction
  82. // with WebThread.
  83. class WebThreadTaskRunner : public base::SingleThreadTaskRunner {
  84. public:
  85. explicit WebThreadTaskRunner(WebThread::ID identifier) : id_(identifier) {}
  86. WebThreadTaskRunner(const WebThreadTaskRunner&) = delete;
  87. WebThreadTaskRunner& operator=(const WebThreadTaskRunner&) = delete;
  88. // SingleThreadTaskRunner implementation.
  89. bool PostDelayedTask(const base::Location& from_here,
  90. base::OnceClosure task,
  91. base::TimeDelta delay) override {
  92. return PostTaskHelper(id_, from_here, std::move(task), delay,
  93. true /* nestable */);
  94. }
  95. bool PostNonNestableDelayedTask(const base::Location& from_here,
  96. base::OnceClosure task,
  97. base::TimeDelta delay) override {
  98. return PostTaskHelper(id_, from_here, std::move(task), delay,
  99. false /* nestable */);
  100. }
  101. bool RunsTasksInCurrentSequence() const override {
  102. return WebThread::CurrentlyOn(id_);
  103. }
  104. protected:
  105. ~WebThreadTaskRunner() override {}
  106. private:
  107. WebThread::ID id_;
  108. };
  109. class WebThreadTaskExecutor : public base::TaskExecutor {
  110. public:
  111. WebThreadTaskExecutor() {}
  112. ~WebThreadTaskExecutor() override {}
  113. // base::TaskExecutor implementation.
  114. bool PostDelayedTask(const base::Location& from_here,
  115. const base::TaskTraits& traits,
  116. base::OnceClosure task,
  117. base::TimeDelta delay) override {
  118. return PostTaskHelper(
  119. GetWebThreadIdentifier(traits), from_here, std::move(task), delay,
  120. traits.GetExtension<WebTaskTraitsExtension>().nestable());
  121. }
  122. scoped_refptr<base::TaskRunner> CreateTaskRunner(
  123. const base::TaskTraits& traits) override {
  124. return GetTaskRunner(GetWebThreadIdentifier(traits), traits);
  125. }
  126. scoped_refptr<base::SequencedTaskRunner> CreateSequencedTaskRunner(
  127. const base::TaskTraits& traits) override {
  128. return GetTaskRunner(GetWebThreadIdentifier(traits), traits);
  129. }
  130. scoped_refptr<base::SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
  131. const base::TaskTraits& traits,
  132. base::SingleThreadTaskRunnerThreadMode thread_mode) override {
  133. // It's not possible to request DEDICATED access to a WebThread.
  134. DCHECK_EQ(thread_mode, base::SingleThreadTaskRunnerThreadMode::SHARED);
  135. return GetTaskRunner(GetWebThreadIdentifier(traits), traits);
  136. }
  137. scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
  138. WebThread::ID identifier,
  139. const base::TaskTraits& traits) const {
  140. // //TODO(crbug.com/1304248): Unlike content, iOS never honored
  141. // |traits.priority()|... but this is where it could.
  142. // Ref. content::BaseBrowserTaskExecutor::GetTaskRunner()
  143. switch (identifier) {
  144. case WebThread::UI:
  145. return ui_thread_task_runner_;
  146. case WebThread::IO:
  147. return io_thread_task_runner_;
  148. case WebThread::ID_COUNT:
  149. NOTREACHED();
  150. return nullptr;
  151. }
  152. }
  153. // A static getter that also verifies the instance was set and warns of steps
  154. // to take if not.
  155. static const WebThreadTaskExecutor* GetInstance() {
  156. DCHECK(g_instance)
  157. << "No web task executor created.\nHint: if this is in a unit test, "
  158. "you're likely missing a WebTaskEnvironment member in your "
  159. "fixture.";
  160. return g_instance;
  161. }
  162. // Creates the WebThreadTaskExecutor instance. It is intentionally leaked on
  163. // shutdown, except in unit tests which can reset it via
  164. // ResetInstanceForTesting().
  165. static void CreateInstance() {
  166. DCHECK(!g_instance);
  167. g_instance = new WebThreadTaskExecutor();
  168. base::RegisterTaskExecutor(WebTaskTraitsExtension::kExtensionId,
  169. g_instance);
  170. }
  171. static void ResetInstanceForTesting() {
  172. DCHECK(g_instance);
  173. base::UnregisterTaskExecutorForTesting(
  174. WebTaskTraitsExtension::kExtensionId);
  175. delete g_instance;
  176. g_instance = nullptr;
  177. }
  178. private:
  179. WebThread::ID GetWebThreadIdentifier(const base::TaskTraits& traits) {
  180. DCHECK_EQ(traits.extension_id(), WebTaskTraitsExtension::kExtensionId);
  181. const WebThread::ID id =
  182. traits.GetExtension<WebTaskTraitsExtension>().web_thread();
  183. DCHECK_LT(id, WebThread::ID_COUNT);
  184. // TODO(crbug.com/872372): Support shutdown behavior on UI/IO threads.
  185. if (traits.shutdown_behavior_set_explicitly()) {
  186. if (id == WebThread::UI) {
  187. DCHECK_EQ(base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
  188. traits.shutdown_behavior())
  189. << "Only SKIP_ON_SHUTDOWN is supported on UI thread.";
  190. } else if (id == WebThread::IO) {
  191. DCHECK_EQ(base::TaskShutdownBehavior::BLOCK_SHUTDOWN,
  192. traits.shutdown_behavior())
  193. << "Only BLOCK_SHUTDOWN is supported on IO thread.";
  194. }
  195. }
  196. return id;
  197. }
  198. static WebThreadTaskExecutor* g_instance;
  199. scoped_refptr<WebThreadTaskRunner> ui_thread_task_runner_ =
  200. base::MakeRefCounted<WebThreadTaskRunner>(WebThread::UI);
  201. scoped_refptr<WebThreadTaskRunner> io_thread_task_runner_ =
  202. base::MakeRefCounted<WebThreadTaskRunner>(WebThread::IO);
  203. };
  204. // static
  205. WebThreadTaskExecutor* WebThreadTaskExecutor::g_instance = nullptr;
  206. } // namespace
  207. scoped_refptr<base::SingleThreadTaskRunner> GetUIThreadTaskRunner(
  208. const WebTaskTraits& traits) {
  209. return WebThreadTaskExecutor::GetInstance()->GetTaskRunner(WebThread::UI,
  210. traits);
  211. }
  212. scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner(
  213. const WebTaskTraits& traits) {
  214. return WebThreadTaskExecutor::GetInstance()->GetTaskRunner(WebThread::IO,
  215. traits);
  216. }
  217. WebThreadImpl::WebThreadImpl(
  218. ID identifier,
  219. scoped_refptr<base::SingleThreadTaskRunner> task_runner)
  220. : identifier_(identifier) {
  221. DCHECK(task_runner);
  222. WebThreadGlobals& globals = g_globals.Get();
  223. base::AutoLock lock(globals.lock);
  224. DCHECK_GE(identifier_, 0);
  225. DCHECK_LT(identifier_, ID_COUNT);
  226. DCHECK_EQ(globals.states[identifier_], WebThreadState::UNINITIALIZED);
  227. globals.states[identifier_] = WebThreadState::RUNNING;
  228. DCHECK(!globals.task_runners[identifier_]);
  229. globals.task_runners[identifier_] = std::move(task_runner);
  230. }
  231. WebThreadImpl::~WebThreadImpl() {
  232. WebThreadGlobals& globals = g_globals.Get();
  233. base::AutoLock lock(globals.lock);
  234. DCHECK_EQ(globals.states[identifier_], WebThreadState::RUNNING);
  235. globals.states[identifier_] = WebThreadState::SHUTDOWN;
  236. }
  237. // static
  238. void WebThreadImpl::ResetGlobalsForTesting(WebThread::ID identifier) {
  239. WebThreadGlobals& globals = g_globals.Get();
  240. base::AutoLock lock(globals.lock);
  241. DCHECK_EQ(globals.states[identifier], WebThreadState::SHUTDOWN);
  242. globals.states[identifier] = WebThreadState::UNINITIALIZED;
  243. globals.task_runners[identifier] = nullptr;
  244. }
  245. // Friendly names for the well-known threads.
  246. // static
  247. const char* WebThreadImpl::GetThreadName(WebThread::ID thread) {
  248. static const char* const kWebThreadNames[WebThread::ID_COUNT] = {
  249. "Web_UIThread", // UI
  250. "Web_IOThread", // IO
  251. };
  252. if (WebThread::UI <= thread && thread < WebThread::ID_COUNT)
  253. return kWebThreadNames[thread];
  254. return "Unknown Thread";
  255. }
  256. // static
  257. bool WebThread::IsThreadInitialized(ID identifier) {
  258. if (!g_globals.IsCreated())
  259. return false;
  260. WebThreadGlobals& globals = g_globals.Get();
  261. base::AutoLock lock(globals.lock);
  262. DCHECK_GE(identifier, 0);
  263. DCHECK_LT(identifier, ID_COUNT);
  264. return globals.states[identifier] == WebThreadState::RUNNING;
  265. }
  266. // static
  267. bool WebThread::CurrentlyOn(ID identifier) {
  268. WebThreadGlobals& globals = g_globals.Get();
  269. base::AutoLock lock(globals.lock);
  270. DCHECK_GE(identifier, 0);
  271. DCHECK_LT(identifier, ID_COUNT);
  272. return globals.task_runners[identifier] &&
  273. globals.task_runners[identifier]->BelongsToCurrentThread();
  274. }
  275. // static
  276. std::string WebThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
  277. std::string actual_name = base::PlatformThread::GetName();
  278. if (actual_name.empty())
  279. actual_name = "Unknown Thread";
  280. std::string result = "Must be called on ";
  281. result += WebThreadImpl::GetThreadName(expected);
  282. result += "; actually called on ";
  283. result += actual_name;
  284. result += ".";
  285. return result;
  286. }
  287. // static
  288. bool WebThread::GetCurrentThreadIdentifier(ID* identifier) {
  289. if (!g_globals.IsCreated())
  290. return false;
  291. WebThreadGlobals& globals = g_globals.Get();
  292. base::AutoLock lock(globals.lock);
  293. for (int i = 0; i < ID_COUNT; ++i) {
  294. if (globals.task_runners[i] &&
  295. globals.task_runners[i]->BelongsToCurrentThread()) {
  296. *identifier = static_cast<ID>(i);
  297. return true;
  298. }
  299. }
  300. return false;
  301. }
  302. // static
  303. scoped_refptr<base::SingleThreadTaskRunner> WebThread::GetTaskRunnerForThread(
  304. ID identifier) {
  305. DCHECK_GE(identifier, 0);
  306. DCHECK_LT(identifier, ID_COUNT);
  307. switch (identifier) {
  308. case UI:
  309. return GetUIThreadTaskRunner({});
  310. case IO:
  311. return GetIOThreadTaskRunner({});
  312. case ID_COUNT:
  313. NOTREACHED();
  314. return nullptr;
  315. }
  316. }
  317. // static
  318. void WebThreadImpl::CreateTaskExecutor() {
  319. WebThreadTaskExecutor::CreateInstance();
  320. }
  321. // static
  322. void WebThreadImpl::ResetTaskExecutorForTesting() {
  323. WebThreadTaskExecutor::ResetInstanceForTesting();
  324. }
  325. } // namespace web