Commit 08282975 authored by Alex Clarke's avatar Alex Clarke Committed by Commit Bot

[java] Prioritize bootstrap and navigation tasks behind a feature flag.

When prioritizing tasks SingleThreadTaskRunnerImpl will post async
messages at the head of the queue.  It's important for these messages to
be async because we want to bypass the java fences and run the tasks
immediately, rather than having them batched up to run every vsync.

Bug: 863341
Change-Id: I4faf7846e628cc05c05809e626d22788ae9a148c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1514533
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#641132}
parent 0709283f
......@@ -4,7 +4,10 @@
package org.chromium.base.task;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import org.chromium.base.annotations.JNINamespace;
......@@ -18,16 +21,26 @@ import org.chromium.base.annotations.JNINamespace;
public class SingleThreadTaskRunnerImpl extends TaskRunnerImpl implements SingleThreadTaskRunner {
@Nullable
private final Handler mHandler;
private final boolean mPostTaskAtFrontOfQueue;
/**
* @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.
* @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.
* @param postTaskAtFrontOfQueue If true, tasks posted to the backing Handler will be posted at
* the front of the queue.
*/
public SingleThreadTaskRunnerImpl(Handler handler, TaskTraits traits) {
public SingleThreadTaskRunnerImpl(
Handler handler, TaskTraits traits, boolean postTaskAtFrontOfQueue) {
super(traits, "SingleThreadTaskRunnerImpl", TaskRunnerType.SINGLE_THREAD);
mHandler = handler;
mPostTaskAtFrontOfQueue = postTaskAtFrontOfQueue;
}
public SingleThreadTaskRunnerImpl(Handler handler, TaskTraits traits) {
this(handler, traits, false);
}
@Override
......@@ -42,6 +55,26 @@ public class SingleThreadTaskRunnerImpl extends TaskRunnerImpl implements Single
@Override
protected void schedulePreNativeTask() {
// if |mHandler| is null then pre-native task execution is not supported.
if (mHandler != null) mHandler.post(mRunPreNativeTaskClosure);
if (mHandler == null) {
return;
} else if (mPostTaskAtFrontOfQueue) {
postAtFrontOfQueue();
} else {
mHandler.post(mRunPreNativeTaskClosure);
}
}
@SuppressLint("NewApi")
private void postAtFrontOfQueue() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// The mHandler.postAtFrontOfQueue() API uses fences which batches messages up per
// frame. We want to bypass that for performance, hence we use async messages where
// possible.
Message message = Message.obtain(mHandler, mRunPreNativeTaskClosure);
message.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(message);
} else {
mHandler.postAtFrontOfQueue(mRunPreNativeTaskClosure);
}
}
}
......@@ -56,7 +56,7 @@ public class SingleThreadTaskRunnerImplTest {
@Test
@SmallTest
public void testPreNativePostTask() {
TaskRunner taskQueue = new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits());
TaskRunner taskQueue = new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
......@@ -72,7 +72,7 @@ public class SingleThreadTaskRunnerImplTest {
public void testBelongsToCurrentThread() {
// The handler created during test setup belongs to a different thread.
SingleThreadTaskRunner taskQueue =
new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits());
new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
try {
Assert.assertFalse(taskQueue.belongsToCurrentThread());
} finally {
......@@ -82,11 +82,42 @@ public class SingleThreadTaskRunnerImplTest {
// We create a handler belonging to current thread.
Looper.prepare();
SingleThreadTaskRunner taskQueueCurrentThread =
new SingleThreadTaskRunnerImpl(new Handler(), new TaskTraits());
new SingleThreadTaskRunnerImpl(new Handler(), new TaskTraits(), false);
try {
Assert.assertTrue(taskQueueCurrentThread.belongsToCurrentThread());
} finally {
taskQueueCurrentThread.destroy();
}
}
@Test
@SmallTest
public void testPrioritization() {
TaskRunner highPriorityQueue =
new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), true);
TaskRunner lowPriorityQueue =
new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
try {
List<Integer> orderList = new ArrayList<>();
// We want to post all these tasks atomically but we're not on the mHandlerThread so
// we need to post a task to do that for us.
lowPriorityQueue.postTask(new Runnable() {
@Override
public void run() {
SchedulerTestHelpers.postRecordOrderTask(lowPriorityQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(lowPriorityQueue, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(lowPriorityQueue, orderList, 3);
SchedulerTestHelpers.postRecordOrderTask(highPriorityQueue, orderList, 10);
SchedulerTestHelpers.postRecordOrderTask(highPriorityQueue, orderList, 20);
SchedulerTestHelpers.postRecordOrderTask(highPriorityQueue, orderList, 30);
}
});
SchedulerTestHelpers.preNativeRunUntilIdle(mHandlerThread);
assertThat(orderList, contains(10, 20, 30, 1, 2, 3));
} finally {
highPriorityQueue.destroy();
lowPriorityQueue.destroy();
}
}
}
......@@ -73,6 +73,7 @@ import org.chromium.chrome.browser.services.GoogleServicesManager;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.sync.SyncController;
import org.chromium.chrome.browser.util.ConversionUtils;
import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.chrome.browser.webapps.WebApkVersionManager;
import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
......@@ -153,6 +154,8 @@ public class ProcessInitializationHandler {
*/
protected void handlePreNativeInitialization() {
BrowserTaskExecutor.register();
BrowserTaskExecutor.setShouldPrioritizeBootstrapTasks(
FeatureUtilities.shouldPrioritizeBootstrapTasks());
Context application = ContextUtils.getApplicationContext();
......
......@@ -47,8 +47,8 @@ public class BrowserTaskExecutor implements TaskExecutor {
if (taskRunner != null) return taskRunner;
// TODO(alexclarke): ThreadUtils.getUiThreadHandler shouldn't be in base.
taskRunner =
new SingleThreadTaskRunnerImpl(ThreadUtils.getUiThreadHandler(), taskTraits);
taskRunner = new SingleThreadTaskRunnerImpl(ThreadUtils.getUiThreadHandler(),
taskTraits, shouldPrioritizeTraits(taskTraits));
taskRunner.disableLifetimeCheck();
mTaskRunners.put(taskTraits, taskRunner);
return taskRunner;
......@@ -74,9 +74,34 @@ public class BrowserTaskExecutor implements TaskExecutor {
UiThreadTaskTraitsImpl.DESCRIPTOR.getId(), new BrowserTaskExecutor());
}
public static boolean getShouldPrioritizeBootstrapTasks() {
return sShouldPrioritizeBootstrapTasks;
}
public static void setShouldPrioritizeBootstrapTasks(boolean shouldPrioritizeBootstrapTasks) {
sShouldPrioritizeBootstrapTasks = shouldPrioritizeBootstrapTasks;
}
private static boolean shouldPrioritizeTraits(TaskTraits taskTraits) {
if (!sShouldPrioritizeBootstrapTasks) return false;
UiThreadTaskTraitsImpl impl = taskTraits.getExtension(UiThreadTaskTraitsImpl.DESCRIPTOR);
if (impl == null) return false;
switch (impl.getTaskType()) {
case BrowserTaskType.BOOTSTRAP:
case BrowserTaskType.NAVIGATION:
return true;
default:
return false;
}
}
@GuardedBy("mTaskRunners")
private final WeakHashMap<TaskTraits, SingleThreadTaskRunner> mTaskRunners =
new WeakHashMap<>();
private static boolean sRegistered;
private static boolean sShouldPrioritizeBootstrapTasks;
}
......@@ -50,6 +50,7 @@ public class UiThreadSchedulerTest {
mUiThread.start();
ThreadUtils.setUiThread(mUiThread.getLooper());
BrowserTaskExecutor.register();
BrowserTaskExecutor.setShouldPrioritizeBootstrapTasks(true);
mHandler = new Handler(mUiThread.getLooper());
}
......@@ -78,6 +79,37 @@ public class UiThreadSchedulerTest {
}
}
@Test
@MediumTest
public void testPrioritizationBeforeNativeLoaded() {
TaskRunner defaultTaskRunner =
PostTask.createSingleThreadTaskRunner(UiThreadTaskTraits.DEFAULT);
TaskRunner bootstrapTaskRunner =
PostTask.createSingleThreadTaskRunner(UiThreadTaskTraits.BOOTSTRAP);
try {
List<Integer> orderList = new ArrayList<>();
// We want to enqueue these tasks atomically but we're not on the mUiThread. So
// we post a task to enqueue them.
PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
@Override
public void run() {
SchedulerTestHelpers.postRecordOrderTask(defaultTaskRunner, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(defaultTaskRunner, orderList, 2);
SchedulerTestHelpers.postRecordOrderTask(defaultTaskRunner, orderList, 3);
SchedulerTestHelpers.postRecordOrderTask(bootstrapTaskRunner, orderList, 10);
SchedulerTestHelpers.postRecordOrderTask(bootstrapTaskRunner, orderList, 20);
SchedulerTestHelpers.postRecordOrderTask(bootstrapTaskRunner, orderList, 30);
}
});
SchedulerTestHelpers.preNativeRunUntilIdle(mUiThread);
assertThat(orderList, contains(10, 20, 30, 1, 2, 3));
} finally {
defaultTaskRunner.destroy();
bootstrapTaskRunner.destroy();
}
}
@Test
@MediumTest
public void testUiThreadTaskRunnerMigrationToNative() throws Exception {
......
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