// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/android/task_scheduler/task_runner_android.h" #include #include #include #include "base/android/jni_string.h" #include "base/android_runtime_unchecked_jni_headers/Runnable_jni.h" #include "base/base_jni_headers/TaskRunnerImpl_jni.h" #include "base/check.h" #include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/no_destructor.h" #include "base/strings/strcat.h" #include "base/task/current_thread.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/task/thread_pool/thread_pool_impl.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/time/time.h" #include "base/trace_event/base_tracing.h" namespace base { namespace { TaskRunnerAndroid::UiThreadTaskRunnerCallback& GetUiThreadTaskRunnerCallback() { static base::NoDestructor callback; return *callback; } void RunJavaTask(base::android::ScopedJavaGlobalRef task, const std::string& runnable_class_name) { TRACE_EVENT("toplevel", nullptr, [&](::perfetto::EventContext& ctx) { std::string event_name = base::StrCat({"JniPostTask: ", runnable_class_name}); ctx.event()->set_name(event_name.c_str()); }); JNIEnv* env = base::android::AttachCurrentThread(); JNI_Runnable::Java_Runnable_run(env, task); if (UNLIKELY(base::android::HasException(env))) { // We can only return control to Java on UI threads (eg. JavaHandlerThread // or the Android Main Thread). if (base::CurrentUIThread::IsSet()) { // Tell the message loop to not perform any tasks after the current one - // we want to make sure we return to Java cleanly without first making any // new JNI calls. This will cause the uncaughtExceptionHandler to catch // and report the Java exception, rather than catching a JNI Exception // with an associated Java stack. base::CurrentUIThread::Get()->Abort(); } else { base::android::CheckException(env); } } } } // namespace jlong JNI_TaskRunnerImpl_Init(JNIEnv* env, jint task_runner_type, jint task_traits) { TaskRunnerAndroid* task_runner = TaskRunnerAndroid::Create(task_runner_type, task_traits).release(); return reinterpret_cast(task_runner); } TaskRunnerAndroid::TaskRunnerAndroid(scoped_refptr task_runner, TaskRunnerType type) : task_runner_(std::move(task_runner)), type_(type) {} TaskRunnerAndroid::~TaskRunnerAndroid() = default; void TaskRunnerAndroid::Destroy(JNIEnv* env) { // This could happen on any thread. delete this; } void TaskRunnerAndroid::PostDelayedTask( JNIEnv* env, const base::android::JavaRef& task, jlong delay, jstring runnable_class_name) { // This could be run on any java thread, so we can't cache |env| in the // BindOnce because JNIEnv is thread specific. task_runner_->PostDelayedTask( FROM_HERE, base::BindOnce( &RunJavaTask, base::android::ScopedJavaGlobalRef(task), android::ConvertJavaStringToUTF8(env, runnable_class_name)), Milliseconds(delay)); } bool TaskRunnerAndroid::BelongsToCurrentThread(JNIEnv* env) { // TODO(crbug.com/1026641): Move BelongsToCurrentThread from TaskRunnerImpl to // SequencedTaskRunnerImpl on the Java side too. if (type_ == TaskRunnerType::BASE) return false; return static_cast(task_runner_.get()) ->RunsTasksInCurrentSequence(); } // static std::unique_ptr TaskRunnerAndroid::Create( jint task_runner_type, jint j_task_traits) { TaskTraits task_traits; bool use_thread_pool = true; switch (j_task_traits) { case ::TaskTraits::BEST_EFFORT: task_traits = {TaskPriority::BEST_EFFORT}; break; case ::TaskTraits::BEST_EFFORT_MAY_BLOCK: task_traits = {base::MayBlock(), TaskPriority::BEST_EFFORT}; break; case ::TaskTraits::USER_VISIBLE: task_traits = {TaskPriority::USER_VISIBLE}; break; case ::TaskTraits::USER_VISIBLE_MAY_BLOCK: task_traits = {base::MayBlock(), TaskPriority::USER_VISIBLE}; break; case ::TaskTraits::USER_BLOCKING: task_traits = {TaskPriority::USER_BLOCKING}; break; case ::TaskTraits::USER_BLOCKING_MAY_BLOCK: task_traits = {base::MayBlock(), TaskPriority::USER_BLOCKING}; break; case ::TaskTraits::UI_BEST_EFFORT: [[fallthrough]]; case ::TaskTraits::UI_USER_VISIBLE: [[fallthrough]]; case ::TaskTraits::UI_USER_BLOCKING: use_thread_pool = false; break; } scoped_refptr task_runner; if (use_thread_pool) { switch (static_cast(task_runner_type)) { case TaskRunnerType::BASE: task_runner = base::ThreadPool::CreateTaskRunner(task_traits); break; case TaskRunnerType::SEQUENCED: task_runner = base::ThreadPool::CreateSequencedTaskRunner(task_traits); break; case TaskRunnerType::SINGLE_THREAD: task_runner = base::ThreadPool::CreateSingleThreadTaskRunner( task_traits, SingleThreadTaskRunnerThreadMode::SHARED); break; } } else { CHECK(static_cast(task_runner_type) == TaskRunnerType::SINGLE_THREAD); CHECK(GetUiThreadTaskRunnerCallback()); task_runner = GetUiThreadTaskRunnerCallback().Run( static_cast<::TaskTraits>(j_task_traits)); } return std::make_unique( task_runner, static_cast(task_runner_type)); } // static void TaskRunnerAndroid::SetUiThreadTaskRunnerCallback( UiThreadTaskRunnerCallback callback) { GetUiThreadTaskRunnerCallback() = std::move(callback); } } // namespace base