From: <jik...@li...> - 2013-08-13 09:07:52
|
details: http://hg.code.sourceforge.net/p/jikesrvm/code/rev/aa3299841d38 changeset: 10658:aa3299841d38 user: Erik Brangs <eri...@gm...> date: Tue Aug 13 11:04:47 2013 +0200 description: RVM-750 : Implement Thread.setPriority (patch by Carl Ritson). Carl Ritson's implementation uses pthread_setschedparam() to set the thread priority via the POSIX threads API. Another important point is that the implementation of sysGetThreadPriority() always returns the OS priority of the thread instead of assuming that the last call to sysSetThreadPriority() suceeded. On Linux, it is necessary to use setpriority() to manipulate the nice level of the thread instead, at least for threads running in the default SCHED_OTHER policy. This requires obtaining the thread ID (thread's process id) using the gettid() syscall. One side effect of this method is that it is not possible to achieve priorities higher than NORM_PRIORITY on Linux without root access (or changing the scheduling policy). Note that this implementation is different from the one in MRP. diffstat: build/components/classpath.xml | 6 +- build/components/patches/classpath-git.RVM-750.patch | 14 ++ build/components/patches/classpath-web.RVM-750.patch | 16 ++ rvm/src/org/jikesrvm/VM.java | 1 + rvm/src/org/jikesrvm/runtime/BootRecord.java | 3 + rvm/src/org/jikesrvm/runtime/SysCall.java | 9 + rvm/src/org/jikesrvm/scheduler/RVMThread.java | 45 +++++++- tools/bootImageRunner/sys.C | 105 +++++++++++++++++++ 8 files changed, 196 insertions(+), 3 deletions(-) diffs (truncated from 332 to 300 lines): diff --git a/build/components/classpath.xml b/build/components/classpath.xml --- a/build/components/classpath.xml +++ b/build/components/classpath.xml @@ -19,7 +19,7 @@ <!-- in target patch-classpath-web --> <property name="classpath.version" value="97.2"/> <!-- Change this whenever you alter the patches. It will let people know classpath has changed. --> - <property name="classpath.patchlevel" value="15"/> + <property name="classpath.patchlevel" value="16"/> <property name="classpath.description" value="GNU Classpath"/> <property name="classpath.component.dir" location="${components.dir}/classpath"/> @@ -111,6 +111,8 @@ dir="${classpath.package.dir}/classpath/" strip="0"/> <patch patchfile="${components.patch.dir}/classpath-git.RVM-732.patch" dir="${classpath.package.dir}/classpath/" strip="0"/> + <patch patchfile="${components.patch.dir}/classpath-git.RVM-750.patch" + dir="${classpath.package.dir}/classpath/" strip="0"/> <patch patchfile="${components.patch.dir}/classpath-git.RVM-881.patch" dir="${classpath.package.dir}/classpath/" strip="0"/> <patch patchfile="${components.patch.dir}/classpath-git.RVM-878.patch" @@ -179,6 +181,8 @@ dir="${classpath.package.dir}/classpath/" strip="0"/> <patch patchfile="${components.patch.dir}/classpath-web.RVM-732.patch" dir="${classpath.package.dir}/classpath/" strip="0"/> + <patch patchfile="${components.patch.dir}/classpath-web.RVM-750.patch" + dir="${classpath.package.dir}/classpath/" strip="0"/> <patch patchfile="${components.patch.dir}/classpath-web.RVM-878.patch" dir="${classpath.package.dir}/classpath/" strip="0"/> <patch patchfile="${components.patch.dir}/classpath-web.RVM-881.patch" diff --git a/build/components/patches/classpath-git.RVM-750.patch b/build/components/patches/classpath-git.RVM-750.patch new file mode 100644 --- /dev/null +++ b/build/components/patches/classpath-git.RVM-750.patch @@ -0,0 +1,14 @@ +# Version of RVM-750 patch that applies cleanly against the git HEAD of GNU Classpath. +--- java/lang/Thread.java 2013-08-11 16:53:57.000000000 +0200 ++++ java/lang/Thread.java 2013-08-13 09:07:45.000000000 +0200 +@@ -374,8 +374,8 @@ + this.threadId = ++totalThreadsCreated; + } + +- priority = current.priority; +- daemon = current.daemon; ++ priority = current.getPriority(); ++ daemon = current.isDaemon(); + contextClassLoader = current.contextClassLoader; + contextClassLoaderIsSystemClassLoader = + current.contextClassLoaderIsSystemClassLoader; diff --git a/build/components/patches/classpath-web.RVM-750.patch b/build/components/patches/classpath-web.RVM-750.patch new file mode 100644 --- /dev/null +++ b/build/components/patches/classpath-web.RVM-750.patch @@ -0,0 +1,16 @@ +# Carl Ritson has contributed this patch as a part of his implementation of RVMThread.setPriority(int). +# This patch ensures that newly created threads will inherit the correct priority even when it is set while they are running. +# As of 2013-08-13 this bug has not been reported upstream at GNU Classpath. +--- java/lang/Thread.java 2013-07-25 14:08:12.000000000 +0100 ++++ java/lang/Thread.java 2013-07-25 14:04:48.000000000 +0100 +@@ -374,8 +374,8 @@ + this.threadId = ++totalThreadsCreated; + } + +- priority = current.priority; +- daemon = current.daemon; ++ priority = current.getPriority(); ++ daemon = current.isDaemon(); + contextClassLoader = current.contextClassLoader; + contextClassLoaderIsSystemClassLoader = + current.contextClassLoaderIsSystemClassLoader; diff --git a/rvm/src/org/jikesrvm/VM.java b/rvm/src/org/jikesrvm/VM.java --- a/rvm/src/org/jikesrvm/VM.java +++ b/rvm/src/org/jikesrvm/VM.java @@ -160,6 +160,7 @@ // sysCall.sysSetupHardwareTrapHandler(); RVMThread.getCurrentThread().pthread_id = sysCall.sysGetThreadId(); + RVMThread.getCurrentThread().priority_handle = sysCall.sysGetThreadPriorityHandle(); RVMThread.availableProcessors = SysCall.sysCall.sysNumProcessors(); // Set up buffer locks used by Thread for logging and status dumping. diff --git a/rvm/src/org/jikesrvm/runtime/BootRecord.java b/rvm/src/org/jikesrvm/runtime/BootRecord.java --- a/rvm/src/org/jikesrvm/runtime/BootRecord.java +++ b/rvm/src/org/jikesrvm/runtime/BootRecord.java @@ -262,6 +262,9 @@ public Address sysSetupHardwareTrapHandlerIP; public Address sysStashVMThreadIP; public Address sysThreadTerminateIP; + public Address sysGetThreadPriorityHandleIP; + public Address sysGetThreadPriorityIP; + public Address sysSetThreadPriorityIP; // monitors public Address sysMonitorCreateIP; diff --git a/rvm/src/org/jikesrvm/runtime/SysCall.java b/rvm/src/org/jikesrvm/runtime/SysCall.java --- a/rvm/src/org/jikesrvm/runtime/SysCall.java +++ b/rvm/src/org/jikesrvm/runtime/SysCall.java @@ -190,6 +190,15 @@ public abstract Word sysGetThreadId(); @SysCallTemplate + public abstract Word sysGetThreadPriorityHandle(); + + @SysCallTemplate + public abstract int sysGetThreadPriority(Word thread, Word handle); + + @SysCallTemplate + public abstract int sysSetThreadPriority(Word thread, Word handle, int priority); + + @SysCallTemplate public abstract void sysSetupHardwareTrapHandler(); // This implies that the RVMThread is somehow pinned, or else the diff --git a/rvm/src/org/jikesrvm/scheduler/RVMThread.java b/rvm/src/org/jikesrvm/scheduler/RVMThread.java --- a/rvm/src/org/jikesrvm/scheduler/RVMThread.java +++ b/rvm/src/org/jikesrvm/scheduler/RVMThread.java @@ -180,6 +180,9 @@ /** Trace adjustments to stack size */ private static final boolean traceAdjustments = false; + /** Trace thread priority */ + private static final boolean tracePriority = false; + /** Never kill threads. Useful for testing bugs related to interaction of thread death with for example MMTk. For production, this should never be set to true. */ @@ -1025,6 +1028,12 @@ public Word pthread_id; /** + * Thread priority handle. Used when manipulating the threads priority. + * This may be different from pthread_id. + */ + public Word priority_handle; + + /** * Scratch area for use for gpr <=> fpr transfers by PPC baseline compiler. * Used to transfer x87 to SSE registers on IA32 */ @@ -2590,6 +2599,14 @@ * get pthread_id from the operating system and store into RVMThread field */ currentThread.pthread_id = sysCall.sysGetThreadId(); + currentThread.priority_handle = sysCall.sysGetThreadPriorityHandle(); + + /* + * set thread priority to match stored value + */ + sysCall.sysSetThreadPriority(currentThread.pthread_id, + currentThread.priority_handle, currentThread.priority - Thread.NORM_PRIORITY); + currentThread.enableYieldpoints(); sysCall.sysStashVMThread(currentThread); if (traceAcct) { @@ -4471,6 +4488,13 @@ * @see java.lang.Thread#getPriority() */ public int getPriority() { + if (isAlive()) { + // compute current priority + priority = sysCall.sysGetThreadPriority(pthread_id, priority_handle) + Thread.NORM_PRIORITY; + } + if (tracePriority) { + VM.sysWriteln("Thread #", getThreadSlot(), " get priority returning: ", priority); + } return priority; } @@ -4481,8 +4505,25 @@ * @see java.lang.Thread#getPriority() */ public void setPriority(int priority) { - this.priority = priority; - // @TODO this should be calling a syscall + if (isAlive()) { + int result = sysCall.sysSetThreadPriority(pthread_id, priority_handle, priority - Thread.NORM_PRIORITY); + if (result == 0) { + this.priority = priority; + if (tracePriority) { + VM.sysWriteln("Thread #", getThreadSlot(), " set priority: ", priority); + } + } else { + // setting priority failed + if (tracePriority) { + VM.sysWriteln("Thread #", getThreadSlot(), " failed to set priority: ", priority, ", result: ", result); + } + } + } else { + if (tracePriority) { + VM.sysWriteln("Thread #", getThreadSlot(), " set priority: ", priority, " while not running"); + } + this.priority = priority; + } } /** diff --git a/tools/bootImageRunner/sys.C b/tools/bootImageRunner/sys.C --- a/tools/bootImageRunner/sys.C +++ b/tools/bootImageRunner/sys.C @@ -34,6 +34,11 @@ extern "C" int sched_yield(void); #endif +// Enable syscall on Linux / glibc +#ifdef RVM_FOR_LINUX +#define _GNU_SOURCE +#endif + #include <stdio.h> #include <stdlib.h> // getenv() and others #include <unistd.h> @@ -41,6 +46,7 @@ #include <dirent.h> #include <fcntl.h> #include <sys/time.h> +#include <sys/resource.h> // getpriority, setpriority and PRIO_PROCESS #include <sys/types.h> #include <sys/wait.h> #include <time.h> // nanosleep() and other @@ -71,6 +77,7 @@ #include <sys/ioctl.h> #ifdef RVM_FOR_LINUX #include <asm/ioctls.h> +#include <sys/syscall.h> #endif # include <sched.h> @@ -1248,6 +1255,104 @@ #endif } +// Determine if a given thread can use pthread_setschedparam to +// configure its priority, this is based on the current priority +// of the thread. +// +// The result will be true on all systems other than Linux where +// pthread_setschedparam cannot be used with SCHED_OTHER policy. +// +static int hasPthreadPriority(Word thread_id) +{ + struct sched_param param; + int policy; + if (!pthread_getschedparam((pthread_t)thread_id, &policy, ¶m)) { + int min = sched_get_priority_min(policy); + int max = sched_get_priority_max(policy); + if (min || max) { + return 1; + } + } + return 0; +} + +// Return a handle which can be used to manipulate a threads priority +// on Linux this will be the kernel thread_id, on other systems the +// standard thread id. +extern "C" Word +sysGetThreadPriorityHandle() +{ + // gettid() syscall is Linux specific, detect its syscall number macro + #ifdef SYS_gettid + pid_t tid = (pid_t) syscall(SYS_gettid); + if (tid != -1) + return (Word) tid; + #endif /* SYS_gettid */ + return (Word) getThreadId(); +} + +// Compute the default (or middle) priority for a given policy. +static int defaultPriority(int policy) +{ + int min = sched_get_priority_min(policy); + int max = sched_get_priority_max(policy); + return min + ((max - min) / 2); +} + +// Get the thread priority as an offset from the default. +extern "C" int +sysGetThreadPriority(Word thread, Word handle) +{ + // use pthread priority mechanisms where possible + if (hasPthreadPriority(thread)) { + struct sched_param param; + int policy; + if (!pthread_getschedparam((pthread_t)thread, &policy, ¶m)) { + return param.sched_priority - defaultPriority(policy); + } + } else if (thread != handle) { + // fallback to setpriority if handle is valid + // i.e. handle is tid from gettid() + int result; + errno = 0; // as result can be legally be -1 + result = getpriority(PRIO_PROCESS, (int) handle); + if (errno == 0) { + // default priority is 0, low number -> high priority + return -result; + } + + } + return 0; +} |