blob: 09899238b36cb6f435f1e2e28e1bf8214f9580ad [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base.task;
import androidx.annotation.Nullable;
import java.util.Arrays;
/**
* 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;
// Convenience variables explicitly specifying common priorities.
// These also imply THREAD_POOL unless explicitly overwritten.
// TODO(1026641): Make destination explicit in Java too.
// This task will only be scheduled when machine resources are available. Once
// running, it may be descheduled if higher priority work arrives (in this
// process or another) and its running on a non-critical thread. This is the
// lowest possible priority.
public static final TaskTraits BEST_EFFORT =
new TaskTraits().taskPriority(TaskPriority.BEST_EFFORT);
// This is a lowest-priority task which may block, for example non-urgent
// logging or deletion of temporary files as clean-up.
public static final TaskTraits BEST_EFFORT_MAY_BLOCK = BEST_EFFORT.mayBlock();
// This task affects UI or responsiveness of future user interactions. It is
// not an immediate response to a user interaction. Most tasks are likely to
// have this priority.
// Examples:
// - Updating the UI to reflect progress on a long task.
// - Loading data that might be shown in the UI after a future user
// interaction.
public static final TaskTraits USER_VISIBLE =
new TaskTraits().taskPriority(TaskPriority.USER_VISIBLE);
// USER_VISIBLE + may block.
public static final TaskTraits USER_VISIBLE_MAY_BLOCK = USER_VISIBLE.mayBlock();
// This task affects UI immediately after a user interaction.
// Example: Generating data shown in the UI immediately after a click.
public static final TaskTraits USER_BLOCKING =
new TaskTraits().taskPriority(TaskPriority.USER_BLOCKING);
// USER_BLOCKING + may block.
public static final TaskTraits USER_BLOCKING_MAY_BLOCK = USER_BLOCKING.mayBlock();
// A bit like requestAnimationFrame, this task will be posted onto the Choreographer
// and will be run on the android main thread after the next vsync.
public static final TaskTraits CHOREOGRAPHER_FRAME = new TaskTraits();
static {
CHOREOGRAPHER_FRAME.mIsChoreographerFrame = true;
}
// For tasks that should run on the thread pool instead of the main thread.
// Note that currently also tasks which lack this trait will execute on the
// thread pool unless a trait for a named thread is given.
public static final TaskTraits THREAD_POOL =
new TaskTraits().threadPool().taskPriority(TaskPriority.USER_BLOCKING);
public static final TaskTraits THREAD_POOL_USER_BLOCKING =
THREAD_POOL.taskPriority(TaskPriority.USER_BLOCKING);
public static final TaskTraits THREAD_POOL_USER_VISIBLE =
THREAD_POOL.taskPriority(TaskPriority.USER_VISIBLE);
public static final TaskTraits THREAD_POOL_BEST_EFFORT =
THREAD_POOL.taskPriority(TaskPriority.BEST_EFFORT);
// For convenience of the JNI code, we use primitive types only.
// Note shutdown behavior is not supported on android.
int mPriority;
boolean mMayBlock;
boolean mUseThreadPool;
byte mExtensionId;
byte mExtensionData[];
boolean mIsChoreographerFrame;
// Derive custom traits from existing trait constants.
private TaskTraits() {
// Assume USER_BLOCKING by default.
mPriority = TaskPriority.USER_BLOCKING;
}
private TaskTraits(TaskTraits other) {
mPriority = other.mPriority;
mMayBlock = other.mMayBlock;
mUseThreadPool = other.mUseThreadPool;
mExtensionId = other.mExtensionId;
mExtensionData = other.mExtensionData;
}
public TaskTraits taskPriority(int taskPriority) {
TaskTraits taskTraits = new TaskTraits(this);
taskTraits.mPriority = taskPriority;
return taskTraits;
}
/**
* 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. The thread pool uses this property to work out if
* additional threads are required.
*/
public TaskTraits mayBlock() {
TaskTraits taskTraits = new TaskTraits(this);
taskTraits.mMayBlock = true;
return taskTraits;
}
public TaskTraits threadPool() {
TaskTraits taskTraits = new TaskTraits(this);
taskTraits.mUseThreadPool = true;
return taskTraits;
}
/**
* @return true if this task is using some TaskTraits extension.
*/
public boolean hasExtension() {
return mExtensionId != INVALID_EXTENSION_ID;
}
/**
* Tries to extract the extension for the given descriptor from this traits.
*
* @return Extension instance or null if the traits do not contain the requested extension
*/
public <Extension> Extension getExtension(TaskTraitsExtensionDescriptor<Extension> descriptor) {
if (mExtensionId == descriptor.getId()) {
return descriptor.fromSerializedData(mExtensionData);
} else {
return null;
}
}
public <Extension> TaskTraits withExtension(
TaskTraitsExtensionDescriptor<Extension> descriptor, Extension extension) {
int id = descriptor.getId();
byte[] data = descriptor.toSerializedData(extension);
assert id > INVALID_EXTENSION_ID && id <= MAX_EXTENSION_ID;
assert data.length <= EXTENSION_STORAGE_SIZE;
TaskTraits taskTraits = new TaskTraits(this);
taskTraits.mExtensionId = (byte) id;
taskTraits.mExtensionData = data;
return taskTraits;
}
/**
* Returns a TaskTraits with an explicit destination.
*
* The C++ side enforces that a destination _must_ be specified. The Java
* side loosely considers lack of destination as implying THREAD_POOL
* destination.
* TODO(1026641): Bring the Java side inline with the C++ side.
*/
public TaskTraits withExplicitDestination() {
if (!mUseThreadPool && !hasExtension()) {
return this.threadPool();
}
return this;
}
@Override
public boolean equals(@Nullable Object object) {
if (object == this) {
return true;
} else if (object instanceof TaskTraits) {
TaskTraits other = (TaskTraits) object;
return mPriority == other.mPriority && mMayBlock == other.mMayBlock
&& mUseThreadPool == other.mUseThreadPool && mExtensionId == other.mExtensionId
&& Arrays.equals(mExtensionData, other.mExtensionData)
&& mIsChoreographerFrame == other.mIsChoreographerFrame;
} else {
return false;
}
}
@Override
public int hashCode() {
int hash = 31;
hash = 37 * hash + mPriority;
hash = 37 * hash + (mMayBlock ? 0 : 1);
hash = 37 * hash + (mUseThreadPool ? 0 : 1);
hash = 37 * hash + (int) mExtensionId;
hash = 37 * hash + Arrays.hashCode(mExtensionData);
hash = 37 * hash + (mIsChoreographerFrame ? 0 : 1);
return hash;
}
}