6515172: Runtime.availableProcessors() ignores Linux taskset command

Thu, 22 Sep 2016 02:04:40 -0700

author
shshahma
date
Thu, 22 Sep 2016 02:04:40 -0700
changeset 8619
3a38e441474d
parent 8618
90faa4a4b09f
child 8620
65847ffbff14

6515172: Runtime.availableProcessors() ignores Linux taskset command
Summary: extract processor count from sched_getaffinity mask
Reviewed-by: dholmes, gthornbr

src/os/linux/vm/globals_linux.hpp file | annotate | diff | comparison | revisions
src/os/linux/vm/os_linux.cpp file | annotate | diff | comparison | revisions
test/runtime/os/AvailableProcessors.java file | annotate | diff | comparison | revisions
     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 +}

mercurial