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 @@ ...@@ -4,7 +4,10 @@
package org.chromium.base.task; package org.chromium.base.task;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
...@@ -18,16 +21,26 @@ import org.chromium.base.annotations.JNINamespace; ...@@ -18,16 +21,26 @@ import org.chromium.base.annotations.JNINamespace;
public class SingleThreadTaskRunnerImpl extends TaskRunnerImpl implements SingleThreadTaskRunner { public class SingleThreadTaskRunnerImpl extends TaskRunnerImpl implements SingleThreadTaskRunner {
@Nullable @Nullable
private final Handler mHandler; 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 * @param handler The backing Handler if any. Note this must run tasks on the
* the native code runs a task with |traits|. If handler is null then tasks won't run until * same thread that the native code runs a task with |traits|.
* native has initialized. * If handler is null then tasks won't run until native has
* @param traits The TaskTraits associated with this SingleThreadTaskRunnerImpl. * 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); super(traits, "SingleThreadTaskRunnerImpl", TaskRunnerType.SINGLE_THREAD);
mHandler = handler; mHandler = handler;
mPostTaskAtFrontOfQueue = postTaskAtFrontOfQueue;
}
public SingleThreadTaskRunnerImpl(Handler handler, TaskTraits traits) {
this(handler, traits, false);
} }
@Override @Override
...@@ -42,6 +55,26 @@ public class SingleThreadTaskRunnerImpl extends TaskRunnerImpl implements Single ...@@ -42,6 +55,26 @@ public class SingleThreadTaskRunnerImpl extends TaskRunnerImpl implements Single
@Override @Override
protected void schedulePreNativeTask() { protected void schedulePreNativeTask() {
// if |mHandler| is null then pre-native task execution is not supported. // 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 { ...@@ -56,7 +56,7 @@ public class SingleThreadTaskRunnerImplTest {
@Test @Test
@SmallTest @SmallTest
public void testPreNativePostTask() { public void testPreNativePostTask() {
TaskRunner taskQueue = new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits()); TaskRunner taskQueue = new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
List<Integer> orderList = new ArrayList<>(); List<Integer> orderList = new ArrayList<>();
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1); SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2); SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
...@@ -72,7 +72,7 @@ public class SingleThreadTaskRunnerImplTest { ...@@ -72,7 +72,7 @@ public class SingleThreadTaskRunnerImplTest {
public void testBelongsToCurrentThread() { public void testBelongsToCurrentThread() {
// The handler created during test setup belongs to a different thread. // The handler created during test setup belongs to a different thread.
SingleThreadTaskRunner taskQueue = SingleThreadTaskRunner taskQueue =
new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits()); new SingleThreadTaskRunnerImpl(mHandler, new TaskTraits(), false);
try { try {
Assert.assertFalse(taskQueue.belongsToCurrentThread()); Assert.assertFalse(taskQueue.belongsToCurrentThread());
} finally { } finally {
...@@ -82,11 +82,42 @@ public class SingleThreadTaskRunnerImplTest { ...@@ -82,11 +82,42 @@ public class SingleThreadTaskRunnerImplTest {
// We create a handler belonging to current thread. // We create a handler belonging to current thread.
Looper.prepare(); Looper.prepare();
SingleThreadTaskRunner taskQueueCurrentThread = SingleThreadTaskRunner taskQueueCurrentThread =
new SingleThreadTaskRunnerImpl(new Handler(), new TaskTraits()); new SingleThreadTaskRunnerImpl(new Handler(), new TaskTraits(), false);
try { try {
Assert.assertTrue(taskQueueCurrentThread.belongsToCurrentThread()); Assert.assertTrue(taskQueueCurrentThread.belongsToCurrentThread());
} finally { } finally {
taskQueueCurrentThread.destroy(); 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; ...@@ -73,6 +73,7 @@ import org.chromium.chrome.browser.services.GoogleServicesManager;
import org.chromium.chrome.browser.share.ShareHelper; import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.sync.SyncController; import org.chromium.chrome.browser.sync.SyncController;
import org.chromium.chrome.browser.util.ConversionUtils; 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.WebApkVersionManager;
import org.chromium.chrome.browser.webapps.WebappRegistry; import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory; import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
...@@ -153,6 +154,8 @@ public class ProcessInitializationHandler { ...@@ -153,6 +154,8 @@ public class ProcessInitializationHandler {
*/ */
protected void handlePreNativeInitialization() { protected void handlePreNativeInitialization() {
BrowserTaskExecutor.register(); BrowserTaskExecutor.register();
BrowserTaskExecutor.setShouldPrioritizeBootstrapTasks(
FeatureUtilities.shouldPrioritizeBootstrapTasks());
Context application = ContextUtils.getApplicationContext(); Context application = ContextUtils.getApplicationContext();
......
...@@ -47,8 +47,8 @@ public class BrowserTaskExecutor implements TaskExecutor { ...@@ -47,8 +47,8 @@ public class BrowserTaskExecutor implements TaskExecutor {
if (taskRunner != null) return taskRunner; if (taskRunner != null) return taskRunner;
// TODO(alexclarke): ThreadUtils.getUiThreadHandler shouldn't be in base. // TODO(alexclarke): ThreadUtils.getUiThreadHandler shouldn't be in base.
taskRunner = taskRunner = new SingleThreadTaskRunnerImpl(ThreadUtils.getUiThreadHandler(),
new SingleThreadTaskRunnerImpl(ThreadUtils.getUiThreadHandler(), taskTraits); taskTraits, shouldPrioritizeTraits(taskTraits));
taskRunner.disableLifetimeCheck(); taskRunner.disableLifetimeCheck();
mTaskRunners.put(taskTraits, taskRunner); mTaskRunners.put(taskTraits, taskRunner);
return taskRunner; return taskRunner;
...@@ -74,9 +74,34 @@ public class BrowserTaskExecutor implements TaskExecutor { ...@@ -74,9 +74,34 @@ public class BrowserTaskExecutor implements TaskExecutor {
UiThreadTaskTraitsImpl.DESCRIPTOR.getId(), new BrowserTaskExecutor()); 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") @GuardedBy("mTaskRunners")
private final WeakHashMap<TaskTraits, SingleThreadTaskRunner> mTaskRunners = private final WeakHashMap<TaskTraits, SingleThreadTaskRunner> mTaskRunners =
new WeakHashMap<>(); new WeakHashMap<>();
private static boolean sRegistered; private static boolean sRegistered;
private static boolean sShouldPrioritizeBootstrapTasks;
} }
...@@ -50,6 +50,7 @@ public class UiThreadSchedulerTest { ...@@ -50,6 +50,7 @@ public class UiThreadSchedulerTest {
mUiThread.start(); mUiThread.start();
ThreadUtils.setUiThread(mUiThread.getLooper()); ThreadUtils.setUiThread(mUiThread.getLooper());
BrowserTaskExecutor.register(); BrowserTaskExecutor.register();
BrowserTaskExecutor.setShouldPrioritizeBootstrapTasks(true);
mHandler = new Handler(mUiThread.getLooper()); mHandler = new Handler(mUiThread.getLooper());
} }
...@@ -78,6 +79,37 @@ public class UiThreadSchedulerTest { ...@@ -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 @Test
@MediumTest @MediumTest
public void testUiThreadTaskRunnerMigrationToNative() throws Exception { 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