• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/android/task_scheduler/task_runner_android.h"
6 
7 #include <array>
8 #include <string>
9 #include <utility>
10 
11 #include "base/android/jni_string.h"
12 #include "base/android_runtime_unchecked_jni_headers/Runnable_jni.h"
13 #include "base/base_jni_headers/TaskRunnerImpl_jni.h"
14 #include "base/check.h"
15 #include "base/compiler_specific.h"
16 #include "base/functional/bind.h"
17 #include "base/no_destructor.h"
18 #include "base/strings/strcat.h"
19 #include "base/task/current_thread.h"
20 #include "base/task/task_traits.h"
21 #include "base/task/thread_pool.h"
22 #include "base/task/thread_pool/thread_pool_impl.h"
23 #include "base/task/thread_pool/thread_pool_instance.h"
24 #include "base/time/time.h"
25 #include "base/trace_event/base_tracing.h"
26 
27 namespace base {
28 
29 namespace {
30 
GetUiThreadTaskRunnerCallback()31 TaskRunnerAndroid::UiThreadTaskRunnerCallback& GetUiThreadTaskRunnerCallback() {
32   static base::NoDestructor<TaskRunnerAndroid::UiThreadTaskRunnerCallback>
33       callback;
34   return *callback;
35 }
36 
RunJavaTask(base::android::ScopedJavaGlobalRef<jobject> task,const std::string & runnable_class_name)37 void RunJavaTask(base::android::ScopedJavaGlobalRef<jobject> task,
38                  const std::string& runnable_class_name) {
39   TRACE_EVENT("toplevel", nullptr, [&](::perfetto::EventContext& ctx) {
40     std::string event_name =
41         base::StrCat({"JniPostTask: ", runnable_class_name});
42     ctx.event()->set_name(event_name.c_str());
43   });
44   JNIEnv* env = base::android::AttachCurrentThread();
45   JNI_Runnable::Java_Runnable_run(env, task);
46   if (UNLIKELY(base::android::HasException(env))) {
47     // We can only return control to Java on UI threads (eg. JavaHandlerThread
48     // or the Android Main Thread).
49     if (base::CurrentUIThread::IsSet()) {
50       // Tell the message loop to not perform any tasks after the current one -
51       // we want to make sure we return to Java cleanly without first making any
52       // new JNI calls. This will cause the uncaughtExceptionHandler to catch
53       // and report the Java exception, rather than catching a JNI Exception
54       // with an associated Java stack.
55       base::CurrentUIThread::Get()->Abort();
56     } else {
57       base::android::CheckException(env);
58     }
59   }
60 }
61 
62 }  // namespace
63 
JNI_TaskRunnerImpl_Init(JNIEnv * env,jint task_runner_type,jint task_traits)64 jlong JNI_TaskRunnerImpl_Init(JNIEnv* env,
65                               jint task_runner_type,
66                               jint task_traits) {
67   TaskRunnerAndroid* task_runner =
68       TaskRunnerAndroid::Create(task_runner_type, task_traits).release();
69   return reinterpret_cast<intptr_t>(task_runner);
70 }
71 
TaskRunnerAndroid(scoped_refptr<TaskRunner> task_runner,TaskRunnerType type)72 TaskRunnerAndroid::TaskRunnerAndroid(scoped_refptr<TaskRunner> task_runner,
73                                      TaskRunnerType type)
74     : task_runner_(std::move(task_runner)), type_(type) {}
75 
76 TaskRunnerAndroid::~TaskRunnerAndroid() = default;
77 
Destroy(JNIEnv * env)78 void TaskRunnerAndroid::Destroy(JNIEnv* env) {
79   // This could happen on any thread.
80   delete this;
81 }
82 
PostDelayedTask(JNIEnv * env,const base::android::JavaRef<jobject> & task,jlong delay,jstring runnable_class_name)83 void TaskRunnerAndroid::PostDelayedTask(
84     JNIEnv* env,
85     const base::android::JavaRef<jobject>& task,
86     jlong delay,
87     jstring runnable_class_name) {
88   // This could be run on any java thread, so we can't cache |env| in the
89   // BindOnce because JNIEnv is thread specific.
90   task_runner_->PostDelayedTask(
91       FROM_HERE,
92       base::BindOnce(
93           &RunJavaTask, base::android::ScopedJavaGlobalRef<jobject>(task),
94           android::ConvertJavaStringToUTF8(env, runnable_class_name)),
95       Milliseconds(delay));
96 }
97 
BelongsToCurrentThread(JNIEnv * env)98 bool TaskRunnerAndroid::BelongsToCurrentThread(JNIEnv* env) {
99   // TODO(crbug.com/1026641): Move BelongsToCurrentThread from TaskRunnerImpl to
100   // SequencedTaskRunnerImpl on the Java side too.
101   if (type_ == TaskRunnerType::BASE)
102     return false;
103   return static_cast<SequencedTaskRunner*>(task_runner_.get())
104       ->RunsTasksInCurrentSequence();
105 }
106 
107 // static
Create(jint task_runner_type,jint j_task_traits)108 std::unique_ptr<TaskRunnerAndroid> TaskRunnerAndroid::Create(
109     jint task_runner_type,
110     jint j_task_traits) {
111   TaskTraits task_traits;
112   bool use_thread_pool = true;
113   switch (j_task_traits) {
114     case ::TaskTraits::BEST_EFFORT:
115       task_traits = {TaskPriority::BEST_EFFORT};
116       break;
117     case ::TaskTraits::BEST_EFFORT_MAY_BLOCK:
118       task_traits = {base::MayBlock(), TaskPriority::BEST_EFFORT};
119       break;
120     case ::TaskTraits::USER_VISIBLE:
121       task_traits = {TaskPriority::USER_VISIBLE};
122       break;
123     case ::TaskTraits::USER_VISIBLE_MAY_BLOCK:
124       task_traits = {base::MayBlock(), TaskPriority::USER_VISIBLE};
125       break;
126     case ::TaskTraits::USER_BLOCKING:
127       task_traits = {TaskPriority::USER_BLOCKING};
128       break;
129     case ::TaskTraits::USER_BLOCKING_MAY_BLOCK:
130       task_traits = {base::MayBlock(), TaskPriority::USER_BLOCKING};
131       break;
132     case ::TaskTraits::UI_BEST_EFFORT:
133       [[fallthrough]];
134     case ::TaskTraits::UI_USER_VISIBLE:
135       [[fallthrough]];
136     case ::TaskTraits::UI_USER_BLOCKING:
137       use_thread_pool = false;
138       break;
139   }
140   scoped_refptr<TaskRunner> task_runner;
141   if (use_thread_pool) {
142     switch (static_cast<TaskRunnerType>(task_runner_type)) {
143       case TaskRunnerType::BASE:
144         task_runner = base::ThreadPool::CreateTaskRunner(task_traits);
145         break;
146       case TaskRunnerType::SEQUENCED:
147         task_runner = base::ThreadPool::CreateSequencedTaskRunner(task_traits);
148         break;
149       case TaskRunnerType::SINGLE_THREAD:
150         task_runner = base::ThreadPool::CreateSingleThreadTaskRunner(
151             task_traits, SingleThreadTaskRunnerThreadMode::SHARED);
152         break;
153     }
154   } else {
155     CHECK(static_cast<TaskRunnerType>(task_runner_type) ==
156           TaskRunnerType::SINGLE_THREAD);
157     CHECK(GetUiThreadTaskRunnerCallback());
158     task_runner = GetUiThreadTaskRunnerCallback().Run(
159         static_cast<::TaskTraits>(j_task_traits));
160   }
161   return std::make_unique<TaskRunnerAndroid>(
162       task_runner, static_cast<TaskRunnerType>(task_runner_type));
163 }
164 
165 // static
SetUiThreadTaskRunnerCallback(UiThreadTaskRunnerCallback callback)166 void TaskRunnerAndroid::SetUiThreadTaskRunnerCallback(
167     UiThreadTaskRunnerCallback callback) {
168   GetUiThreadTaskRunnerCallback() = std::move(callback);
169 }
170 
171 }  // namespace base
172