172 lines
5.9 KiB
C++
172 lines
5.9 KiB
C++
// 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 <array>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#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<TaskRunnerAndroid::UiThreadTaskRunnerCallback>
|
|
callback;
|
|
return *callback;
|
|
}
|
|
|
|
void RunJavaTask(base::android::ScopedJavaGlobalRef<jobject> 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<intptr_t>(task_runner);
|
|
}
|
|
|
|
TaskRunnerAndroid::TaskRunnerAndroid(scoped_refptr<TaskRunner> 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<jobject>& 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<jobject>(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<SequencedTaskRunner*>(task_runner_.get())
|
|
->RunsTasksInCurrentSequence();
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<TaskRunnerAndroid> 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<TaskRunner> task_runner;
|
|
if (use_thread_pool) {
|
|
switch (static_cast<TaskRunnerType>(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<TaskRunnerType>(task_runner_type) ==
|
|
TaskRunnerType::SINGLE_THREAD);
|
|
CHECK(GetUiThreadTaskRunnerCallback());
|
|
task_runner = GetUiThreadTaskRunnerCallback().Run(
|
|
static_cast<::TaskTraits>(j_task_traits));
|
|
}
|
|
return std::make_unique<TaskRunnerAndroid>(
|
|
task_runner, static_cast<TaskRunnerType>(task_runner_type));
|
|
}
|
|
|
|
// static
|
|
void TaskRunnerAndroid::SetUiThreadTaskRunnerCallback(
|
|
UiThreadTaskRunnerCallback callback) {
|
|
GetUiThreadTaskRunnerCallback() = std::move(callback);
|
|
}
|
|
|
|
} // namespace base
|