blob: 3612b5cee676a1269e0e575d38b77a5677cf36e6 [file] [log] [blame]
// 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.annotation.Nullable;
import android.util.Pair;
import org.chromium.base.TraceEvent;
import org.chromium.base.annotations.JNINamespace;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* 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 String mTraceEvent;
private final @TaskRunnerType int mTaskRunnerType;
private final Object mLock = new Object();
protected long mNativeTaskRunnerAndroid;
protected final Runnable mRunPreNativeTaskClosure = this::runPreNativeTask;
private boolean mIsDestroying;
@Nullable
protected LinkedList<Runnable> mPreNativeTasks = new LinkedList<>();
@Nullable
protected List<Pair<Runnable, Long>> mPreNativeDelayedTasks = new ArrayList<>();
/**
* @param traits The TaskTraits associated with this TaskRunnerImpl.
*/
TaskRunnerImpl(TaskTraits traits) {
this(traits, "TaskRunnerImpl", TaskRunnerType.BASE);
}
/**
* @param traits The TaskTraits associated with this TaskRunnerImpl.
* @param traceCategory Specifies which subclass is this instance for logging purposes.
* @param taskRunnerType Specifies which subclass is this instance for initialising the correct
* native scheduler.
*/
protected TaskRunnerImpl(
TaskTraits traits, String traceCategory, @TaskRunnerType int taskRunnerType) {
mTaskTraits = traits;
mTraceEvent = traceCategory + ".PreNativeTask.run";
mTaskRunnerType = taskRunnerType;
if (!PostTask.registerPreNativeTaskRunnerLocked(this)) initNativeTaskRunner();
}
@Override
public void destroy() {
synchronized (mLock) {
if (mNativeTaskRunnerAndroid != 0) nativeDestroy(mNativeTaskRunnerAndroid);
mNativeTaskRunnerAndroid = 0;
mIsDestroying = true;
}
}
@Override
protected void finalize() throws Throwable {
try {
// Calling destroy as a fallback in case destroy wasn't called by the user.
destroy();
} finally {
super.finalize();
}
}
@Override
public void postTask(Runnable task) {
postDelayedTask(task, 0);
}
@Override
public void postDelayedTask(Runnable task, long delay) {
synchronized (mLock) {
assert !mIsDestroying;
if (mNativeTaskRunnerAndroid != 0) {
nativePostDelayedTask(mNativeTaskRunnerAndroid, task, delay);
return;
}
// We don't expect a whole lot of these, if that changes consider pooling them.
// If a task is scheduled for immediate execution, we post it on the
// pre-native task runner. Tasks scheduled to run with a delay will
// wait until the native task runner is initialised.
if (delay == 0) {
mPreNativeTasks.add(task);
schedulePreNativeTask();
} else {
Pair<Runnable, Long> preNativeDelayedTask = new Pair<>(task, delay);
mPreNativeDelayedTasks.add(preNativeDelayedTask);
}
}
}
/**
* Must be overridden in subclasses, schedules a call to runPreNativeTask() at an appropriate
* time.
*/
protected void schedulePreNativeTask() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(mRunPreNativeTaskClosure);
}
/**
* Runs a single task and returns when its finished.
*/
protected void runPreNativeTask() {
try (TraceEvent te = TraceEvent.scoped(mTraceEvent)) {
Runnable task;
synchronized (mLock) {
if (mPreNativeTasks == null) return;
task = mPreNativeTasks.poll();
}
task.run();
}
}
/**
* Instructs the TaskRunner to initialize the native TaskRunner and migrate any tasks over to
* it.
*/
@Override
public void initNativeTaskRunner() {
synchronized (mLock) {
if (mPreNativeTasks != null) {
mNativeTaskRunnerAndroid =
nativeInit(mTaskRunnerType, mTaskTraits.mPrioritySetExplicitly,
mTaskTraits.mPriority, mTaskTraits.mMayBlock,
mTaskTraits.mExtensionId, mTaskTraits.mExtensionData);
for (Runnable task : mPreNativeTasks) {
nativePostDelayedTask(mNativeTaskRunnerAndroid, task, 0);
}
for (Pair<Runnable, Long> task : mPreNativeDelayedTasks) {
nativePostDelayedTask(mNativeTaskRunnerAndroid, task.first, task.second);
}
mPreNativeTasks = null;
mPreNativeDelayedTasks = null;
}
}
}
// NB due to Proguard obfuscation it's easiest to pass the traits via arguments.
private native long nativeInit(@TaskRunnerType int taskRunnerType,
boolean prioritySetExplicitly, int priority, boolean mayBlock, byte extensionId,
byte[] extensionData);
private native void nativeDestroy(long nativeTaskRunnerAndroid);
private native void nativePostDelayedTask(
long nativeTaskRunnerAndroid, Runnable task, long delay);
protected native boolean nativeBelongsToCurrentThread(long nativeTaskRunnerAndroid);
}