Thu, 22 Sep 2016 02:04:40 -0700
6515172: Runtime.availableProcessors() ignores Linux taskset command
Summary: extract processor count from sched_getaffinity mask
Reviewed-by: dholmes, gthornbr
1.1 --- a/src/os/linux/vm/globals_linux.hpp Tue Apr 05 13:55:31 2016 +0200 1.2 +++ b/src/os/linux/vm/globals_linux.hpp Thu Sep 22 02:04:40 2016 -0700 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -47,7 +47,10 @@ 1.11 "Load DLLs with executable-stack attribute in the VM Thread") \ 1.12 \ 1.13 product(bool, UseSHM, false, \ 1.14 - "Use SYSV shared memory for large pages") 1.15 + "Use SYSV shared memory for large pages") \ 1.16 + \ 1.17 + diagnostic(bool, PrintActiveCpus, false, \ 1.18 + "Print the number of CPUs detected in os::active_processor_count") 1.19 1.20 // 1.21 // Defines Linux-specific default values. The flags are available on all
2.1 --- a/src/os/linux/vm/os_linux.cpp Tue Apr 05 13:55:31 2016 +0200 2.2 +++ b/src/os/linux/vm/os_linux.cpp Thu Sep 22 02:04:40 2016 -0700 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -104,6 +104,14 @@ 2.11 2.12 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC 2.13 2.14 +#ifndef _GNU_SOURCE 2.15 + #define _GNU_SOURCE 2.16 + #include <sched.h> 2.17 + #undef _GNU_SOURCE 2.18 +#else 2.19 + #include <sched.h> 2.20 +#endif 2.21 + 2.22 // if RUSAGE_THREAD for getrusage() has not been defined, do it here. The code calling 2.23 // getrusage() is prepared to handle the associated failure. 2.24 #ifndef RUSAGE_THREAD 2.25 @@ -5016,12 +5024,42 @@ 2.26 } 2.27 }; 2.28 2.29 +static int os_cpu_count(const cpu_set_t* cpus) { 2.30 + int count = 0; 2.31 + // only look up to the number of configured processors 2.32 + for (int i = 0; i < os::processor_count(); i++) { 2.33 + if (CPU_ISSET(i, cpus)) { 2.34 + count++; 2.35 + } 2.36 + } 2.37 + return count; 2.38 +} 2.39 + 2.40 +// Get the current number of available processors for this process. 2.41 +// This value can change at any time during a process's lifetime. 2.42 +// sched_getaffinity gives an accurate answer as it accounts for cpusets. 2.43 +// If anything goes wrong we fallback to returning the number of online 2.44 +// processors - which can be greater than the number available to the process. 2.45 int os::active_processor_count() { 2.46 - // Linux doesn't yet have a (official) notion of processor sets, 2.47 - // so just return the number of online processors. 2.48 - int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); 2.49 - assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); 2.50 - return online_cpus; 2.51 + cpu_set_t cpus; // can represent at most 1024 (CPU_SETSIZE) processors 2.52 + int cpus_size = sizeof(cpu_set_t); 2.53 + int cpu_count = 0; 2.54 + 2.55 + // pid 0 means the current thread - which we have to assume represents the process 2.56 + if (sched_getaffinity(0, cpus_size, &cpus) == 0) { 2.57 + cpu_count = os_cpu_count(&cpus); 2.58 + if (PrintActiveCpus) { 2.59 + tty->print_cr("active_processor_count: sched_getaffinity processor count: %d", cpu_count); 2.60 + } 2.61 + } 2.62 + else { 2.63 + cpu_count = ::sysconf(_SC_NPROCESSORS_ONLN); 2.64 + warning("sched_getaffinity failed (%s)- using online processor count (%d) " 2.65 + "which may exceed available processors", strerror(errno), cpu_count); 2.66 + } 2.67 + 2.68 + assert(cpu_count > 0 && cpu_count <= processor_count(), "sanity check"); 2.69 + return cpu_count; 2.70 } 2.71 2.72 void os::set_native_thread_name(const char *name) {
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/runtime/os/AvailableProcessors.java Thu Sep 22 02:04:40 2016 -0700 3.3 @@ -0,0 +1,103 @@ 3.4 +/* 3.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.23 + * or visit www.oracle.com if you need additional information or have any 3.24 + * questions. 3.25 + */ 3.26 +import java.io.File; 3.27 +import com.oracle.java.testlibrary.ProcessTools; 3.28 +import com.oracle.java.testlibrary.OutputAnalyzer; 3.29 +import java.util.ArrayList; 3.30 + 3.31 +/* 3.32 + * @test 3.33 + * @bug 6515172 3.34 + * @summary Check that availableProcessors reports the correct value when running in a cpuset on linux 3.35 + * @requires os.family == "linux" 3.36 + * @library /testlibrary 3.37 + * @build com.oracle.java.testlibrary.* 3.38 + * @run driver AvailableProcessors 3.39 + */ 3.40 +public class AvailableProcessors { 3.41 + 3.42 + static final String SUCCESS_STRING = "Found expected processors: "; 3.43 + 3.44 + public static void main(String[] args) throws Throwable { 3.45 + if (args.length > 0) 3.46 + checkProcessors(Integer.parseInt(args[0])); 3.47 + else { 3.48 + // run ourselves under different cpu configurations 3.49 + // using the taskset command 3.50 + String taskset; 3.51 + final String taskset1 = "/bin/taskset"; 3.52 + final String taskset2 = "/usr/bin/taskset"; 3.53 + if (new File(taskset1).exists()) 3.54 + taskset = taskset1; 3.55 + else if (new File(taskset2).exists()) 3.56 + taskset = taskset2; 3.57 + else { 3.58 + System.out.println("Skipping test: could not find taskset command"); 3.59 + return; 3.60 + } 3.61 + 3.62 + int available = Runtime.getRuntime().availableProcessors(); 3.63 + 3.64 + if (available == 1) { 3.65 + System.out.println("Skipping test: only one processor available"); 3.66 + return; 3.67 + } 3.68 + 3.69 + // Get the java command we want to execute 3.70 + // Enable logging for easier failure diagnosis 3.71 + ProcessBuilder master = 3.72 + ProcessTools.createJavaProcessBuilder(false, 3.73 + "-XX:+UnlockDiagnosticVMOptions", 3.74 + "-XX:+PrintActiveCpus", 3.75 + "AvailableProcessors"); 3.76 + 3.77 + int[] expected = new int[] { 1, available/2, available-1, available }; 3.78 + 3.79 + for (int i : expected) { 3.80 + System.out.println("Testing for " + i + " processors ..."); 3.81 + int max = i - 1; 3.82 + ArrayList<String> cmdline = new ArrayList<>(master.command()); 3.83 + // prepend taskset command 3.84 + cmdline.add(0, "0-" + max); 3.85 + cmdline.add(0, "-c"); 3.86 + cmdline.add(0, taskset); 3.87 + // append expected processor count 3.88 + cmdline.add(String.valueOf(i)); 3.89 + ProcessBuilder pb = new ProcessBuilder(cmdline); 3.90 + System.out.println("Final command line: " + 3.91 + ProcessTools.getCommandLine(pb)); 3.92 + OutputAnalyzer output = ProcessTools.executeProcess(pb); 3.93 + output.shouldContain(SUCCESS_STRING); 3.94 + } 3.95 + } 3.96 + } 3.97 + 3.98 + static void checkProcessors(int expected) { 3.99 + int available = Runtime.getRuntime().availableProcessors(); 3.100 + if (available != expected) 3.101 + throw new Error("Expected " + expected + " processors, but found " 3.102 + + available); 3.103 + else 3.104 + System.out.println(SUCCESS_STRING + available); 3.105 + } 3.106 +}