Commit 7e275b84 authored by Alex Clarke's avatar Alex Clarke Committed by Commit Bot

Implement a Java task scheduler which delegates to the native scheduler when loaded

The task scheduler is usable before native is loaded although only a subset the full native
task prioritisation will be available. After native libraries are loaded, all remaining tasks
will be migrated to native queues.

Design Doc: https://docs.google.com/document/d/1ZRXRypVluu7YvnB9MC73J3Z9NTsYDDxkhUDK4O-siJg/edit?ts=5bb5f398#heading=h.8n30tv8r1tun
And https://docs.google.com/document/d/1z1BDq9vzcEpkhN9LSPF5XMnZ0kLJ8mWWkNAi4OI7cos/edit#heading=h.7nki9mck5t64

Bug: 863341, 872372
Change-Id: I1c71a670662065d1d84cb23493f4cde53db9ae7c
Reviewed-on: https://chromium-review.googlesource.com/c/1268021
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avataragrieve <agrieve@chromium.org>
Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Reviewed-by: default avatarSam Maier <smaier@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602319}
parent a1904eff
...@@ -1312,6 +1312,12 @@ jumbo_component("base") { ...@@ -1312,6 +1312,12 @@ jumbo_component("base") {
"android/statistics_recorder_android.cc", "android/statistics_recorder_android.cc",
"android/sys_utils.cc", "android/sys_utils.cc",
"android/sys_utils.h", "android/sys_utils.h",
"android/task_scheduler/post_task_android.cc",
"android/task_scheduler/post_task_android.h",
"android/task_scheduler/sequenced_task_runner_android.cc",
"android/task_scheduler/single_thread_task_runner_android.cc",
"android/task_scheduler/task_runner_android.cc",
"android/task_scheduler/task_runner_android.h",
"android/time_utils.cc", "android/time_utils.cc",
"android/timezone_utils.cc", "android/timezone_utils.cc",
"android/timezone_utils.h", "android/timezone_utils.h",
...@@ -2852,6 +2858,10 @@ if (is_android) { ...@@ -2852,6 +2858,10 @@ if (is_android) {
"android/java/src/org/chromium/base/metrics/RecordUserAction.java", "android/java/src/org/chromium/base/metrics/RecordUserAction.java",
"android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java", "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
"android/java/src/org/chromium/base/process_launcher/ChildProcessService.java", "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
"android/java/src/org/chromium/base/task/PostTask.java",
"android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java",
"android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java",
"android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
] ]
public_deps = [ public_deps = [
...@@ -2863,7 +2873,10 @@ if (is_android) { ...@@ -2863,7 +2873,10 @@ if (is_android) {
generate_jar_jni("android_runtime_jni_headers") { generate_jar_jni("android_runtime_jni_headers") {
jni_package = "base" jni_package = "base"
classes = [ "java/lang/Runtime.class" ] classes = [
"java/lang/Runnable.class",
"java/lang/Runtime.class",
]
} }
java_library("jni_processor_annotations_java") { java_library("jni_processor_annotations_java") {
...@@ -2971,11 +2984,19 @@ if (is_android) { ...@@ -2971,11 +2984,19 @@ if (is_android) {
"android/java/src/org/chromium/base/memory/MemoryPressureCallback.java", "android/java/src/org/chromium/base/memory/MemoryPressureCallback.java",
"android/java/src/org/chromium/base/memory/MemoryPressureUma.java", "android/java/src/org/chromium/base/memory/MemoryPressureUma.java",
"android/java/src/org/chromium/base/task/AsyncTask.java", "android/java/src/org/chromium/base/task/AsyncTask.java",
"android/java/src/org/chromium/base/task/DefaultTaskExecutor.java",
"android/java/src/org/chromium/base/task/ChromeThreadPoolExecutor.java", "android/java/src/org/chromium/base/task/ChromeThreadPoolExecutor.java",
"android/java/src/org/chromium/base/task/PostTask.java",
"android/java/src/org/chromium/base/task/PreNativeSequence.java",
"android/java/src/org/chromium/base/task/SequencedTaskRunner.java", "android/java/src/org/chromium/base/task/SequencedTaskRunner.java",
"android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java",
"android/java/src/org/chromium/base/task/SerialExecutor.java", "android/java/src/org/chromium/base/task/SerialExecutor.java",
"android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java", "android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java",
"android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java",
"android/java/src/org/chromium/base/task/TaskExecutor.java",
"android/java/src/org/chromium/base/task/TaskRunner.java", "android/java/src/org/chromium/base/task/TaskRunner.java",
"android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
"android/java/src/org/chromium/base/task/TaskTraits.java",
] ]
# New versions of BuildConfig.java and NativeLibraries.java # New versions of BuildConfig.java and NativeLibraries.java
...@@ -3020,6 +3041,10 @@ if (is_android) { ...@@ -3020,6 +3041,10 @@ if (is_android) {
"android/javatests/src/org/chromium/base/StrictModeContextTest.java", "android/javatests/src/org/chromium/base/StrictModeContextTest.java",
"android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java", "android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java",
"android/javatests/src/org/chromium/base/task/AsyncTaskTest.java", "android/javatests/src/org/chromium/base/task/AsyncTaskTest.java",
"android/javatests/src/org/chromium/base/task/PostTaskTest.java",
"android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java",
"android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java",
"android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java",
] ]
} }
...@@ -3062,6 +3087,8 @@ if (is_android) { ...@@ -3062,6 +3087,8 @@ if (is_android) {
"test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegateFactory.java", "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegateFactory.java",
"test/android/javatests/src/org/chromium/base/test/params/ParameterProvider.java", "test/android/javatests/src/org/chromium/base/test/params/ParameterProvider.java",
"test/android/javatests/src/org/chromium/base/test/params/ParameterSet.java", "test/android/javatests/src/org/chromium/base/test/params/ParameterSet.java",
"test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java",
"test/android/javatests/src/org/chromium/base/test/task/TaskSchedulerTestHelpers.java",
"test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java", "test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java",
"test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java", "test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java",
"test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java", "test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java",
...@@ -3165,6 +3192,7 @@ if (is_android) { ...@@ -3165,6 +3192,7 @@ if (is_android) {
"android/library_loader/library_loader_hooks.h", "android/library_loader/library_loader_hooks.h",
"memory/memory_pressure_listener.h", "memory/memory_pressure_listener.h",
"metrics/histogram_base.h", "metrics/histogram_base.h",
"task/task_traits.h",
"trace_event/trace_config.h", "trace_event/trace_config.h",
] ]
} }
......
// Copyright 2018 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.
package org.chromium.base.task;
/**
* The default {@link TaskExecutor} which maps directly to base::TaskScheduler.
*/
class DefaultTaskExecutor implements TaskExecutor {
@Override
public TaskRunner createTaskRunner(TaskTraits taskTraits) {
return new TaskRunnerImpl(taskTraits);
}
@Override
public SequencedTaskRunner createSequencedTaskRunner(TaskTraits taskTraits) {
return new SequencedTaskRunnerImpl(taskTraits);
}
/**
* This maps to a single thread within the native thread pool. Due to that contract we
* can't run tasks posted on it until native has started.
*/
@Override
public SingleThreadTaskRunner createSingleThreadTaskRunner(TaskTraits taskTraits) {
// Tasks posted via this API will not execute until after native has started.
return new SingleThreadTaskRunnerImpl(null, taskTraits);
}
@Override
public void postTask(TaskTraits taskTraits, Runnable task) {
createTaskRunner(taskTraits).postTask(task);
}
}
// Copyright 2018 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.
package org.chromium.base.task;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
/**
* Java interface to the native chromium scheduler. Note tasks can be posted before native
* initialization, but task prioritization is extremely limited. Once the native scheduler
* is ready, tasks will be migrated over.
*/
@JNINamespace("base")
public class PostTask {
private static final Object sLock = new Object();
private static Set<TaskRunner> sPreNativeTaskRunners =
Collections.newSetFromMap(new WeakHashMap<TaskRunner, Boolean>());
private static final TaskExecutor sTaskExecutors[] = getInitialTaskExecutors();
private static TaskExecutor[] getInitialTaskExecutors() {
TaskExecutor taskExecutors[] = new TaskExecutor[TaskTraits.MAX_EXTENSION_ID + 1];
taskExecutors[0] = new DefaultTaskExecutor();
return taskExecutors;
}
/**
* @param traits The TaskTraits that describe the desired TaskRunner.
* @return The TaskRunner for the specified TaskTraits.
*/
public static TaskRunner createTaskRunner(TaskTraits taskTraits) {
synchronized (sLock) {
TaskRunner taskRunner =
getTaskExecutorForTraits(taskTraits).createTaskRunner(taskTraits);
if (sPreNativeTaskRunners != null) {
sPreNativeTaskRunners.add(taskRunner);
} else {
taskRunner.initNativeTaskRunner();
}
return taskRunner;
}
}
/**
* @param traits The TaskTraits that describe the desired TaskRunner.
* @return The TaskRunner for the specified TaskTraits.
*/
public static SequencedTaskRunner createSequencedTaskRunner(TaskTraits taskTraits) {
synchronized (sLock) {
SequencedTaskRunner taskRunner =
getTaskExecutorForTraits(taskTraits).createSequencedTaskRunner(taskTraits);
if (sPreNativeTaskRunners != null) {
sPreNativeTaskRunners.add(taskRunner);
} else {
taskRunner.initNativeTaskRunner();
}
return taskRunner;
}
}
/**
*
* @param traits The TaskTraits that describe the desired TaskRunner.
* @return The TaskRunner for the specified TaskTraits.
*/
public static SingleThreadTaskRunner createSingleThreadTaskRunner(TaskTraits taskTraits) {
synchronized (sLock) {
SingleThreadTaskRunner taskRunner =
getTaskExecutorForTraits(taskTraits).createSingleThreadTaskRunner(taskTraits);
if (sPreNativeTaskRunners != null) {
sPreNativeTaskRunners.add(taskRunner);
} else {
taskRunner.initNativeTaskRunner();
}
return taskRunner;
}
}
/**
* @param taskTraits The TaskTraits that describe the desired TaskRunner.
* @param task The task to be run with the specified traits.
*/
public static void postTask(TaskTraits taskTraits, Runnable task) {
synchronized (sLock) {
if (sPreNativeTaskRunners != null) {
getTaskExecutorForTraits(taskTraits).postTask(taskTraits, task);
} else {
nativePostTask(taskTraits.mPrioritySetExplicitly, taskTraits.mPriority,
taskTraits.mMayBlock, taskTraits.mExtensionId, taskTraits.mExtensionData,
task);
}
}
}
/**
* Registers a TaskExecutor, this must be called before any other usages of this API.
*
* @param extensionId The id associated with the TaskExecutor.
* @param taskExecutor The TaskExecutor to be registered. Must not equal zero.
*/
public static void registerTaskExecutor(byte extensionId, TaskExecutor taskExecutor) {
synchronized (sLock) {
assert extensionId != 0;
assert extensionId <= TaskTraits.MAX_EXTENSION_ID;
assert sTaskExecutors[extensionId] == null;
sTaskExecutors[extensionId] = taskExecutor;
}
}
private static TaskExecutor getTaskExecutorForTraits(TaskTraits traits) {
return sTaskExecutors[traits.mExtensionId];
}
@CalledByNative
private static void onNativeTaskSchedulerReady() {
synchronized (sLock) {
for (TaskRunner taskRunner : sPreNativeTaskRunners) {
taskRunner.initNativeTaskRunner();
}
sPreNativeTaskRunners = null;
}
}
// This is here to make C++ tests work.
@CalledByNative
private static void onNativeTaskSchedulerShutdown() {
synchronized (sLock) {
sPreNativeTaskRunners =
Collections.newSetFromMap(new WeakHashMap<TaskRunner, Boolean>());
}
}
private static native void nativePostTask(boolean prioritySetExplicitly, int priority,
boolean mayBlock, byte extensionId, byte[] extensionData, Runnable task);
}
// Copyright 2018 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.
package org.chromium.base.task;
import org.chromium.base.TraceEvent;
import java.util.ArrayDeque;
/**
* Common implementation of a pre-native task runner which runs on either a thread pool or
* on a Handler, which can migrate posted tasks to native APIs.
*/
abstract class PreNativeSequence implements Runnable {
protected final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
private final String mTraceCategory;
private boolean mTaskRunning;
private boolean mMigrateToNative;
/**
* @param traceCategory The trace category to use when emitting trace events for tasks posted
* on this sequence.
*/
PreNativeSequence(String traceCategory) {
mTraceCategory = traceCategory;
}
/**
* @param task The task to be enqueued and run sequentially.
*/
synchronized void postTask(Runnable task) {
boolean wasEmpty = mTasks.isEmpty();
mTasks.offer(task);
if (wasEmpty && !mTaskRunning) scheduleNext();
}
/**
* Ensures the next task from this sequence is run via native APIs.
*/
synchronized void requestMigrateToNative() {
if (mTaskRunning) {
mMigrateToNative = true;
} else {
migrateToNative();
}
}
@Override
public void run() {
try (TraceEvent te = TraceEvent.scoped(mTraceCategory)) {
Runnable activeTask;
synchronized (this) {
if (mMigrateToNative) {
migrateToNative();
return;
}
activeTask = mTasks.poll();
mTaskRunning = true;
}
// Avoid holding the lock for too long, to allow other threads to postTask.
if (activeTask != null) activeTask.run();
synchronized (this) {
mTaskRunning = false;
if (!mTasks.isEmpty()) scheduleNext();
}
}
}
protected abstract void scheduleNext();
protected abstract void migrateToNative();
}
// Copyright 2018 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.
package org.chromium.base.task;
import org.chromium.base.annotations.JNINamespace;
import javax.annotation.Nullable;
/**
* Implementation of the abstract class {@link SequencedTaskRunner}. Uses AsyncTasks until
* native APIs are available.
*/
@JNINamespace("base")
public class SequencedTaskRunnerImpl implements SequencedTaskRunner {
private final Object mLock = new Object();
@Nullable
private final TaskTraits mTaskTraits;
private long mNativeTaskRunnerAndroid;
@Nullable
private PreNativeSequence mPreNativeSequence;
/**
* @param traits The TaskTraits associated with this SequencedTaskRunnerImpl.
*/
SequencedTaskRunnerImpl(TaskTraits traits) {
mTaskTraits = traits;
}
@Override
protected void finalize() {
if (mNativeTaskRunnerAndroid != 0) nativeFinalize(mNativeTaskRunnerAndroid);
}
@Override
public void initNativeTaskRunner() {
synchronized (mLock) {
mNativeTaskRunnerAndroid = nativeInit(mTaskTraits.mPrioritySetExplicitly,
mTaskTraits.mPriority, mTaskTraits.mMayBlock, mTaskTraits.mExtensionId,
mTaskTraits.mExtensionData);
if (mPreNativeSequence != null) mPreNativeSequence.migrateToNative();
}
}
@Override
public void postTask(Runnable task) {
synchronized (mLock) {
if (mNativeTaskRunnerAndroid != 0) {
nativePostTask(mNativeTaskRunnerAndroid, task);
} else {
if (mPreNativeSequence == null) mPreNativeSequence = new PreNativeImpl();
mPreNativeSequence.postTask(task);
}
}
}
private class PreNativeImpl extends PreNativeSequence {
PreNativeImpl() {
super("SequencedTaskRunnerImpl.PreNativeImpl.run");
}
@Override
protected void scheduleNext() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(this);
}
@Override
protected void migrateToNative() {
while (!mTasks.isEmpty()) {
nativePostTask(mNativeTaskRunnerAndroid, mTasks.poll());
}
mPreNativeSequence = null;
}
}
// NB due to Proguard obfuscation it's easiest to pass the traits via arguments.
private static native long nativeInit(boolean prioritySetExplicitly, int priority,
boolean mayBlock, byte extensionId, byte[] extensionData);
private native void nativeFinalize(long nativeTaskRunnerAndroid);
private native void nativePostTask(long nativeTaskRunnerAndroid, Runnable task);
}
// Copyright 2018 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.
package org.chromium.base.task;
import android.os.Handler;
import org.chromium.base.annotations.JNINamespace;
import javax.annotation.Nullable;
/**
* Implementation of the abstract class {@link SingleThreadTaskRunner}. Before native initialization
* tasks are posted to the {@link java android.os.Handler}, after native initialization they're
* posted to a base::SingleThreadTaskRunner which runs on the same thread.
*/
@JNINamespace("base")
public class SingleThreadTaskRunnerImpl implements SingleThreadTaskRunner {
private final Object mLock = new Object();
@Nullable
private final Handler mHandler;
@Nullable
private final TaskTraits mTaskTraits;
private long mNativeTaskRunnerAndroid;
@Nullable
private PreNativeSequence mPreNativeSequence;
/**
* @param handler The backing Handler if any. Note this must run tasks on the same thread that
* the native code runs a task with |traits|. If handler is null then tasks won't run until
* native has initialized.
* @param traits The TaskTraits associated with this SingleThreadTaskRunnerImpl.
*/
public SingleThreadTaskRunnerImpl(Handler handler, TaskTraits traits) {
mHandler = handler;
mTaskTraits = traits;
}
@Override
protected void finalize() {
if (mNativeTaskRunnerAndroid != 0) nativeFinalize(mNativeTaskRunnerAndroid);
}
@Override
public void initNativeTaskRunner() {
synchronized (mLock) {
mNativeTaskRunnerAndroid = nativeInit(mTaskTraits.mPrioritySetExplicitly,
mTaskTraits.mPriority, mTaskTraits.mMayBlock, mTaskTraits.mExtensionId,
mTaskTraits.mExtensionData);
if (mPreNativeSequence != null) mPreNativeSequence.migrateToNative();
}
}
@Override
public void postTask(Runnable task) {
synchronized (mLock) {
if (mNativeTaskRunnerAndroid != 0) {
nativePostTask(mNativeTaskRunnerAndroid, task);
} else {
if (mPreNativeSequence == null) mPreNativeSequence = new PreNativeImpl();
mPreNativeSequence.postTask(task);
}
}
}
private class PreNativeImpl extends PreNativeSequence {
PreNativeImpl() {
super("SingleThreadTaskRunnerImpl.PreNativeImpl.run");
}
@Override
protected void scheduleNext() {
// if |mHandler| is null then pre-native task execution is not supported.
if (mHandler != null) mHandler.post(this);
}
/**
* This is only called in the context of PreNativeSequence.initNativeTaskRunner and
* PreNativeSequence.postTask which are synchronized hence this doesn't need additional
* synchronization.
*/
@Override
protected void migrateToNative() {
while (!mTasks.isEmpty()) {
nativePostTask(mNativeTaskRunnerAndroid, mTasks.poll());
}
mPreNativeSequence = null;
}
}
// NB due to Proguard obfuscation it's easiest to pass the traits via arguments.
private static native long nativeInit(boolean prioritySetExplicitly, int priority,
boolean mayBlock, byte extensionId, byte[] extensionData);
private native void nativeFinalize(long nativeTaskRunnerAndroid);
private native void nativePostTask(long nativeTaskRunnerAndroid, Runnable task);
}
// Copyright 2018 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.
package org.chromium.base.task;
/**
* The Java equivalent of base::TaskExecutor, which can execute Tasks with a specific TaskTraits
* id. To handle tasks posted via the PostTask API, the TaskExecutor should be registered by
* calling {@link PostTask.registerTaskExecutor}.
*/
public interface TaskExecutor {
/**
* @param traits The TaskTraits that describe the desired TaskRunner.
* @param task The task to be run with the specified traits.
*/
public void postTask(TaskTraits traits, Runnable task);
/**
* @param traits The TaskTraits that describe the desired TaskRunner.
* @return The TaskRunner for the specified TaskTraits.
*/
public TaskRunner createTaskRunner(TaskTraits traits);
/**
* @param traits The TaskTraits that describe the desired TaskRunner.
* @return The TaskRunner for the specified TaskTraits.
*/
public SequencedTaskRunner createSequencedTaskRunner(TaskTraits traits);
/**
* @param traits The TaskTraits that describe the desired TaskRunner.
* @return The TaskRunner for the specified TaskTraits.
*/
public SingleThreadTaskRunner createSingleThreadTaskRunner(TaskTraits traits);
}
// Copyright 2018 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.
package org.chromium.base.task;
import org.chromium.base.TraceEvent;
import org.chromium.base.annotations.JNINamespace;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
/**
* Implementation of the abstract class {@link TaskRunnerImpl}. Uses AsyncTasks until
* native APIs are available.
*/
@JNINamespace("base")
public class TaskRunnerImpl implements TaskRunner {
@Nullable
private final TaskTraits mTaskTraits;
private final Object mLock = new Object();
private long mNativeTaskRunnerAndroid;
@Nullable
private List<PreNativeTask> mPreNativeTasks = new ArrayList<>();
/**
* @param traits The TaskTraits associated with this TaskRunnerImpl.
*/
TaskRunnerImpl(TaskTraits traits) {
mTaskTraits = traits;
}
@Override
protected void finalize() {
if (mNativeTaskRunnerAndroid != 0) nativeFinalize(mNativeTaskRunnerAndroid);
}
@Override
public void postTask(Runnable task) {
synchronized (mLock) {
if (mNativeTaskRunnerAndroid != 0) {
nativePostTask(mNativeTaskRunnerAndroid, task);
return;
}
// We don't expect a whole lot of these, if that changes consider pooling them.
PreNativeTask preNativeTask = new PreNativeTask(task);
mPreNativeTasks.add(preNativeTask);
AsyncTask.THREAD_POOL_EXECUTOR.execute(preNativeTask);
}
}
private class PreNativeTask implements Runnable {
PreNativeTask(Runnable task) {
this.mTask = task;
}
@Override
public void run() {
try (TraceEvent te = TraceEvent.scoped("TaskRunnerImpl.PreNativeTask.run")) {
synchronized (mLock) {
if (mPreNativeTasks == null) return;
mPreNativeTasks.remove(this);
}
mTask.run();
}
}
final Runnable mTask;
}
@Override
public void initNativeTaskRunner() {
synchronized (mLock) {
mNativeTaskRunnerAndroid = nativeInit(mTaskTraits.mPrioritySetExplicitly,
mTaskTraits.mPriority, mTaskTraits.mMayBlock, mTaskTraits.mExtensionId,
mTaskTraits.mExtensionData);
for (PreNativeTask task : mPreNativeTasks) {
nativePostTask(mNativeTaskRunnerAndroid, task.mTask);
}
mPreNativeTasks = null;
}
}
// NB due to Proguard obfuscation it's easiest to pass the traits via arguments.
private static native long nativeInit(boolean prioritySetExplicitly, int priority,
boolean mayBlock, byte extensionId, byte[] extensionData);
private native void nativeFinalize(long nativeTaskRunnerAndroid);
private native void nativePostTask(long nativeTaskRunnerAndroid, Runnable task);
}
// Copyright 2018 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.
package org.chromium.base.task;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* TaskTraits are metadata that influence how the TaskSecheduler deals with that task.
* E.g. the trait can directly or indirectly control task prioritization.
*/
public class TaskTraits {
// Keep in sync with base::TaskTraitsExtensionStorage:: kInvalidExtensionId
public static final byte INVALID_EXTENSION_ID = 0;
// Keep in sync with base::TaskTraitsExtensionStorage::kMaxExtensionId
public static final int MAX_EXTENSION_ID = 4;
// Keep in sync with base::TaskTraitsExtensionStorage::kStorageSize
public static final int EXTENSION_STORAGE_SIZE = 8;
public TaskTraits() {}
public TaskTraits setTaskPriority(int taskPriority) {
mPrioritySetExplicitly = true;
mPriority = taskPriority;
return this;
}
/**
* Tasks with this trait may block. This includes but is not limited to tasks that wait on
* synchronous file I/O operations: read or write a file from disk, interact with a pipe or a
* socket, rename or delete a file, enumerate files in a directory, etc. This trait isn't
* required for the mere use of locks.
*/
public TaskTraits setMayBlock(boolean mayBlock) {
mMayBlock = mayBlock;
return this;
}
// For convenience of the JNI code, we use primitive types only.
// Note shutdown behavior is not supported on android.
boolean mPrioritySetExplicitly;
int mPriority = TaskPriority.USER_VISIBLE;
boolean mMayBlock;
byte mExtensionId = INVALID_EXTENSION_ID;
byte mExtensionData[];
protected void setExtensionId(byte extensionId) {
mExtensionId = extensionId;
}
protected void setExtensionData(byte[] extensionData) {
mExtensionData = extensionData;
}
@Override
public boolean equals(@Nullable Object object) {
if (object == this) {
return true;
} else if (object instanceof TaskTraits) {
TaskTraits other = (TaskTraits) object;
return mPrioritySetExplicitly == other.mPrioritySetExplicitly
&& mPriority == other.mPriority && mExtensionId == other.mExtensionId
&& Arrays.equals(mExtensionData, other.mExtensionData);
} else {
return false;
}
}
@Override
public int hashCode() {
int hash = 31;
hash = 37 * hash + (mPrioritySetExplicitly ? 0 : 1);
hash = 37 * hash + mPriority;
hash = 37 * hash + (mMayBlock ? 0 : 1);
hash = 37 * hash + (int) mExtensionId;
hash = 37 * hash + Arrays.hashCode(mExtensionData);
return hash;
}
}
// Copyright 2018 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.
package org.chromium.base.task;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.junit.Assert.assertNotNull;
import android.annotation.TargetApi;
import android.os.Build;
import android.support.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.task.SchedulerTestHelpers;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Test class for {@link PostTask}.
*
* Note due to layering concerns we can't test post native functionality in a
* base javatest. Instead see:
* content/public/android/javatests/src/org/chromium/content/browser/scheduler/
* TaskSchedulerTest.java
*/
@RunWith(BaseJUnit4ClassRunner.class)
@MinAndroidSdkLevel(23)
@TargetApi(Build.VERSION_CODES.M)
public class PostTaskTest {
@Test
@SmallTest
public void testPreNativePostTask() {
// This test should not timeout.
final Object lock = new Object();
final AtomicBoolean taskExecuted = new AtomicBoolean();
PostTask.postTask(new TaskTraits(), new Runnable() {
@Override
public void run() {
synchronized (lock) {
taskExecuted.set(true);
lock.notify();
}
}
});
synchronized (lock) {
try {
while (!taskExecuted.get()) {
lock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
@Test
@SmallTest
public void testCreateSingleThreadTaskRunner() throws Exception {
TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
// A SingleThreadTaskRunner with default traits will run in the native thread pool
// and tasks posted won't run until after the native library has loaded.
assertNotNull(taskQueue);
}
@Test
@SmallTest
public void testCreateSequencedTaskRunner() throws Exception {
TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
assertThat(orderList, contains(1, 2, 3));
}
@Test
@SmallTest
public void testCreateTaskRunner() throws Exception {
TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits());
// This should not timeout.
SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
}
}
// Copyright 2018 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.
package org.chromium.base.task;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import android.support.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.task.SchedulerTestHelpers;
import java.util.ArrayList;
import java.util.List;
/**
* Test class for {@link SequencedTaskRunnerImpl}.
*
* Note due to layering concerns we can't test post native functionality in a
* base javatest. Instead see:
* content/public/android/javatests/src/org/chromium/content/browser/scheduler/
* TaskSchedulerTest.java
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class SequencedTaskRunnerImplTest {
@Test
@SmallTest
public void testPreNativeTasksRunInOrder() {
TaskRunner taskQueue = new SequencedTaskRunnerImpl(new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
assertThat(orderList, contains(1, 2, 3));
}
}
// Copyright 2018 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.
package org.chromium.base.task;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.support.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.task.SchedulerTestHelpers;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import java.util.ArrayList;
import java.util.List;
/**
* Test class for {@link SingleThreadTaskRunnerImpl}.
*
* Note due to layering concerns we can't test post native functionality in a
* base javatest. Instead see:
* content/public/android/javatests/src/org/chromium/content/browser/scheduler/
* TaskSchedulerTest.java
*/
@RunWith(BaseJUnit4ClassRunner.class)
@MinAndroidSdkLevel(23)
@TargetApi(Build.VERSION_CODES.M)
public class SingleThreadTaskRunnerImplTest {
@Before
public void setUp() throws Exception {
mHandlerThread = new HandlerThread("SingleThreadTaskRunnerImplTest thread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}
@After
@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
public void tearDown() throws InterruptedException {
Looper looper = mHandlerThread.getLooper();
if (looper != null) {
looper.quitSafely();
}
mHandlerThread.join();
}
private HandlerThread mHandlerThread;
private Handler mHandler;
@Test
@SmallTest
public void testPreNativePostTask() {
TaskRunner taskQueue = new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
SchedulerTestHelpers.preNativeRunUntilIdle(mHandlerThread);
assertThat(orderList, contains(1, 2, 3));
}
}
// Copyright 2018 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.
package org.chromium.base.task;
import android.support.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.task.SchedulerTestHelpers;
/**
* Test class for {@link TaskRunner}.
*
* Note due to layering concerns we can't test post native functionality in a
* base javatest. Instead see:
* content/public/android/javatests/src/org/chromium/content/browser/scheduler/
* TaskSchedulerTest.java
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class TaskRunnerImplTest {
@Test
@SmallTest
public void testPreNativePostTask() {
TaskRunner taskQueue = new TaskRunnerImpl(new TaskTraits());
// This should not time out.
SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
}
}
// Copyright 2018 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 "base/android/task_scheduler/post_task_android.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/task/task_scheduler/task_scheduler.h"
#include "jni/PostTask_jni.h"
#include "jni/Runnable_jni.h"
namespace base {
// static
void PostTaskAndroid::SignalNativeSchedulerReady() {
Java_PostTask_onNativeTaskSchedulerReady(
base::android::AttachCurrentThread());
}
// static
void PostTaskAndroid::SignalNativeSchedulerShutdown() {
Java_PostTask_onNativeTaskSchedulerShutdown(
base::android::AttachCurrentThread());
}
namespace {
std::array<uint8_t, TaskTraitsExtensionStorage::kStorageSize> GetExtensionData(
JNIEnv* env,
const base::android::JavaParamRef<jbyteArray>& array_object) {
if (env->IsSameObject(array_object, nullptr))
return std::array<uint8_t, TaskTraitsExtensionStorage::kStorageSize>();
jbyteArray array = static_cast<jbyteArray>(array_object);
DCHECK_EQ(env->GetArrayLength(array),
static_cast<jsize>(TaskTraitsExtensionStorage::kStorageSize));
std::array<uint8_t, TaskTraitsExtensionStorage::kStorageSize> result;
jbyte* src_bytes = env->GetByteArrayElements(array, nullptr);
memcpy(&result[0], src_bytes, TaskTraitsExtensionStorage::kStorageSize);
env->ReleaseByteArrayElements(array, src_bytes, JNI_ABORT);
return result;
}
} // namespace
// static
TaskTraits PostTaskAndroid::CreateTaskTraits(
JNIEnv* env,
jboolean priority_set_explicitly,
jint priority,
jboolean may_block,
jbyte extension_id,
const base::android::JavaParamRef<jbyteArray>& extension_data) {
return TaskTraits(priority_set_explicitly,
static_cast<TaskPriority>(priority),
/* shutdown_behavior_set_explicitly */ false,
TaskShutdownBehavior::SKIP_ON_SHUTDOWN, may_block,
/* with_base_sync_primitives */ false,
TaskTraitsExtensionStorage(
extension_id, GetExtensionData(env, extension_data)));
}
void JNI_PostTask_PostTask(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& jcaller,
jboolean priority_set_explicitly,
jint priority,
jboolean may_block,
jbyte extension_id,
const base::android::JavaParamRef<jbyteArray>& extension_data,
const base::android::JavaParamRef<jobject>& task) {
// This could be run on any java thread, so we can't cache |env| in the
// BindOnce because JNIEnv is thread specific.
PostTaskWithTraits(
FROM_HERE,
PostTaskAndroid::CreateTaskTraits(env, priority_set_explicitly, priority,
may_block, extension_id,
extension_data),
BindOnce(&PostTaskAndroid::RunJavaTask,
base::android::ScopedJavaGlobalRef<jobject>(task)));
}
// static
void PostTaskAndroid::RunJavaTask(
base::android::ScopedJavaGlobalRef<jobject> task) {
// JNIEnv is thread specific, but we don't know which thread we'll be run on
// so we must look it up.
JNI_Runnable::Java_Runnable_run(base::android::AttachCurrentThread(), task);
}
} // namespace base
// Copyright 2018 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.
#ifndef BASE_ANDROID_TASK_SCHEDULER_POST_TASK_ANDROID_H_
#define BASE_ANDROID_TASK_SCHEDULER_POST_TASK_ANDROID_H_
#include "base/android/jni_weak_ref.h"
#include "base/base_export.h"
#include "base/task/task_traits.h"
namespace base {
// C++ interface for PostTask.java
class BASE_EXPORT PostTaskAndroid {
public:
// Routes Java tasks posted via TaskScheduler APIs through the C++
// TaskScheduler.
static void SignalNativeSchedulerReady();
// Signals that the C++ scheduler has shutdown. Needed to make unit tests that
// repeatedly create and destroy the scheduler work.
static void SignalNativeSchedulerShutdown();
static TaskTraits CreateTaskTraits(
JNIEnv* env,
jboolean priority_set_explicitly,
jint priority,
jboolean may_block,
jbyte extension_id,
const base::android::JavaParamRef<jbyteArray>& extension_data);
// We don't know ahead of time which thread this will run on so it looks up
// the JNI environment and the bindings dynamically (albeit with caching).
static void RunJavaTask(base::android::ScopedJavaGlobalRef<jobject> task);
DISALLOW_COPY_AND_ASSIGN(PostTaskAndroid);
};
} // namespace base
#endif // BASE_ANDROID_TASK_SCHEDULER_POST_TASK_ANDROID_H_
// Copyright 2018 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 "base/android/task_scheduler/post_task_android.h"
#include "base/android/task_scheduler/task_runner_android.h"
#include "base/task/post_task.h"
#include "jni/SequencedTaskRunnerImpl_jni.h"
namespace base {
jlong JNI_SequencedTaskRunnerImpl_Init(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& jcaller,
jboolean priority_set_explicitly,
jint priority,
jboolean may_block,
jbyte extension_id,
const base::android::JavaParamRef<jbyteArray>& extension_data) {
return reinterpret_cast<intptr_t>(new TaskRunnerAndroid(
CreateSequencedTaskRunnerWithTraits(PostTaskAndroid::CreateTaskTraits(
env, priority_set_explicitly, priority, may_block, extension_id,
extension_data))));
}
} // namespace base
// Copyright 2018 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 "base/android/task_scheduler/post_task_android.h"
#include "base/android/task_scheduler/task_runner_android.h"
#include "base/task/post_task.h"
#include "jni/SingleThreadTaskRunnerImpl_jni.h"
namespace base {
jlong JNI_SingleThreadTaskRunnerImpl_Init(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& jcaller,
jboolean priority_set_explicitly,
jint priority,
jboolean may_block,
jbyte extension_id,
const base::android::JavaParamRef<jbyteArray>& extension_data) {
return reinterpret_cast<intptr_t>(new TaskRunnerAndroid(
CreateSingleThreadTaskRunnerWithTraits(PostTaskAndroid::CreateTaskTraits(
env, priority_set_explicitly, priority, may_block, extension_id,
extension_data))));
}
} // namespace base
// Copyright 2018 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 "base/android/task_scheduler/task_runner_android.h"
#include "base/android/task_scheduler/post_task_android.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "jni/TaskRunnerImpl_jni.h"
namespace base {
jlong JNI_TaskRunnerImpl_Init(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& jcaller,
jboolean priority_set_explicitly,
jint priority,
jboolean may_block,
jbyte extension_id,
const base::android::JavaParamRef<jbyteArray>& extension_data) {
return reinterpret_cast<intptr_t>(new TaskRunnerAndroid(
CreateTaskRunnerWithTraits(PostTaskAndroid::CreateTaskTraits(
env, priority_set_explicitly, priority, may_block, extension_id,
extension_data))));
}
TaskRunnerAndroid::TaskRunnerAndroid(scoped_refptr<TaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
TaskRunnerAndroid::~TaskRunnerAndroid() = default;
void TaskRunnerAndroid::Finalize(
JNIEnv* env,
const base::android::JavaRef<jobject>& caller) {
// This will happen on the Java finalizer thread.
delete this;
}
void TaskRunnerAndroid::PostTask(JNIEnv* env,
const base::android::JavaRef<jobject>& caller,
const base::android::JavaRef<jobject>& task) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PostTaskAndroid::RunJavaTask,
base::android::ScopedJavaGlobalRef<jobject>(task)));
}
} // namespace base
// Copyright 2018 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.
#ifndef BASE_ANDROID_TASK_SCHEDULER_TASK_RUNNER_ANDROID_H_
#define BASE_ANDROID_TASK_SCHEDULER_TASK_RUNNER_ANDROID_H_
#include "base/android/jni_weak_ref.h"
#include "base/single_thread_task_runner.h"
namespace base {
// Native implementation backing TaskRunnerImpl.java which posts java tasks onto
// a C++ TaskRunner.
class TaskRunnerAndroid {
public:
explicit TaskRunnerAndroid(scoped_refptr<TaskRunner> task_runner);
~TaskRunnerAndroid();
void Finalize(JNIEnv* env, const base::android::JavaRef<jobject>& caller);
void PostTask(JNIEnv* env,
const base::android::JavaRef<jobject>& caller,
const base::android::JavaRef<jobject>& task);
private:
const scoped_refptr<TaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(TaskRunnerAndroid);
};
} // namespace base
#endif // BASE_ANDROID_TASK_SCHEDULER_TASK_RUNNER_ANDROID_H_
...@@ -37,6 +37,7 @@ namespace base { ...@@ -37,6 +37,7 @@ namespace base {
class HistogramBase; class HistogramBase;
class SchedulerWorkerObserver; class SchedulerWorkerObserver;
class TaskSchedulerTestHelpers;
// Interface for a task scheduler and static methods to manage the instance used // Interface for a task scheduler and static methods to manage the instance used
// by the post_task.h API. // by the post_task.h API.
...@@ -201,6 +202,7 @@ class BASE_EXPORT TaskScheduler : public TaskExecutor { ...@@ -201,6 +202,7 @@ class BASE_EXPORT TaskScheduler : public TaskExecutor {
static TaskScheduler* GetInstance(); static TaskScheduler* GetInstance();
private: private:
friend class TaskSchedulerTestHelpers;
friend class gin::V8Platform; friend class gin::V8Platform;
friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test; friend class content::BrowserMainLoopTest_CreateThreadsInSingleProcess_Test;
......
...@@ -20,10 +20,13 @@ ...@@ -20,10 +20,13 @@
namespace base { namespace base {
class PostTaskAndroid;
// Valid priorities supported by the task scheduler. Note: internal algorithms // Valid priorities supported by the task scheduler. Note: internal algorithms
// depend on priorities being expressed as a continuous zero-based list from // depend on priorities being expressed as a continuous zero-based list from
// lowest to highest priority. Users of this API shouldn't otherwise care about // lowest to highest priority. Users of this API shouldn't otherwise care about
// nor use the underlying values. // nor use the underlying values.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.task
enum class TaskPriority { enum class TaskPriority {
// This will always be equal to the lowest priority available. // This will always be equal to the lowest priority available.
LOWEST = 0, LOWEST = 0,
...@@ -246,6 +249,26 @@ class BASE_EXPORT TaskTraits { ...@@ -246,6 +249,26 @@ class BASE_EXPORT TaskTraits {
} }
private: private:
friend PostTaskAndroid;
// For use by PostTaskAndroid.
TaskTraits(bool priority_set_explicitly,
TaskPriority priority,
bool shutdown_behavior_set_explicitly,
TaskShutdownBehavior shutdown_behavior,
bool may_block,
bool with_base_sync_primitives,
TaskTraitsExtensionStorage extension)
: extension_(extension),
priority_(priority),
shutdown_behavior_(shutdown_behavior),
priority_set_explicitly_(priority_set_explicitly),
shutdown_behavior_set_explicitly_(shutdown_behavior_set_explicitly),
may_block_(may_block),
with_base_sync_primitives_(with_base_sync_primitives) {
static_assert(sizeof(TaskTraits) == 24, "Keep this constructor up to date");
}
constexpr TaskTraits(const TaskTraits& left, const TaskTraits& right) constexpr TaskTraits(const TaskTraits& left, const TaskTraits& right)
: extension_(right.extension_.extension_id != : extension_(right.extension_.extension_id !=
TaskTraitsExtensionStorage::kInvalidExtensionId TaskTraitsExtensionStorage::kInvalidExtensionId
......
...@@ -128,7 +128,9 @@ namespace base { ...@@ -128,7 +128,9 @@ namespace base {
// this data directly (rather than in a separate object on the heap) to support // this data directly (rather than in a separate object on the heap) to support
// constexpr-compatible TaskTraits construction. // constexpr-compatible TaskTraits construction.
struct BASE_EXPORT TaskTraitsExtensionStorage { struct BASE_EXPORT TaskTraitsExtensionStorage {
static constexpr size_t kStorageSize = 8; // bytes // Size in bytes.
// Keep in sync with org.chromium.base.task.TaskTraits.EXTENSION_STORAGE_SIZE
static constexpr size_t kStorageSize = 8;
inline constexpr TaskTraitsExtensionStorage(); inline constexpr TaskTraitsExtensionStorage();
inline constexpr TaskTraitsExtensionStorage( inline constexpr TaskTraitsExtensionStorage(
...@@ -146,11 +148,13 @@ struct BASE_EXPORT TaskTraitsExtensionStorage { ...@@ -146,11 +148,13 @@ struct BASE_EXPORT TaskTraitsExtensionStorage {
inline bool operator==(const TaskTraitsExtensionStorage& other) const; inline bool operator==(const TaskTraitsExtensionStorage& other) const;
enum ExtensionId : uint8_t { enum ExtensionId : uint8_t {
// Keep in sync with org.chromium.base.task.TaskTraits.INVALID_EXTENSION_ID
kInvalidExtensionId = 0, kInvalidExtensionId = 0,
// The embedder is responsible for assigning the remaining values uniquely. // The embedder is responsible for assigning the remaining values uniquely.
kFirstEmbedderExtensionId = 1, kFirstEmbedderExtensionId = 1,
// Maximum number of extension types is artificially limited to support // Maximum number of extension types is artificially limited to support
// super efficient TaskExecutor lookup in post_task.cc. // super efficient TaskExecutor lookup in post_task.cc.
// Keep in sync with org.chromium.base.TaskTraits.MAX_EXTENSION_ID
kMaxExtensionId = 4 kMaxExtensionId = 4
}; };
......
...@@ -116,6 +116,7 @@ static_library("test_support") { ...@@ -116,6 +116,7 @@ static_library("test_support") {
"simple_test_tick_clock.h", "simple_test_tick_clock.h",
"task_runner_test_template.cc", "task_runner_test_template.cc",
"task_runner_test_template.h", "task_runner_test_template.h",
"task_scheduler_test_helpers_android.cc",
"test_discardable_memory_allocator.cc", "test_discardable_memory_allocator.cc",
"test_discardable_memory_allocator.h", "test_discardable_memory_allocator.h",
"test_file_util.cc", "test_file_util.cc",
...@@ -412,6 +413,7 @@ if (is_android) { ...@@ -412,6 +413,7 @@ if (is_android) {
sources = [ sources = [
"android/java/src/org/chromium/base/MainReturnCodeResult.java", "android/java/src/org/chromium/base/MainReturnCodeResult.java",
"android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java", "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
"android/javatests/src/org/chromium/base/test/task/TaskSchedulerTestHelpers.java",
"android/javatests/src/org/chromium/base/test/util/UrlUtils.java", "android/javatests/src/org/chromium/base/test/util/UrlUtils.java",
] ]
jni_package = "base" jni_package = "base"
......
// Copyright 2018 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.
package org.chromium.base.test.task;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.MessageQueue;
import org.chromium.base.task.TaskRunner;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Collection of helpers for testing the java PostTask.
*/
@MinAndroidSdkLevel(23)
@TargetApi(Build.VERSION_CODES.M)
public class SchedulerTestHelpers {
public static void postRecordOrderTask(
TaskRunner taskQueue, List<Integer> orderList, int order) {
taskQueue.postTask(new Runnable() {
@Override
public void run() {
orderList.add(order);
}
});
}
public static void postTaskAndBlockUntilRun(TaskRunner taskQueue) {
final Object lock = new Object();
final AtomicBoolean taskExecuted = new AtomicBoolean();
taskQueue.postTask(new Runnable() {
@Override
public void run() {
synchronized (lock) {
taskExecuted.set(true);
lock.notify();
}
}
});
synchronized (lock) {
try {
while (!taskExecuted.get()) {
lock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
/**
* A helper which posts a task on the handler which when run blocks until unblock() is called.
*/
public static class HandlerBlocker {
final Handler mHandler;
final Object mLock = new Object();
final AtomicBoolean mTaskExecuted = new AtomicBoolean();
public HandlerBlocker(Handler handler) {
mHandler = handler;
}
/**
* Posts a task that blocks until unblock() is called.
*/
public void postBlockingTask() {
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mLock) {
try {
while (!mTaskExecuted.get()) {
mLock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
});
}
public void unblock() {
synchronized (mLock) {
mTaskExecuted.set(true);
mLock.notify();
}
}
};
/**
* Waits until the looper's MessageQueue becomes idle.
*/
public static void preNativeRunUntilIdle(HandlerThread handlerThread) {
final MessageQueue messageQueue = handlerThread.getLooper().getQueue();
// This API was added in sdk level 23.
if (messageQueue.isIdle()) {
return;
}
final Object lock = new Object();
final AtomicBoolean taskExecuted = new AtomicBoolean();
messageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
synchronized (lock) {
taskExecuted.set(true);
lock.notify();
}
return false;
}
});
synchronized (lock) {
try {
while (!taskExecuted.get()) {
lock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
// Copyright 2018 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.
package org.chromium.base.test.task;
/** Helpers that allow base::TaskScheduler to be initialized or shutdown for testing. */
public class TaskSchedulerTestHelpers {
/**
* Initializes base::TaskScheduler with default params.
*/
public static void enableTaskSchedulerExecutionForTesting() {
nativeEnableTaskSchedulerExecutionForTesting();
}
/**
* Shuts down base::TaskScheduler.
*/
public static void disableTaskSchedulerExecutionForTesting() {
nativeDisableTaskSchedulerExecutionForTesting();
}
private static native void nativeEnableTaskSchedulerExecutionForTesting();
private static native void nativeDisableTaskSchedulerExecutionForTesting();
}
// Copyright (c) 2018 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 "base/task/task_scheduler/task_scheduler.h"
#include "jni/TaskSchedulerTestHelpers_jni.h"
namespace base {
// TaskSchedulerTestHelpers is a friend of TaskScheduler which grants access to
// SetExecutionFenceEnabled.
class TaskSchedulerTestHelpers {
public:
// Enables/disables an execution fence that prevents tasks from running.
static void SetTaskSchedulerExecutionFenceEnabledForTesting(
bool execution_fence_enabled);
};
// static
void TaskSchedulerTestHelpers::SetTaskSchedulerExecutionFenceEnabledForTesting(
bool execution_fence_enabled) {
TaskScheduler::GetInstance()->SetExecutionFenceEnabled(
execution_fence_enabled);
}
} // namespace base
void JNI_TaskSchedulerTestHelpers_EnableTaskSchedulerExecutionForTesting(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& jcaller) {
base::TaskSchedulerTestHelpers::
SetTaskSchedulerExecutionFenceEnabledForTesting(false);
}
void JNI_TaskSchedulerTestHelpers_DisableTaskSchedulerExecutionForTesting(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& jcaller) {
base::TaskSchedulerTestHelpers::
SetTaskSchedulerExecutionFenceEnabledForTesting(true);
}
...@@ -74,6 +74,7 @@ import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler ...@@ -74,6 +74,7 @@ import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler
import org.chromium.components.minidump_uploader.CrashFileManager; import org.chromium.components.minidump_uploader.CrashFileManager;
import org.chromium.components.signin.AccountManagerFacade; import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.AccountsChangeObserver; import org.chromium.components.signin.AccountsChangeObserver;
import org.chromium.content_public.browser.BrowserTaskExecutor;
import org.chromium.content_public.browser.ChildProcessLauncherHelper; import org.chromium.content_public.browser.ChildProcessLauncherHelper;
import org.chromium.content_public.common.ContentSwitches; import org.chromium.content_public.common.ContentSwitches;
import org.chromium.printing.PrintDocumentAdapterWrapper; import org.chromium.printing.PrintDocumentAdapterWrapper;
...@@ -146,6 +147,8 @@ public class ProcessInitializationHandler { ...@@ -146,6 +147,8 @@ public class ProcessInitializationHandler {
* Performs the shared class initialization. * Performs the shared class initialization.
*/ */
protected void handlePreNativeInitialization() { protected void handlePreNativeInitialization() {
BrowserTaskExecutor.register();
Context application = ContextUtils.getApplicationContext(); Context application = ContextUtils.getApplicationContext();
// Initialize the AccountManagerFacade with the correct AccountManagerDelegate. Must be done // Initialize the AccountManagerFacade with the correct AccountManagerDelegate. Must be done
......
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
#if defined(OS_ANDROID)
#include "base/android/task_scheduler/post_task_android.h"
#endif
namespace content { namespace content {
namespace { namespace {
...@@ -99,12 +103,24 @@ BrowserThreadImpl::BrowserThreadImpl( ...@@ -99,12 +103,24 @@ BrowserThreadImpl::BrowserThreadImpl(
DCHECK(!globals.task_runners[identifier_]); DCHECK(!globals.task_runners[identifier_]);
globals.task_runners[identifier_] = std::move(task_runner); globals.task_runners[identifier_] = std::move(task_runner);
#if defined(OS_ANDROID)
// TODO(alexclarke): Move this to the BrowserUIThreadScheduler.
if (identifier_ == BrowserThread::ID::UI)
base::PostTaskAndroid::SignalNativeSchedulerReady();
#endif
} }
BrowserThreadImpl::~BrowserThreadImpl() { BrowserThreadImpl::~BrowserThreadImpl() {
BrowserThreadGlobals& globals = GetBrowserThreadGlobals(); BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_);
#if defined(OS_ANDROID)
// TODO(alexclarke): Move this to the BrowserUIThreadScheduler.
if (identifier_ == BrowserThread::ID::UI)
base::PostTaskAndroid::SignalNativeSchedulerShutdown();
#endif
DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier_]), DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier_]),
BrowserThreadState::RUNNING); BrowserThreadState::RUNNING);
base::subtle::NoBarrier_Store(&globals.states[identifier_], base::subtle::NoBarrier_Store(&globals.states[identifier_],
......
...@@ -228,6 +228,7 @@ android_library("content_java") { ...@@ -228,6 +228,7 @@ android_library("content_java") {
"java/src/org/chromium/content_public/browser/AccessibilitySnapshotNode.java", "java/src/org/chromium/content_public/browser/AccessibilitySnapshotNode.java",
"java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java", "java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java",
"java/src/org/chromium/content_public/browser/BrowserStartupController.java", "java/src/org/chromium/content_public/browser/BrowserStartupController.java",
"java/src/org/chromium/content_public/browser/BrowserTaskExecutor.java",
"java/src/org/chromium/content_public/browser/ContentViewStatics.java", "java/src/org/chromium/content_public/browser/ContentViewStatics.java",
"java/src/org/chromium/content_public/browser/DeviceUtils.java", "java/src/org/chromium/content_public/browser/DeviceUtils.java",
"java/src/org/chromium/content_public/browser/InputMethodManagerWrapper.java", "java/src/org/chromium/content_public/browser/InputMethodManagerWrapper.java",
...@@ -259,6 +260,7 @@ android_library("content_java") { ...@@ -259,6 +260,7 @@ android_library("content_java") {
"java/src/org/chromium/content_public/browser/SmartClipProvider.java", "java/src/org/chromium/content_public/browser/SmartClipProvider.java",
"java/src/org/chromium/content_public/browser/SpeechRecognition.java", "java/src/org/chromium/content_public/browser/SpeechRecognition.java",
"java/src/org/chromium/content_public/browser/RenderCoordinates.java", "java/src/org/chromium/content_public/browser/RenderCoordinates.java",
"java/src/org/chromium/content_public/browser/UiThreadTaskTraits.java",
"java/src/org/chromium/content_public/browser/ViewEventSink.java", "java/src/org/chromium/content_public/browser/ViewEventSink.java",
"java/src/org/chromium/content_public/browser/WebContents.java", "java/src/org/chromium/content_public/browser/WebContents.java",
"java/src/org/chromium/content_public/browser/WebContentsAccessibility.java", "java/src/org/chromium/content_public/browser/WebContentsAccessibility.java",
...@@ -500,6 +502,8 @@ android_library("content_javatests") { ...@@ -500,6 +502,8 @@ android_library("content_javatests") {
"javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java", "javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java",
"javatests/src/org/chromium/content/browser/picker/DateTimePickerDialogTest.java", "javatests/src/org/chromium/content/browser/picker/DateTimePickerDialogTest.java",
"javatests/src/org/chromium/content/browser/remoteobjects/RemoteObjectHostImplTest.java", "javatests/src/org/chromium/content/browser/remoteobjects/RemoteObjectHostImplTest.java",
"javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java",
"javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java",
"javatests/src/org/chromium/content/browser/webcontents/AccessibilitySnapshotTest.java", "javatests/src/org/chromium/content/browser/webcontents/AccessibilitySnapshotTest.java",
"javatests/src/org/chromium/content/browser/webcontents/WebContentsTest.java", "javatests/src/org/chromium/content/browser/webcontents/WebContentsTest.java",
"javatests/src/org/chromium/content/common/ServiceManagerConnectionImplTest.java", "javatests/src/org/chromium/content/common/ServiceManagerConnectionImplTest.java",
......
// Copyright 2018 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.
package org.chromium.content_public.browser;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.SequencedTaskRunner;
import org.chromium.base.task.SingleThreadTaskRunner;
import org.chromium.base.task.SingleThreadTaskRunnerImpl;
import org.chromium.base.task.TaskExecutor;
import org.chromium.base.task.TaskRunner;
import org.chromium.base.task.TaskTraits;
import java.util.WeakHashMap;
/**
* This {@link TaskExecutor} is for tasks posted with {@link UiThreadTaskTraits}. It maps directly
* to content::BrowserTaskExecutor except only UI thread posting is supported from java.
*
* NB if you wish to post to the thread pool then use {@link TaskTraits} instead of {@link
* UiThreadTaskTraits}.
*/
public class BrowserTaskExecutor implements TaskExecutor {
@Override
public TaskRunner createTaskRunner(TaskTraits taskTraits) {
return createSingleThreadTaskRunner(taskTraits);
}
@Override
public SequencedTaskRunner createSequencedTaskRunner(TaskTraits taskTraits) {
return createSingleThreadTaskRunner(taskTraits);
}
/**
* This maps to a single thread within the native thread pool. Due to that contract we
* can't run tasks posted on it until native has started.
*/
@Override
public SingleThreadTaskRunner createSingleThreadTaskRunner(TaskTraits taskTraits) {
synchronized (mTaskRunners) {
SingleThreadTaskRunner taskRunner = mTaskRunners.get(taskTraits);
if (taskRunner != null) return taskRunner;
// TODO(alexclarke): ThreadUtils.getUiThreadHandler shouldn't be in base.
taskRunner =
new SingleThreadTaskRunnerImpl(ThreadUtils.getUiThreadHandler(), taskTraits);
mTaskRunners.put(taskTraits, taskRunner);
return taskRunner;
}
}
@Override
public void postTask(TaskTraits taskTraits, Runnable task) {
createSingleThreadTaskRunner(taskTraits).postTask(task);
}
public static void register() {
// In some tests we will get called multiple times.
if (sRegistered) return;
PostTask.registerTaskExecutor(UiThreadTaskTraits.EXTENSION_ID, new BrowserTaskExecutor());
sRegistered = true;
}
private final WeakHashMap<TaskTraits, SingleThreadTaskRunner> mTaskRunners =
new WeakHashMap<>();
private static boolean sRegistered;
}
// Copyright 2018 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.
package org.chromium.content_public.browser;
import org.chromium.base.task.TaskTraits;
/**
* Traits for tasks that need to run on the Browser UI thread. Keep in sync with
* content::BrowserTaskTraitsExtension.
*
* NB if you wish to post to the thread pool then use {@link TaskTraits} instead of {@link
* UiThreadTaskTraits}.
*/
public class UiThreadTaskTraits extends TaskTraits {
// Corresponds to content::BrowserTaskTraitsExtension.
static final byte EXTENSION_ID = 1;
private static final byte UI_THREAD_ID = 0; // Corresponds to content::BrowserThread::ID.
// Keep in sync with content::BrowserTaskTraitsExtension::Serialize.
private static final byte THREAD_INDEX = 0;
private static final byte NESTING_INDEX = 1;
private static final byte[] sDefaultExtensionData = getDefaultExtesionData();
public UiThreadTaskTraits() {
setExtensionId(EXTENSION_ID);
setExtensionData(sDefaultExtensionData);
}
private static byte[] getDefaultExtesionData() {
byte extensionData[] = new byte[TaskTraits.EXTENSION_STORAGE_SIZE];
// Note we don't specify the UI thread directly here because it's ID 0 and the array is
// initialized to zero.
// TODO(crbug.com/876272) Remove this if possible.
extensionData[NESTING_INDEX] = 1; // Allow the task to run in a nested RunLoop.
return extensionData;
}
}
// Copyright 2018 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.
package org.chromium.content.browser.scheduler;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import android.annotation.TargetApi;
import android.os.Build;
import android.support.test.filters.MediumTest;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskRunner;
import org.chromium.base.task.TaskTraits;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.task.SchedulerTestHelpers;
import org.chromium.base.test.task.TaskSchedulerTestHelpers;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.content.app.ContentMain;
import org.chromium.content_public.browser.test.NativeLibraryTestRule;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Test class for {@link PostTask}.
*
* Due to layering concerns we can't test native backed task posting in base, so we do it here
* instead.
*/
@RunWith(BaseJUnit4ClassRunner.class)
@MinAndroidSdkLevel(23)
@TargetApi(Build.VERSION_CODES.M)
public class NativePostTaskTest {
@Rule
public NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
@After
public void tearDown() {
TaskSchedulerTestHelpers.disableTaskSchedulerExecutionForTesting();
}
@Test
@MediumTest
public void testNativePostTask() throws Exception {
startNativeScheduler();
// This should not timeout.
final Object lock = new Object();
final AtomicBoolean taskExecuted = new AtomicBoolean();
PostTask.postTask(new TaskTraits(), new Runnable() {
@Override
public void run() {
synchronized (lock) {
taskExecuted.set(true);
lock.notify();
}
}
});
synchronized (lock) {
try {
while (!taskExecuted.get()) {
lock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
@Test
@MediumTest
public void testCreateTaskRunner() throws Exception {
startNativeScheduler();
TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits());
// This should not time out.
SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
}
@Test
@MediumTest
public void testCreateSequencedTaskRunner() throws Exception {
startNativeScheduler();
TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
assertThat(orderList, contains(1, 2, 3));
}
@Test
@MediumTest
public void testCreateSingleThreadSequencedTaskRunner() throws Exception {
startNativeScheduler();
TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
assertThat(orderList, contains(1, 2, 3));
}
@Test
@MediumTest
public void testCreateTaskRunnerMigrationToNative() throws Exception {
TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
@Override
public void run() {
orderList.add(2);
}
});
assertThat(orderList, contains(1, 2));
}
@Test
@MediumTest
public void testCreateSequencedTaskRunnerMigrationToNative() throws Exception {
TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
@Override
public void run() {
orderList.add(2);
}
});
assertThat(orderList, contains(1, 2));
}
@Test
@MediumTest
public void testCreateSingleThreadSequencedTaskRunnerMigrationToNative() throws Exception {
TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
@Override
public void run() {
orderList.add(2);
}
});
assertThat(orderList, contains(1, 2));
}
private void postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(
TaskRunner taskQueue, Runnable taskToRunAfterNativeSchedulerLoaded) throws Exception {
final Object lock = new Object();
final AtomicBoolean taskRun = new AtomicBoolean();
final AtomicBoolean nativeSchedulerStarted = new AtomicBoolean();
// Post a task that reposts itself until nativeSchedulerStarted is set to true. This tests
// that tasks posted before the native library is loaded still run afterwards.
taskQueue.postTask(new Runnable() {
@Override
public void run() {
if (nativeSchedulerStarted.compareAndSet(true, true)) {
taskToRunAfterNativeSchedulerLoaded.run();
synchronized (lock) {
taskRun.set(true);
lock.notify();
}
} else {
taskQueue.postTask(this);
}
}
});
startNativeScheduler();
nativeSchedulerStarted.set(true);
synchronized (lock) {
try {
while (!taskRun.get()) {
lock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
private void startNativeScheduler() throws Exception {
mNativeLibraryTestRule.loadNativeLibraryNoBrowserProcess();
ContentMain.start(/* startServiceManagerOnly */ true);
TaskSchedulerTestHelpers.enableTaskSchedulerExecutionForTesting();
}
}
// Copyright 2018 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.
package org.chromium.content.browser.scheduler;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.test.filters.MediumTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskRunner;
import org.chromium.base.task.TaskTraits;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.task.SchedulerTestHelpers;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.content.app.ContentMain;
import org.chromium.content_public.browser.BrowserTaskExecutor;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.browser.test.NativeLibraryTestRule;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Test class for scheduling on the UI Thread.
*/
@RunWith(BaseJUnit4ClassRunner.class)
@MinAndroidSdkLevel(23)
@TargetApi(Build.VERSION_CODES.M)
public class UiThreadSchedulerTest {
@Rule
public NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
@Before
public void setUp() {
mNativeLibraryTestRule.loadNativeLibraryNoBrowserProcess();
ThreadUtils.setUiThread(null);
ThreadUtils.setWillOverrideUiThread();
mUiThread = new HandlerThread("UiThreadForTest");
mUiThread.start();
ThreadUtils.setUiThread(mUiThread.getLooper());
BrowserTaskExecutor.register();
mHandler = new Handler(mUiThread.getLooper());
}
@After
public void tearDown() {
mUiThread.quitSafely();
ThreadUtils.setUiThread(null);
}
@Test
@MediumTest
public void testSimpleUiThreadPostingBeforeNativeLoaded() throws Exception {
TaskRunner uiThreadTaskRunner =
PostTask.createSingleThreadTaskRunner(new UiThreadTaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 3);
SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
assertThat(orderList, contains(1, 2, 3));
}
@Test
@MediumTest
public void testUiThreadTaskRunnerMigrationToNative() throws Exception {
TaskRunner uiThreadTaskRunner =
PostTask.createSingleThreadTaskRunner(new UiThreadTaskTraits());
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(uiThreadTaskRunner, orderList, 1);
postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(
uiThreadTaskRunner, new Runnable() {
@Override
public void run() {
orderList.add(2);
}
});
assertThat(orderList, contains(1, 2));
}
@Test
@MediumTest
public void testSimpleUiThreadPostingAfterNativeLoaded() throws Exception {
TaskRunner uiThreadTaskRunner =
PostTask.createSingleThreadTaskRunner(new UiThreadTaskTraits());
startContentMainOnUiThread();
uiThreadTaskRunner.postTask(new Runnable() {
@Override
public void run() {
Assert.assertTrue(ThreadUtils.runningOnUiThread());
}
});
SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
}
@Test
@MediumTest
public void testTaskNotRunOnUiThreadWithoutUiThreadTaskTraits() throws Exception {
TaskRunner uiThreadTaskRunner = PostTask.createSingleThreadTaskRunner(new TaskTraits());
startContentMainOnUiThread();
uiThreadTaskRunner.postTask(new Runnable() {
@Override
public void run() {
Assert.assertFalse(ThreadUtils.runningOnUiThread());
}
});
SchedulerTestHelpers.postTaskAndBlockUntilRun(uiThreadTaskRunner);
}
private void startContentMainOnUiThread() {
final Object lock = new Object();
final AtomicBoolean uiThreadInitalized = new AtomicBoolean();
mHandler.post(new Runnable() {
@Override
public void run() {
try {
ContentMain.start(/* startServiceManagerOnly */ true);
synchronized (lock) {
uiThreadInitalized.set(true);
lock.notify();
}
} catch (Exception e) {
}
}
});
synchronized (lock) {
try {
while (!uiThreadInitalized.get()) {
lock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
private void postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(
TaskRunner taskQueue, Runnable taskToRunAfterNativeSchedulerLoaded) throws Exception {
final Object lock = new Object();
final AtomicBoolean taskRun = new AtomicBoolean();
final AtomicBoolean nativeSchedulerStarted = new AtomicBoolean();
// Post a task that reposts itself until nativeSchedulerStarted is set to true. This tests
// that tasks posted before the native library is loaded still run afterwards.
taskQueue.postTask(new Runnable() {
@Override
public void run() {
if (nativeSchedulerStarted.compareAndSet(true, true)) {
taskToRunAfterNativeSchedulerLoaded.run();
synchronized (lock) {
taskRun.set(true);
lock.notify();
}
} else {
taskQueue.postTask(this);
}
}
});
startContentMainOnUiThread();
nativeSchedulerStarted.set(true);
synchronized (lock) {
try {
while (!taskRun.get()) {
lock.wait();
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
private Handler mHandler;
private HandlerThread mUiThread;
}
...@@ -68,6 +68,7 @@ class CONTENT_EXPORT BrowserTaskTraitsExtension { ...@@ -68,6 +68,7 @@ class CONTENT_EXPORT BrowserTaskTraitsExtension {
nestable_(!base::trait_helpers::GetTraitFromArgList<NonNestableFilter>( nestable_(!base::trait_helpers::GetTraitFromArgList<NonNestableFilter>(
args...)) {} args...)) {}
// Keep in sync with UiThreadTaskTraits.java
constexpr base::TaskTraitsExtensionStorage Serialize() const { constexpr base::TaskTraitsExtensionStorage Serialize() const {
static_assert(8 == sizeof(BrowserTaskTraitsExtension), static_assert(8 == sizeof(BrowserTaskTraitsExtension),
"Update Serialize() and Parse() when changing " "Update Serialize() and Parse() when changing "
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment