Add support for changing a threads scheduler group. Three groups are available (default, background non interactive, foreground boost). Setting a thread priority to PRIORITY_BACKGROUND will transparently change groups to background
Signed-off-by: San Mehat <san@google.com>
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e4412a3..30acef9 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -176,6 +176,26 @@
*/
public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;
+ /**
+ * Default thread group - gets a 'normal' share of the CPU
+ * @hide
+ */
+ public static final int THREAD_GROUP_DEFAULT = 0;
+
+ /**
+ * Background non-interactive thread group - All threads in
+ * this group are scheduled with a reduced share of the CPU.
+ * @hide
+ */
+ public static final int THREAD_GROUP_BG_NONINTERACTIVE = 1;
+
+ /**
+ * Foreground 'boost' thread group - All threads in
+ * this group are scheduled with an increased share of the CPU
+ * @hide
+ **/
+ public static final int THREAD_GROUP_FG_BOOST = 2;
+
public static final int SIGNAL_QUIT = 3;
public static final int SIGNAL_KILL = 9;
public static final int SIGNAL_USR1 = 10;
@@ -569,6 +589,21 @@
*/
public static final native void setThreadPriority(int tid, int priority)
throws IllegalArgumentException, SecurityException;
+
+ /**
+ * Sets the scheduling group for a thread.
+ * @hide
+ * @param tid The indentifier of the thread/process to change.
+ * @param group The target group for this thread/process.
+ *
+ * @throws IllegalArgumentException Throws IllegalArgumentException if
+ * <var>tid</var> does not exist.
+ * @throws SecurityException Throws SecurityException if your process does
+ * not have permission to modify the given thread, or to use the given
+ * priority.
+ */
+ public static final native void setThreadGroup(int tid, int group)
+ throws IllegalArgumentException, SecurityException;
/**
* Set the priority of the calling thread, based on Linux priorities. See
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index bd56605..d760feb 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -50,6 +50,16 @@
#undef __KERNEL__
#endif
+#define ENABLE_CGROUP_ERR_LOGGING 0
+
+/*
+ * List of cgroup names which map to ANDROID_TGROUP_ values in Thread.h
+ * and Process.java
+ * These names are used to construct the path to the cgroup control dir
+ */
+
+static const char *cgroup_names[] = { NULL, "bg_non_interactive", "fg_boost" };
+
using namespace android;
static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err)
@@ -73,6 +83,28 @@
}
}
+static void signalExceptionForGroupError(JNIEnv* env, jobject obj, int err)
+{
+ switch (err) {
+ case EINVAL:
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ break;
+ case ESRCH:
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
+ break;
+ case EPERM:
+ jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
+ break;
+ case EACCES:
+ jniThrowException(env, "java/lang/SecurityException", "No permission to set to given group");
+ break;
+ default:
+ jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+ break;
+ }
+}
+
+
static void fakeProcessEntry(void* arg)
{
String8* cls = (String8*)arg;
@@ -164,9 +196,55 @@
return -1;
}
+static int add_pid_to_cgroup(int pid, int grp)
+{
+ FILE *fp;
+ char path[255];
+ int rc;
+
+ sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup_names[grp] ? cgroup_names[grp] : ""));
+
+ if (!(fp = fopen(path, "w"))) {
+#if ENABLE_CGROUP_ERR_LOGGING
+ LOGW("Unable to open %s (%s)\n", path, strerror(errno));
+#endif
+ return -errno;
+ }
+
+ rc = fprintf(fp, "%d", pid);
+ fclose(fp);
+
+ if (rc < 0) {
+#if ENABLE_CGROUP_ERR_LOGGING
+ LOGW("Unable to move pid %d to cgroup %s (%s)\n", pid,
+ (cgroup_names[grp] ? cgroup_names[grp] : "<default>"),
+ strerror(errno));
+#endif
+ }
+
+ return (rc < 0) ? errno : 0;
+}
+
+void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
+{
+ if (grp > ANDROID_TGROUP_MAX || grp < 0) {
+ signalExceptionForGroupError(env, clazz, EINVAL);
+ return;
+ }
+
+ if (add_pid_to_cgroup(pid, grp))
+ signalExceptionForGroupError(env, clazz, errno);
+}
+
void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
jint pid, jint pri)
{
+ if (pri == ANDROID_PRIORITY_BACKGROUND) {
+ add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT);
+ } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+ add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT);
+ }
+
if (setpriority(PRIO_PROCESS, pid, pri) < 0) {
signalExceptionForPriorityError(env, clazz, errno);
}
@@ -741,6 +819,7 @@
{"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority},
{"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority},
{"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority},
+ {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup},
{"setOomAdj", "(II)Z", (void*)android_os_Process_setOomAdj},
{"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
{"setUid", "(I)I", (void*)android_os_Process_setUid},
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 8d8d46a..b320915 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -79,6 +79,13 @@
ANDROID_PRIORITY_LESS_FAVORABLE = +1,
};
+enum {
+ ANDROID_TGROUP_DEFAULT = 0,
+ ANDROID_TGROUP_BG_NONINTERACT = 1,
+ ANDROID_TGROUP_FG_BOOST = 2,
+ ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST,
+};
+
// Create and run a new thread.
extern int androidCreateThread(android_thread_func_t, void *);