[m-rev.] For review: Thread pinning improvements.

Paul Bone pbone at csse.unimelb.edu.au
Wed Oct 5 01:34:57 AEDT 2011


For review by anyone:

While I'm confident that my runtime changes do the right thing I wouldn't mind
if someone would look over the build system changes.

Thanks.

---

Improve thread pinning:
    + Now pins threads intelligently on SMT systems by balancing threads among
      cores.
    + performs fewer migrations when pinning threads (If a thread's current
      CPU is a valid CPU for pinning, then it is not migrated).
    + Handle cases where the user requests more threads than available CPUs.
    + Handle cases where the process is restricted to a subset of CPUs by its
      environment.  (for instance, Linux cpuset(7))

This is largely made possible by the hwloc library
http://www.open-mpi.org/projects/hwloc/  However, hwloc is not required and the
runtime system will fall back to sched_setaffinity(), it will simply be less
intelligent WRT SMT.

runtime/mercury_context.h:
runtime/mercury_context.c:
    Do thread pinning either via hwloc or sched_setaffinity.  Previously only
    sched_setaffinity was used.

    Update thread-pinning algorithm, this:

    Include the general thread pinning code only if MR_HAVE_THREAD_PINNING is
    defined.

    Use a combination of sysconf and sched_getaffinity to detect the number of
    processors when hwloc isn't available.  This makes the runtime compatible
    with Linux cpuset(7) when hwloc isn't available.

configure.in:
Mmake.common.in:
    Detect presence of the hwloc library.

configure.in:
    Detect sched_getaffinity()

aclocal.m4:
acinclude.m4:
    Move aclocal.m4 to acinclude.m4, the aclocal program will build aclocal.m4
    and retrieve macros from the system and the contents of acinclude.m4.

Mmakefile:
    Create a make target for aclocal.m4.

runtime/Mmakefile:
    Link the runtime with libhwloc in low-level C parallel grades.

    Include CFLAGS for libhwloc.

scripts/ml.in:
    Link programs and libraries with libhwloc in low-level C parallel grades.

runtime/mercury_conf.h.in:
    Define MR_HAVE_HWLOC when it is available.

    Define MR_HAVE_SCHED_GETAFFINITY when it is available.

runtime/mercury_conf_param.h:
    Define MR_HAVE_THREAD_PINNING if either hwloc or [sched_setaffinity and
    sched_getaffinity] are available.

runtime/mercury_thread.c:
runtime/mercury_wrapper.c:
    Only call MR_pin_thread and MR_pin_primordial_thread if
    MR_HAVE_THREAD_PINNING is defined.

runtime/mercury_thread.h:
runtime/mercury_context.h:
    Move the declaration of MR_pin_primordial_thread to mercury_context.h from
    mercury_thead.h since it's definition is in mercury_context.c.

    Require MR_HAVE_THREAD_PINNING for the declaration of
    MR_pin_primordial_thread.

runtime/mercury_wrapper.c:
    Conform to changes in mercury_context.h

Index: Mmake.common.in
===================================================================
RCS file: /home/mercury1/repository/mercury/Mmake.common.in,v
retrieving revision 1.103
diff -u -p -b -r1.103 Mmake.common.in
--- Mmake.common.in	17 Dec 2010 11:59:02 -0000	1.103
+++ Mmake.common.in	4 Oct 2011 12:19:52 -0000
@@ -234,6 +234,8 @@ NSL_LIBRARY=@NSL_LIBRARY@
 DL_LIBRARY=@DL_LIBRARY@
 READLINE_LIBRARIES=@READLINE_LIBRARIES@
 TERMCAP_LIBRARY=@TERMCAP_LIBRARY@
+HWLOC_CFLAGS=@HWLOC_CFLAGS@
+HWLOC_LIBS=@HWLOC_LIBS@
 
 # Extensions to use
 O=@OBJ_SUFFIX@
Index: Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/Mmakefile,v
retrieving revision 1.145
diff -u -p -b -r1.145 Mmakefile
--- Mmakefile	27 Sep 2011 04:41:21 -0000	1.145
+++ Mmakefile	4 Oct 2011 12:19:52 -0000
@@ -370,6 +370,9 @@ cleanint:
 
 #-----------------------------------------------------------------------------#
 
+aclocal.m4: configure.in acinclude.m4
+	aclocal
+
 configure: configure.in aclocal.m4
 	autoconf
 
Index: acinclude.m4
===================================================================
RCS file: acinclude.m4
diff -N acinclude.m4
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ acinclude.m4	4 Oct 2011 12:19:52 -0000
@@ -0,0 +1,665 @@
+#-----------------------------------------------------------------------------#
+# Copyright (C) 1999,2001-2004, 2006-2011 The University of Melbourne.
+# This file may only be copied under the terms of the GNU General
+# Public Licence - see the file COPYING in the Mercury distribution.
+#-----------------------------------------------------------------------------#
+#
+# aclocal.m4
+#
+# This file contains Mercury-specific autoconf tests.
+#
+# We ought to move most of the code in configure.in into this file...
+#
+#-----------------------------------------------------------------------------#
+AC_DEFUN(MERCURY_CHECK_FOR_HEADERS,
+[
+    for mercury_cv_header in $1; do
+	mercury_cv_header_define="MR_HAVE_`echo $mercury_cv_header | \
+		tr abcdefghijklmnopqrstuvwxyz./ ABCDEFGHIJKLMNOPQRSTUVWXYZ__`"
+	AC_CHECK_HEADER($mercury_cv_header, [
+		AC_DEFINE_UNQUOTED($mercury_cv_header_define)
+		eval "$mercury_cv_header_define=1"
+	])
+    done
+])
+#-----------------------------------------------------------------------------#
+AC_DEFUN(MERCURY_CHECK_FOR_IEEEFP_H,
+[
+	MERCURY_CHECK_FOR_HEADERS(ieeefp.h)
+])
+
+AC_DEFUN(MERCURY_CHECK_FOR_IEEE_FUNC,
+[
+AC_REQUIRE([MERCURY_CHECK_FOR_IEEEFP_H])
+AC_MSG_CHECKING(for $1 function)
+mercury_cv_ieee_func_define="MR_HAVE_`echo $1 | \
+	tr abcdefghijklmnopqrstuvwxyz./ ABCDEFGHIJKLMNOPQRSTUVWXYZ__`"
+
+AC_TRY_LINK([
+	#include <math.h>
+#ifdef MR_HAVE_IEEEFP_H
+	#include <ieeefp.h>
+#endif
+],[
+	float f;
+	$1(f);
+],[mercury_cv_have_ieee_func=yes],[mercury_cv_have_ieee_func=no])
+
+if test "$mercury_cv_have_ieee_func" = yes; then
+	AC_MSG_RESULT(yes)
+	AC_DEFINE_UNQUOTED($mercury_cv_ieee_func_define)
+else
+	AC_MSG_RESULT(no)
+fi
+])
+
+#-----------------------------------------------------------------------------#
+
+# Test for C99 fp environment functions in the math library.
+# The second argument to this macro should be the flags required by the
+# C compiler to link against the math library.  This is needed because
+# some OSs, e.g. Mac OS X, don't have a separate math library.
+
+AC_DEFUN([MERCURY_CHECK_FOR_FENV_FUNC],
+[
+AC_MSG_CHECKING(for $1 function)
+mercury_cv_math_func_define="MR_HAVE_`echo $1 | \
+	tr abcdefghijklmnopqrstuvwxyz./ ABCDEFGHIJKLMNOPQRSTUVWXYZ__`"
+
+save_libs="$LIBS"
+LIBS="$2 $LIBS"
+
+AC_TRY_LINK([
+#ifdef MR_HAVE_FENV_H
+	#include <fenv.h>
+#endif
+],[
+
+	int i = 0;
+	$1(i);
+],[mercury_cv_have_math_func=yes],[mercury_cv_have_math_func=no])
+
+LIBS="$save_libs"
+
+if test "$mercury_cv_have_math_func" = "yes"
+then
+	AC_MSG_RESULT([yes])
+	AC_DEFINE_UNQUOTED([$mercury_cv_math_func_define])
+else
+	AC_MSG_RESULT([no])
+fi
+])
+
+#-----------------------------------------------------------------------------#
+#
+# Turn off MacOS's so-called "smart" C preprocessor, if present,
+# since it causes lots of spurious warning messages,
+# and furthermore it takes way too long and uses way too much memory
+# when preprocessing the C code generated by the Mercury compiler's LLDS
+# back-end.
+#
+AC_DEFUN(MERCURY_CHECK_CC_NEEDS_TRAD_CPP,
+[
+AC_REQUIRE([AC_PROG_CC])
+AC_MSG_CHECKING(whether C compiler needs -no-cpp-precomp)
+AC_CACHE_VAL(mercury_cv_cpp_precomp, [
+	>conftest.c
+	if	test "$GCC" = yes &&
+		$CC -v -c conftest.c 2>&1 | \
+			grep "cpp-precomp.*-smart" > /dev/null
+	then
+		mercury_cv_cpp_precomp=yes
+	else
+		mercury_cv_cpp_precomp=no
+	fi
+])
+AC_MSG_RESULT($mercury_cv_cpp_precomp)
+if test $mercury_cv_cpp_precomp = yes; then
+	CC="$CC -no-cpp-precomp"
+fi
+])
+#-----------------------------------------------------------------------------#
+#
+# Check whether we need to add any extra directories to the search path for
+# header files, and set ALL_LOCAL_C_INCL_DIRS to the -I option(s) needed
+# for this, if any.
+#
+# GNU C normally searches /usr/local/include by default;
+# to keep things consistent, we do the same for other C compilers.
+#
+AC_DEFUN(MERCURY_CHECK_LOCAL_C_INCL_DIRS,
+[
+AC_REQUIRE([AC_PROG_CC])
+AC_MSG_CHECKING(whether to pass -I/usr/local/include to C compiler)
+ALL_LOCAL_C_INCL_DIRS=""
+ALL_LOCAL_C_INCL_DIR_MMC_OPTS=""
+
+if test "$GCC" = yes -o "$USING_MICROSOFT_CL_COMPILER" = yes; then
+	# Don't add -I/usr/local/include, since it causes a warning
+	# with gcc 3.1, and gcc already searches /usr/local/include.
+	# Microsoft compilers don't understand Unix pathnames.
+	AC_MSG_RESULT(no)
+else
+	# It's some other compiler.  We don't know if it searches
+	# /usr/local/include by default, so add it.
+	if test -d /usr/local/include/.; then
+		AC_MSG_RESULT(yes)
+		ALL_LOCAL_C_INCL_DIRS="-I/usr/local/include "
+		ALL_LOCAL_C_INCL_DIR_MMC_OPTS="--c-include-directory /usr/local/include "
+	else
+		AC_MSG_RESULT(no)
+	fi
+fi
+AC_SUBST(ALL_LOCAL_C_INCL_DIRS)
+AC_SUBST(ALL_LOCAL_C_INCL_DIR_MMC_OPTS)
+])
+#-----------------------------------------------------------------------------#
+#
+# Set ALL_LOCAL_C_LIB_DIRS to any extra directories we need to add to the
+# search path for libraries.
+#
+AC_DEFUN(MERCURY_CHECK_LOCAL_C_LIB_DIRS,
+[
+AC_MSG_CHECKING(whether to pass -L/usr/local/lib to the linker)
+
+# Microsoft compilers don't understand Unix pathnames.
+if test "$USING_MICROSOFT_CL_COMPILER" = no -a -d /usr/local/lib/.; then
+	AC_MSG_RESULT(yes)
+	ALL_LOCAL_C_LIB_DIRS=/usr/local/lib
+	ALL_LOCAL_C_LIB_DIR_MMC_OPTS="-L/usr/local/lib -R/usr/local/lib"
+else
+	AC_MSG_RESULT(no)
+	ALL_LOCAL_C_LIB_DIRS=
+	ALL_LOCAL_C_LIB_DIR_MMC_OPTS=
+fi
+AC_SUBST(ALL_LOCAL_C_LIB_DIRS)
+AC_SUBST(ALL_LOCAL_C_LIB_DIR_MMC_OPTS)
+])
+
+#-----------------------------------------------------------------------------#
+#
+# Check for readline and related header files and libraries
+#
+AC_DEFUN(MERCURY_CHECK_READLINE,
+[
+AC_ARG_WITH(readline,
+[  --without-readline      Don't use the GPL'd GNU readline library],
+mercury_cv_with_readline="$withval", mercury_cv_with_readline=yes)
+
+if test "$mercury_cv_with_readline" = yes; then
+
+	# check for the readline header files
+	MERCURY_CHECK_FOR_HEADERS(readline/readline.h readline/history.h)
+
+	# check for the libraries that readline depends on
+	MERCURY_MSG('looking for termcap or curses (needed by readline)...')
+	AC_CHECK_LIB(termcap, tgetent, mercury_cv_termcap_lib=-ltermcap,
+	 [AC_CHECK_LIB(curses,  tgetent, mercury_cv_termcap_lib=-lcurses,
+	  [AC_CHECK_LIB(ncurses, tgetent, mercury_cv_termcap_lib=-lncurses,
+	   mercury_cv_termcap_lib='')])])
+
+	# check for the readline library
+	AC_CHECK_LIB(readline, readline, mercury_cv_have_readline=yes,
+		mercury_cv_have_readline=no, $mercury_cv_termcap_lib)
+else
+	mercury_cv_have_readline=no
+fi
+
+# Now figure out whether we can use readline, and define variables according.
+# Note that on most systems, we don't actually need the header files in
+# order to use readline. (Ain't C grand? ;-).
+
+if test $mercury_cv_have_readline = no; then
+	TERMCAP_LIBRARY=""
+	READLINE_LIBRARIES=""
+	AC_DEFINE(MR_NO_USE_READLINE)
+else
+	TERMCAP_LIBRARY="$mercury_cv_termcap_lib"
+	READLINE_LIBRARIES="-lreadline $TERMCAP_LIBRARY"
+fi
+AC_SUBST(TERMCAP_LIBRARY)
+AC_SUBST(READLINE_LIBRARIES)
+
+])
+
+#-----------------------------------------------------------------------------#
+#
+# Microsoft.NET configuration
+#
+AC_DEFUN(MERCURY_CHECK_DOTNET,
+[
+AC_PATH_PROG(ILASM, ilasm)
+AC_PATH_PROG(GACUTIL, gacutil)
+
+AC_MSG_CHECKING(for Microsoft.NET Framework SDK)
+AC_CACHE_VAL(mercury_cv_microsoft_dotnet, [
+if test "$ILASM" != ""; then
+	changequote(<<,>>) 
+	MS_DOTNET_SDK_DIR=`expr "$ILASM" : '\(.*\)[/\\]*[bB]in[/\\]*ilasm'`
+	changequote([,]) 
+	mercury_cv_microsoft_dotnet="yes"
+else
+	MS_DOTNET_SDK_DIR=""
+	mercury_cv_microsoft_dotnet="no"
+fi
+])
+AC_MSG_RESULT($mercury_cv_microsoft_dotnet)
+ILASM=`basename "$ILASM"`
+GACUTIL=`basename "$GACUTIL"`
+
+# Check for the C# (C sharp) compiler.
+# gmcs is the Mono C# compiler targeting the 2.0 runtime (with generics).
+# cscc is the DotGNU C# compiler.
+AC_PATH_PROGS(CSC, csc gmcs cscc)
+CSC=`basename "$CSC"`
+
+# We default to the Beta 2 version of the library
+mercury_cv_microsoft_dotnet_library_version=1.0.2411.0
+if	test $mercury_cv_microsoft_dotnet = "yes" &&
+	test "$CSC" != "";
+then
+	AC_MSG_CHECKING(version of .NET libraries)
+	cat > conftest.cs << EOF
+	using System;
+	using System.Reflection;
+	public class version {
+	    public static void Main()
+	    {
+		Assembly asm = Assembly.Load("mscorlib");
+		AssemblyName name = asm.GetName();
+		Version version = name.Version;
+		Console.Write(version);
+		Console.Write("\n");
+	    }
+	}
+EOF
+	if
+		echo $CSC conftest.cs >&AC_FD_CC 2>&1 && \
+			$CSC conftest.cs  >&AC_FD_CC 2>&1 && \
+			./conftest > conftest.out 2>&1
+	then
+		mercury_cv_microsoft_dotnet_library_version=`cat conftest.out`
+		AC_MSG_RESULT($mercury_cv_microsoft_dotnet_library_version)
+		rm -f conftest*
+	else
+		rm -f conftest*
+		if test "$enable_dotnet_grades" = "yes"; then
+			AC_MSG_ERROR(unable to determine version)
+			exit 1
+		else
+			AC_MSG_WARN(unable to determine version)
+		fi
+	fi
+fi
+MS_DOTNET_LIBRARY_VERSION=$mercury_cv_microsoft_dotnet_library_version
+
+# Check for the assembly linker.
+# ilalink is the DotGNU assembly linker.
+AC_PATH_PROGS(MS_AL, al ilalink)
+MS_AL=`basename "$MS_AL"`
+
+# Check for an implementation of the Common Language Infrastructure.
+AC_PATH_PROGS(CLI_INTERPRETER, mono)
+MONO=`basename "$CLI_INTERPRETER"`
+
+AC_SUBST(ILASM)
+AC_SUBST(GACUTIL)
+AC_SUBST(CSC)
+AC_SUBST(MS_AL)
+AC_SUBST(MS_DOTNET_SDK_DIR)
+AC_SUBST(MS_DOTNET_LIBRARY_VERSION)
+AC_SUBST(MS_VISUALCPP_DIR)
+AC_SUBST(CLI_INTERPRETER)
+])
+
+#-----------------------------------------------------------------------------#
+#
+# Java configuration
+#
+AC_DEFUN(MERCURY_CHECK_JAVA,
+[
+# jikes requires the usual Java SDK to run, so if we checked for javac first,
+# then that's what we'd get. If the user has jikes installed, then that
+# probably means that they want to use it, so we check for jikes before javac.
+# On Windows, the Java SDK has a high chance of being installed in a path
+# containing spaces.  The simplest solution is to keep only the basename.
+# Everything will still work so long as the executables can be found on the
+# PATH later.
+AC_PATH_PROGS(JAVAC, jikes javac gcj)
+case "$JAVAC" in
+    *" "*)
+        JAVAC=`basename "$JAVAC"`
+        ;;
+esac
+case "$JAVAC" in
+    *gcj)
+        JAVAC="$JAVAC -C"
+        ;;
+esac
+AC_PATH_PROG(JAVA_INTERPRETER, java gij)
+case "$JAVA_INTERPRETER" in
+    *" "*)
+        JAVA_INTERPRETER=`basename "$JAVA_INTERPRETER"`
+        ;;
+esac
+AC_PATH_PROG(JAR, jar)
+case "$JAR" in
+    *" "*)
+        JAR=`basename "$JAR"`
+        ;;
+esac
+
+AC_CACHE_VAL(mercury_cv_java, [
+if test "$JAVAC" != "" -a "$JAVA_INTERPRETER" != "" -a "$JAR" != ""; then
+	AC_MSG_CHECKING(if the above Java SDK works and is sufficiently recent)
+	cat > conftest.java << EOF
+		// This program simply retrieves the constant
+		// specifying the version number of the Java SDK and
+		// checks it is at least 1.5, printing "Hello, world"
+		// if successful.
+		public class conftest {
+		    public static void main (String[[]] args) {
+			float	version;
+			String	strVer = System.getProperty(
+					"java.specification.version");
+
+			try {
+				version = Float.valueOf(strVer).floatValue();
+			}
+			catch (NumberFormatException e) {
+				System.out.println("ERROR: \"java." +
+						"specification.version\" " +
+						"constant has incorrect " +
+						"format.\nGot \"" + strVer +
+						"\", expected a number.");
+				version = 0f;
+			}
+
+			if (version >= 1.5f) {
+				System.out.println("Hello, world\n");
+			} else {
+				System.out.println("Nope, sorry.\n");
+			}
+		    }
+		}
+EOF
+	if
+		echo "$JAVAC" conftest.java >&AC_FD_CC 2>&1 &&
+		"$JAVAC" conftest.java >&AC_FD_CC 2>&1 &&
+		echo "$JAVA_INTERPRETER" conftest > conftest.out 2>&AC_FD_CC &&
+		CLASSPATH=. "$JAVA_INTERPRETER" conftest > conftest.out 2>&AC_FD_CC &&
+		test "`tr -d '\015' < conftest.out`" = "Hello, world"
+	then
+		mercury_cv_java="yes"
+	else
+		mercury_cv_java="no"
+	fi
+	AC_MSG_RESULT($mercury_cv_java)
+else
+	if test "$JAVAC" = ""; then
+		JAVAC="javac"
+	fi
+	if test "$JAVA_INTERPRETER" = ""; then
+		JAVA_INTERPRETER="java"
+	fi
+	if test "$JAR" = ""; then
+		JAR="jar"
+	fi
+	mercury_cv_java="no"
+fi
+])
+
+AC_SUBST(JAVAC)
+AC_SUBST(JAVA_INTERPRETER)
+AC_SUBST(JAR)
+])
+
+AC_DEFUN(MERCURY_CHECK_JAVAC_HEAP_SIZE,
+[
+# The default maximum heap size is too small to build the standard library and
+# other programs so we need to increase it.  The option to do that is
+# non-standard so we have to check that it is accepted.
+AC_CACHE_VAL(mercury_cv_javac_flags_for_heap_size, [
+if test "$mercury_cv_java" = "yes"; then
+	AC_MSG_CHECKING(if the Java compiler accepts the max heap size option)
+	mercury_cv_javac_flags_for_heap_size="-J-Xmx256m"
+	if "$JAVAC" "$mercury_cv_javac_flags_for_heap_size" -version \
+                2> /dev/null
+        then
+		AC_MSG_RESULT(yes)
+	else
+		AC_MSG_RESULT(no)
+		mercury_cv_javac_flags_for_heap_size=
+	fi
+else
+	mercury_cv_javac_flags_for_heap_size=
+fi
+])
+])
+
+#-----------------------------------------------------------------------------#
+#
+# Erlang configuration
+#
+
+# copy of AC_ERLANG_PATH_ERLC from autoconf-2.60
+AC_DEFUN([MERCURY_ERLANG_PATH_ERLC],
+[AC_ARG_VAR([ERLC], [Erlang/OTP compiler command [autodetected]])dnl
+if test -n "$ERLC"; then
+    AC_MSG_CHECKING([for erlc])
+    AC_MSG_RESULT([$ERLC])
+else
+    AC_PATH_TOOL(ERLC, erlc, [$1], [$2])
+fi
+AC_ARG_VAR([ERLCFLAGS], [Erlang/OTP compiler flags [none]])dnl
+])
+
+# copy of AC_ERLANG_PATH_ERL from autoconf-2.60
+AC_DEFUN([MERCURY_ERLANG_PATH_ERL],
+[AC_ARG_VAR([ERL], [Erlang/OTP interpreter command [autodetected]])dnl
+if test -n "$ERL"; then
+    AC_MSG_CHECKING([for erl])
+    AC_MSG_RESULT([$ERL])
+else
+    AC_PATH_TOOL(ERL, erl, [$1], [$2])[]dnl
+fi
+])
+
+AC_DEFUN(MERCURY_CHECK_ERLANG,
+[
+MERCURY_ERLANG_PATH_ERLC
+MERCURY_ERLANG_PATH_ERL
+
+if test "$ERLC" != "" -a "$ERL" != ""; then
+	mercury_cv_erlang="yes"
+else
+	mercury_cv_erlang="no"
+fi
+
+AC_SUBST(ERLC)
+AC_SUBST(ERL)
+])
+
+#-----------------------------------------------------------------------------#
+
+# NOTE: updates to this macro may need to be reflected in compiler/globals.m.
+
+AC_DEFUN([MERCURY_GCC_VERSION], [
+AC_REQUIRE([AC_PROG_CC])
+
+cat > conftest.c << EOF
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+
+#if defined(__GNUC__)
+    printf("%d_", __GNUC__);
+    #if defined(__GNUC_MINOR__)
+        printf("%d_", __GNUC_MINOR__);
+    #else
+        printf("u_");
+    #endif /* ! __GNUC_MINOR__ */
+    #if defined(__GNUC_PATCHLEVEL__)
+    	printf("%d", __GNUC_PATCHLEVEL__);
+    #else
+        printf("u");
+    #endif /* ! __GNUC_PATCHLEVEL__ */
+#endif /* __GNUC__ */
+
+	return 0;
+}
+EOF
+
+echo "$CC -o conftest contest.c" >&AC_FD_CC 2>&1
+if
+    $CC -o conftest conftest.c
+then
+    mercury_cv_gcc_version=`./conftest`
+else
+    # This shouldn't happen as we have already checked for this.
+    AC_MSG_ERROR([unexpected: $CC cannot create executable])
+fi
+])
+
+#-----------------------------------------------------------------------------#
+
+# Work out the C compiler type using a stronger test than AC_PROG_CC to
+# distinguish between clang and gcc.
+# (We don't handle lcc here - I don't think that it's possible to.)
+
+AC_DEFUN([MERCURY_CC_TYPE], [
+AC_REQUIRE([AC_PROG_CC])
+AC_MSG_CHECKING([what the C compiler type really is])
+
+cat > conftest.c << EOF
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+    #if defined(__clang__)
+       printf("clang");
+    #elif defined(__GNUC__)
+       printf("gcc");
+    #elif defined(_MSC_VER)
+       printf("msvc");
+    #else
+       printf("unknown");
+    #endif
+   
+    return 0;
+}
+EOF
+
+echo "$CC -o conftest conftest.c" >&AC_FD_CC 2>&1
+if
+    $CC -o conftest conftest.c
+then
+    mercury_cv_cc_type=`./conftest`
+else
+    # This shouldn't happen as we have already checked for this.
+    AC_MSG_ERROR([unexpected: $CC cannot create executable])
+fi
+
+AC_MSG_RESULT([$mercury_cv_cc_type])
+])
+
+#-----------------------------------------------------------------------------#
+
+AC_DEFUN([MERCURY_CLANG_VERSION], [
+AC_REQUIRE([AC_PROG_CC])
+
+cat > conftest.c << EOF
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+
+    printf("%d_%d_%d", __clang_major__, __clang_minor__, __clang_patchlevel__);
+    return 0;
+}
+EOF
+
+echo "$CC -o conftest contest.c" >&AC_FD_CC 2>&1
+if
+    $CC -o conftest conftest.c
+then
+    mercury_cv_clang_version=`./conftest`
+else
+    # This shouldn't happen as we have already checked for this.
+    AC_MSG_ERROR([unexpected: $CC cannot create executable])
+fi
+])
+
+#-----------------------------------------------------------------------------#
+
+AC_DEFUN([MERCURY_MSVC_VERSION], [
+AC_REQUIRE([AC_PROG_CC])
+
+cat > conftest.c << EOF
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+
+    printf("%d", _MSC_VER);
+    return 0;
+}
+EOF
+
+echo "$CC conftest.c -Fecontest" >&AC_FD_CC 2>&1
+if
+    $CC conftest.c -Feconftest > /dev/null
+then
+    mercury_cv_msvc_version=`./conftest`
+else
+    # This shouldn't happen as we have already checked for this.
+    AC_MSG_ERROR([unexpected: $CC cannot create executable])
+fi
+])
+
+#-----------------------------------------------------------------------------#
+#
+# Check if the POSIX threads library is pthreads-win32.
+#
+
+AC_DEFUN([MERCURY_HAVE_PTHREADS_WIN32], [
+
+AC_MSG_CHECKING([if we are using pthreads-win32])
+
+cat > conftest.c << EOF
+
+#include <pthread.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+
+#if defined(PTW32_VERSION)
+    return 0;
+#else
+    return 1;
+#endif
+
+}
+
+EOF
+
+echo "$CC -o conftest contest.c" >&AC_FD_CC 2>&1
+if
+    $CC -o conftest conftest.c
+then
+    mercury_cv_have_pthreads_win32="yes"
+else
+    mercury_cv_have_pthreads_win32="no"
+fi
+
+AC_MSG_RESULT($mercury_cv_have_pthreads_win32)
+
+])
+
+#-----------------------------------------------------------------------------#
Index: aclocal.m4
===================================================================
RCS file: aclocal.m4
diff -N aclocal.m4
--- aclocal.m4	3 Sep 2011 17:50:32 -0000	1.39
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,665 +0,0 @@
-#-----------------------------------------------------------------------------#
-# Copyright (C) 1999,2001-2004, 2006-2011 The University of Melbourne.
-# This file may only be copied under the terms of the GNU General
-# Public Licence - see the file COPYING in the Mercury distribution.
-#-----------------------------------------------------------------------------#
-#
-# aclocal.m4
-#
-# This file contains Mercury-specific autoconf tests.
-#
-# We ought to move most of the code in configure.in into this file...
-#
-#-----------------------------------------------------------------------------#
-AC_DEFUN(MERCURY_CHECK_FOR_HEADERS,
-[
-    for mercury_cv_header in $1; do
-	mercury_cv_header_define="MR_HAVE_`echo $mercury_cv_header | \
-		tr abcdefghijklmnopqrstuvwxyz./ ABCDEFGHIJKLMNOPQRSTUVWXYZ__`"
-	AC_CHECK_HEADER($mercury_cv_header, [
-		AC_DEFINE_UNQUOTED($mercury_cv_header_define)
-		eval "$mercury_cv_header_define=1"
-	])
-    done
-])
-#-----------------------------------------------------------------------------#
-AC_DEFUN(MERCURY_CHECK_FOR_IEEEFP_H,
-[
-	MERCURY_CHECK_FOR_HEADERS(ieeefp.h)
-])
-
-AC_DEFUN(MERCURY_CHECK_FOR_IEEE_FUNC,
-[
-AC_REQUIRE([MERCURY_CHECK_FOR_IEEEFP_H])
-AC_MSG_CHECKING(for $1 function)
-mercury_cv_ieee_func_define="MR_HAVE_`echo $1 | \
-	tr abcdefghijklmnopqrstuvwxyz./ ABCDEFGHIJKLMNOPQRSTUVWXYZ__`"
-
-AC_TRY_LINK([
-	#include <math.h>
-#ifdef MR_HAVE_IEEEFP_H
-	#include <ieeefp.h>
-#endif
-],[
-	float f;
-	$1(f);
-],[mercury_cv_have_ieee_func=yes],[mercury_cv_have_ieee_func=no])
-
-if test "$mercury_cv_have_ieee_func" = yes; then
-	AC_MSG_RESULT(yes)
-	AC_DEFINE_UNQUOTED($mercury_cv_ieee_func_define)
-else
-	AC_MSG_RESULT(no)
-fi
-])
-
-#-----------------------------------------------------------------------------#
-
-# Test for C99 fp environment functions in the math library.
-# The second argument to this macro should be the flags required by the
-# C compiler to link against the math library.  This is needed because
-# some OSs, e.g. Mac OS X, don't have a separate math library.
-
-AC_DEFUN([MERCURY_CHECK_FOR_FENV_FUNC],
-[
-AC_MSG_CHECKING(for $1 function)
-mercury_cv_math_func_define="MR_HAVE_`echo $1 | \
-	tr abcdefghijklmnopqrstuvwxyz./ ABCDEFGHIJKLMNOPQRSTUVWXYZ__`"
-
-save_libs="$LIBS"
-LIBS="$2 $LIBS"
-
-AC_TRY_LINK([
-#ifdef MR_HAVE_FENV_H
-	#include <fenv.h>
-#endif
-],[
-
-	int i = 0;
-	$1(i);
-],[mercury_cv_have_math_func=yes],[mercury_cv_have_math_func=no])
-
-LIBS="$save_libs"
-
-if test "$mercury_cv_have_math_func" = "yes"
-then
-	AC_MSG_RESULT([yes])
-	AC_DEFINE_UNQUOTED([$mercury_cv_math_func_define])
-else
-	AC_MSG_RESULT([no])
-fi
-])
-
-#-----------------------------------------------------------------------------#
-#
-# Turn off MacOS's so-called "smart" C preprocessor, if present,
-# since it causes lots of spurious warning messages,
-# and furthermore it takes way too long and uses way too much memory
-# when preprocessing the C code generated by the Mercury compiler's LLDS
-# back-end.
-#
-AC_DEFUN(MERCURY_CHECK_CC_NEEDS_TRAD_CPP,
-[
-AC_REQUIRE([AC_PROG_CC])
-AC_MSG_CHECKING(whether C compiler needs -no-cpp-precomp)
-AC_CACHE_VAL(mercury_cv_cpp_precomp, [
-	>conftest.c
-	if	test "$GCC" = yes &&
-		$CC -v -c conftest.c 2>&1 | \
-			grep "cpp-precomp.*-smart" > /dev/null
-	then
-		mercury_cv_cpp_precomp=yes
-	else
-		mercury_cv_cpp_precomp=no
-	fi
-])
-AC_MSG_RESULT($mercury_cv_cpp_precomp)
-if test $mercury_cv_cpp_precomp = yes; then
-	CC="$CC -no-cpp-precomp"
-fi
-])
-#-----------------------------------------------------------------------------#
-#
-# Check whether we need to add any extra directories to the search path for
-# header files, and set ALL_LOCAL_C_INCL_DIRS to the -I option(s) needed
-# for this, if any.
-#
-# GNU C normally searches /usr/local/include by default;
-# to keep things consistent, we do the same for other C compilers.
-#
-AC_DEFUN(MERCURY_CHECK_LOCAL_C_INCL_DIRS,
-[
-AC_REQUIRE([AC_PROG_CC])
-AC_MSG_CHECKING(whether to pass -I/usr/local/include to C compiler)
-ALL_LOCAL_C_INCL_DIRS=""
-ALL_LOCAL_C_INCL_DIR_MMC_OPTS=""
-
-if test "$GCC" = yes -o "$USING_MICROSOFT_CL_COMPILER" = yes; then
-	# Don't add -I/usr/local/include, since it causes a warning
-	# with gcc 3.1, and gcc already searches /usr/local/include.
-	# Microsoft compilers don't understand Unix pathnames.
-	AC_MSG_RESULT(no)
-else
-	# It's some other compiler.  We don't know if it searches
-	# /usr/local/include by default, so add it.
-	if test -d /usr/local/include/.; then
-		AC_MSG_RESULT(yes)
-		ALL_LOCAL_C_INCL_DIRS="-I/usr/local/include "
-		ALL_LOCAL_C_INCL_DIR_MMC_OPTS="--c-include-directory /usr/local/include "
-	else
-		AC_MSG_RESULT(no)
-	fi
-fi
-AC_SUBST(ALL_LOCAL_C_INCL_DIRS)
-AC_SUBST(ALL_LOCAL_C_INCL_DIR_MMC_OPTS)
-])
-#-----------------------------------------------------------------------------#
-#
-# Set ALL_LOCAL_C_LIB_DIRS to any extra directories we need to add to the
-# search path for libraries.
-#
-AC_DEFUN(MERCURY_CHECK_LOCAL_C_LIB_DIRS,
-[
-AC_MSG_CHECKING(whether to pass -L/usr/local/lib to the linker)
-
-# Microsoft compilers don't understand Unix pathnames.
-if test "$USING_MICROSOFT_CL_COMPILER" = no -a -d /usr/local/lib/.; then
-	AC_MSG_RESULT(yes)
-	ALL_LOCAL_C_LIB_DIRS=/usr/local/lib
-	ALL_LOCAL_C_LIB_DIR_MMC_OPTS="-L/usr/local/lib -R/usr/local/lib"
-else
-	AC_MSG_RESULT(no)
-	ALL_LOCAL_C_LIB_DIRS=
-	ALL_LOCAL_C_LIB_DIR_MMC_OPTS=
-fi
-AC_SUBST(ALL_LOCAL_C_LIB_DIRS)
-AC_SUBST(ALL_LOCAL_C_LIB_DIR_MMC_OPTS)
-])
-
-#-----------------------------------------------------------------------------#
-#
-# Check for readline and related header files and libraries
-#
-AC_DEFUN(MERCURY_CHECK_READLINE,
-[
-AC_ARG_WITH(readline,
-[  --without-readline      Don't use the GPL'd GNU readline library],
-mercury_cv_with_readline="$withval", mercury_cv_with_readline=yes)
-
-if test "$mercury_cv_with_readline" = yes; then
-
-	# check for the readline header files
-	MERCURY_CHECK_FOR_HEADERS(readline/readline.h readline/history.h)
-
-	# check for the libraries that readline depends on
-	MERCURY_MSG('looking for termcap or curses (needed by readline)...')
-	AC_CHECK_LIB(termcap, tgetent, mercury_cv_termcap_lib=-ltermcap,
-	 [AC_CHECK_LIB(curses,  tgetent, mercury_cv_termcap_lib=-lcurses,
-	  [AC_CHECK_LIB(ncurses, tgetent, mercury_cv_termcap_lib=-lncurses,
-	   mercury_cv_termcap_lib='')])])
-
-	# check for the readline library
-	AC_CHECK_LIB(readline, readline, mercury_cv_have_readline=yes,
-		mercury_cv_have_readline=no, $mercury_cv_termcap_lib)
-else
-	mercury_cv_have_readline=no
-fi
-
-# Now figure out whether we can use readline, and define variables according.
-# Note that on most systems, we don't actually need the header files in
-# order to use readline. (Ain't C grand? ;-).
-
-if test $mercury_cv_have_readline = no; then
-	TERMCAP_LIBRARY=""
-	READLINE_LIBRARIES=""
-	AC_DEFINE(MR_NO_USE_READLINE)
-else
-	TERMCAP_LIBRARY="$mercury_cv_termcap_lib"
-	READLINE_LIBRARIES="-lreadline $TERMCAP_LIBRARY"
-fi
-AC_SUBST(TERMCAP_LIBRARY)
-AC_SUBST(READLINE_LIBRARIES)
-
-])
-
-#-----------------------------------------------------------------------------#
-#
-# Microsoft.NET configuration
-#
-AC_DEFUN(MERCURY_CHECK_DOTNET,
-[
-AC_PATH_PROG(ILASM, ilasm)
-AC_PATH_PROG(GACUTIL, gacutil)
-
-AC_MSG_CHECKING(for Microsoft.NET Framework SDK)
-AC_CACHE_VAL(mercury_cv_microsoft_dotnet, [
-if test "$ILASM" != ""; then
-	changequote(<<,>>) 
-	MS_DOTNET_SDK_DIR=`expr "$ILASM" : '\(.*\)[/\\]*[bB]in[/\\]*ilasm'`
-	changequote([,]) 
-	mercury_cv_microsoft_dotnet="yes"
-else
-	MS_DOTNET_SDK_DIR=""
-	mercury_cv_microsoft_dotnet="no"
-fi
-])
-AC_MSG_RESULT($mercury_cv_microsoft_dotnet)
-ILASM=`basename "$ILASM"`
-GACUTIL=`basename "$GACUTIL"`
-
-# Check for the C# (C sharp) compiler.
-# gmcs is the Mono C# compiler targeting the 2.0 runtime (with generics).
-# cscc is the DotGNU C# compiler.
-AC_PATH_PROGS(CSC, csc gmcs cscc)
-CSC=`basename "$CSC"`
-
-# We default to the Beta 2 version of the library
-mercury_cv_microsoft_dotnet_library_version=1.0.2411.0
-if	test $mercury_cv_microsoft_dotnet = "yes" &&
-	test "$CSC" != "";
-then
-	AC_MSG_CHECKING(version of .NET libraries)
-	cat > conftest.cs << EOF
-	using System;
-	using System.Reflection;
-	public class version {
-	    public static void Main()
-	    {
-		Assembly asm = Assembly.Load("mscorlib");
-		AssemblyName name = asm.GetName();
-		Version version = name.Version;
-		Console.Write(version);
-		Console.Write("\n");
-	    }
-	}
-EOF
-	if
-		echo $CSC conftest.cs >&AC_FD_CC 2>&1 && \
-			$CSC conftest.cs  >&AC_FD_CC 2>&1 && \
-			./conftest > conftest.out 2>&1
-	then
-		mercury_cv_microsoft_dotnet_library_version=`cat conftest.out`
-		AC_MSG_RESULT($mercury_cv_microsoft_dotnet_library_version)
-		rm -f conftest*
-	else
-		rm -f conftest*
-		if test "$enable_dotnet_grades" = "yes"; then
-			AC_MSG_ERROR(unable to determine version)
-			exit 1
-		else
-			AC_MSG_WARN(unable to determine version)
-		fi
-	fi
-fi
-MS_DOTNET_LIBRARY_VERSION=$mercury_cv_microsoft_dotnet_library_version
-
-# Check for the assembly linker.
-# ilalink is the DotGNU assembly linker.
-AC_PATH_PROGS(MS_AL, al ilalink)
-MS_AL=`basename "$MS_AL"`
-
-# Check for an implementation of the Common Language Infrastructure.
-AC_PATH_PROGS(CLI_INTERPRETER, mono)
-MONO=`basename "$CLI_INTERPRETER"`
-
-AC_SUBST(ILASM)
-AC_SUBST(GACUTIL)
-AC_SUBST(CSC)
-AC_SUBST(MS_AL)
-AC_SUBST(MS_DOTNET_SDK_DIR)
-AC_SUBST(MS_DOTNET_LIBRARY_VERSION)
-AC_SUBST(MS_VISUALCPP_DIR)
-AC_SUBST(CLI_INTERPRETER)
-])
-
-#-----------------------------------------------------------------------------#
-#
-# Java configuration
-#
-AC_DEFUN(MERCURY_CHECK_JAVA,
-[
-# jikes requires the usual Java SDK to run, so if we checked for javac first,
-# then that's what we'd get. If the user has jikes installed, then that
-# probably means that they want to use it, so we check for jikes before javac.
-# On Windows, the Java SDK has a high chance of being installed in a path
-# containing spaces.  The simplest solution is to keep only the basename.
-# Everything will still work so long as the executables can be found on the
-# PATH later.
-AC_PATH_PROGS(JAVAC, jikes javac gcj)
-case "$JAVAC" in
-    *" "*)
-        JAVAC=`basename "$JAVAC"`
-        ;;
-esac
-case "$JAVAC" in
-    *gcj)
-        JAVAC="$JAVAC -C"
-        ;;
-esac
-AC_PATH_PROG(JAVA_INTERPRETER, java gij)
-case "$JAVA_INTERPRETER" in
-    *" "*)
-        JAVA_INTERPRETER=`basename "$JAVA_INTERPRETER"`
-        ;;
-esac
-AC_PATH_PROG(JAR, jar)
-case "$JAR" in
-    *" "*)
-        JAR=`basename "$JAR"`
-        ;;
-esac
-
-AC_CACHE_VAL(mercury_cv_java, [
-if test "$JAVAC" != "" -a "$JAVA_INTERPRETER" != "" -a "$JAR" != ""; then
-	AC_MSG_CHECKING(if the above Java SDK works and is sufficiently recent)
-	cat > conftest.java << EOF
-		// This program simply retrieves the constant
-		// specifying the version number of the Java SDK and
-		// checks it is at least 1.5, printing "Hello, world"
-		// if successful.
-		public class conftest {
-		    public static void main (String[[]] args) {
-			float	version;
-			String	strVer = System.getProperty(
-					"java.specification.version");
-
-			try {
-				version = Float.valueOf(strVer).floatValue();
-			}
-			catch (NumberFormatException e) {
-				System.out.println("ERROR: \"java." +
-						"specification.version\" " +
-						"constant has incorrect " +
-						"format.\nGot \"" + strVer +
-						"\", expected a number.");
-				version = 0f;
-			}
-
-			if (version >= 1.5f) {
-				System.out.println("Hello, world\n");
-			} else {
-				System.out.println("Nope, sorry.\n");
-			}
-		    }
-		}
-EOF
-	if
-		echo "$JAVAC" conftest.java >&AC_FD_CC 2>&1 &&
-		"$JAVAC" conftest.java >&AC_FD_CC 2>&1 &&
-		echo "$JAVA_INTERPRETER" conftest > conftest.out 2>&AC_FD_CC &&
-		CLASSPATH=. "$JAVA_INTERPRETER" conftest > conftest.out 2>&AC_FD_CC &&
-		test "`tr -d '\015' < conftest.out`" = "Hello, world"
-	then
-		mercury_cv_java="yes"
-	else
-		mercury_cv_java="no"
-	fi
-	AC_MSG_RESULT($mercury_cv_java)
-else
-	if test "$JAVAC" = ""; then
-		JAVAC="javac"
-	fi
-	if test "$JAVA_INTERPRETER" = ""; then
-		JAVA_INTERPRETER="java"
-	fi
-	if test "$JAR" = ""; then
-		JAR="jar"
-	fi
-	mercury_cv_java="no"
-fi
-])
-
-AC_SUBST(JAVAC)
-AC_SUBST(JAVA_INTERPRETER)
-AC_SUBST(JAR)
-])
-
-AC_DEFUN(MERCURY_CHECK_JAVAC_HEAP_SIZE,
-[
-# The default maximum heap size is too small to build the standard library and
-# other programs so we need to increase it.  The option to do that is
-# non-standard so we have to check that it is accepted.
-AC_CACHE_VAL(mercury_cv_javac_flags_for_heap_size, [
-if test "$mercury_cv_java" = "yes"; then
-	AC_MSG_CHECKING(if the Java compiler accepts the max heap size option)
-	mercury_cv_javac_flags_for_heap_size="-J-Xmx256m"
-	if "$JAVAC" "$mercury_cv_javac_flags_for_heap_size" -version \
-                2> /dev/null
-        then
-		AC_MSG_RESULT(yes)
-	else
-		AC_MSG_RESULT(no)
-		mercury_cv_javac_flags_for_heap_size=
-	fi
-else
-	mercury_cv_javac_flags_for_heap_size=
-fi
-])
-])
-
-#-----------------------------------------------------------------------------#
-#
-# Erlang configuration
-#
-
-# copy of AC_ERLANG_PATH_ERLC from autoconf-2.60
-AC_DEFUN([MERCURY_ERLANG_PATH_ERLC],
-[AC_ARG_VAR([ERLC], [Erlang/OTP compiler command [autodetected]])dnl
-if test -n "$ERLC"; then
-    AC_MSG_CHECKING([for erlc])
-    AC_MSG_RESULT([$ERLC])
-else
-    AC_PATH_TOOL(ERLC, erlc, [$1], [$2])
-fi
-AC_ARG_VAR([ERLCFLAGS], [Erlang/OTP compiler flags [none]])dnl
-])
-
-# copy of AC_ERLANG_PATH_ERL from autoconf-2.60
-AC_DEFUN([MERCURY_ERLANG_PATH_ERL],
-[AC_ARG_VAR([ERL], [Erlang/OTP interpreter command [autodetected]])dnl
-if test -n "$ERL"; then
-    AC_MSG_CHECKING([for erl])
-    AC_MSG_RESULT([$ERL])
-else
-    AC_PATH_TOOL(ERL, erl, [$1], [$2])[]dnl
-fi
-])
-
-AC_DEFUN(MERCURY_CHECK_ERLANG,
-[
-MERCURY_ERLANG_PATH_ERLC
-MERCURY_ERLANG_PATH_ERL
-
-if test "$ERLC" != "" -a "$ERL" != ""; then
-	mercury_cv_erlang="yes"
-else
-	mercury_cv_erlang="no"
-fi
-
-AC_SUBST(ERLC)
-AC_SUBST(ERL)
-])
-
-#-----------------------------------------------------------------------------#
-
-# NOTE: updates to this macro may need to be reflected in compiler/globals.m.
-
-AC_DEFUN([MERCURY_GCC_VERSION], [
-AC_REQUIRE([AC_PROG_CC])
-
-cat > conftest.c << EOF
-
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
-
-#if defined(__GNUC__)
-    printf("%d_", __GNUC__);
-    #if defined(__GNUC_MINOR__)
-        printf("%d_", __GNUC_MINOR__);
-    #else
-        printf("u_");
-    #endif /* ! __GNUC_MINOR__ */
-    #if defined(__GNUC_PATCHLEVEL__)
-    	printf("%d", __GNUC_PATCHLEVEL__);
-    #else
-        printf("u");
-    #endif /* ! __GNUC_PATCHLEVEL__ */
-#endif /* __GNUC__ */
-
-	return 0;
-}
-EOF
-
-echo "$CC -o conftest contest.c" >&AC_FD_CC 2>&1
-if
-    $CC -o conftest conftest.c
-then
-    mercury_cv_gcc_version=`./conftest`
-else
-    # This shouldn't happen as we have already checked for this.
-    AC_MSG_ERROR([unexpected: $CC cannot create executable])
-fi
-])
-
-#-----------------------------------------------------------------------------#
-
-# Work out the C compiler type using a stronger test than AC_PROG_CC to
-# distinguish between clang and gcc.
-# (We don't handle lcc here - I don't think that it's possible to.)
-
-AC_DEFUN([MERCURY_CC_TYPE], [
-AC_REQUIRE([AC_PROG_CC])
-AC_MSG_CHECKING([what the C compiler type really is])
-
-cat > conftest.c << EOF
-
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
-    #if defined(__clang__)
-       printf("clang");
-    #elif defined(__GNUC__)
-       printf("gcc");
-    #elif defined(_MSC_VER)
-       printf("msvc");
-    #else
-       printf("unknown");
-    #endif
-   
-    return 0;
-}
-EOF
-
-echo "$CC -o conftest conftest.c" >&AC_FD_CC 2>&1
-if
-    $CC -o conftest conftest.c
-then
-    mercury_cv_cc_type=`./conftest`
-else
-    # This shouldn't happen as we have already checked for this.
-    AC_MSG_ERROR([unexpected: $CC cannot create executable])
-fi
-
-AC_MSG_RESULT([$mercury_cv_cc_type])
-])
-
-#-----------------------------------------------------------------------------#
-
-AC_DEFUN([MERCURY_CLANG_VERSION], [
-AC_REQUIRE([AC_PROG_CC])
-
-cat > conftest.c << EOF
-
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
-
-    printf("%d_%d_%d", __clang_major__, __clang_minor__, __clang_patchlevel__);
-    return 0;
-}
-EOF
-
-echo "$CC -o conftest contest.c" >&AC_FD_CC 2>&1
-if
-    $CC -o conftest conftest.c
-then
-    mercury_cv_clang_version=`./conftest`
-else
-    # This shouldn't happen as we have already checked for this.
-    AC_MSG_ERROR([unexpected: $CC cannot create executable])
-fi
-])
-
-#-----------------------------------------------------------------------------#
-
-AC_DEFUN([MERCURY_MSVC_VERSION], [
-AC_REQUIRE([AC_PROG_CC])
-
-cat > conftest.c << EOF
-
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
-
-    printf("%d", _MSC_VER);
-    return 0;
-}
-EOF
-
-echo "$CC conftest.c -Fecontest" >&AC_FD_CC 2>&1
-if
-    $CC conftest.c -Feconftest > /dev/null
-then
-    mercury_cv_msvc_version=`./conftest`
-else
-    # This shouldn't happen as we have already checked for this.
-    AC_MSG_ERROR([unexpected: $CC cannot create executable])
-fi
-])
-
-#-----------------------------------------------------------------------------#
-#
-# Check if the POSIX threads library is pthreads-win32.
-#
-
-AC_DEFUN([MERCURY_HAVE_PTHREADS_WIN32], [
-
-AC_MSG_CHECKING([if we are using pthreads-win32])
-
-cat > conftest.c << EOF
-
-#include <pthread.h>
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
-
-#if defined(PTW32_VERSION)
-    return 0;
-#else
-    return 1;
-#endif
-
-}
-
-EOF
-
-echo "$CC -o conftest contest.c" >&AC_FD_CC 2>&1
-if
-    $CC -o conftest conftest.c
-then
-    mercury_cv_have_pthreads_win32="yes"
-else
-    mercury_cv_have_pthreads_win32="no"
-fi
-
-AC_MSG_RESULT($mercury_cv_have_pthreads_win32)
-
-])
-
-#-----------------------------------------------------------------------------#
Index: configure.in
===================================================================
RCS file: /home/mercury1/repository/mercury/configure.in,v
retrieving revision 1.596
diff -u -p -b -r1.596 configure.in
--- configure.in	4 Oct 2011 02:37:57 -0000	1.596
+++ configure.in	4 Oct 2011 14:31:39 -0000
@@ -1265,7 +1265,7 @@ mercury_check_for_functions \
         grantpt unlockpt ptsname tcgetattr tcsetattr ioctl \
         access sleep opendir readdir closedir mkdir symlink readlink \
         gettimeofday setenv putenv _putenv posix_spawn sched_setaffinity \
-        sched_getcpu sched_yield mkstemp
+        sched_getaffinity sched_getcpu sched_yield mkstemp
 
 #-----------------------------------------------------------------------------#
 
@@ -5107,6 +5107,30 @@ MERCURY_CHECK_READLINE
 
 #-----------------------------------------------------------------------------#
 #
+# Check for libhwloc, http://www.open-mpi.org/projects/hwloc/
+#
+PKG_PROG_PKG_CONFIG
+PKG_CHECK_MODULES(libhwloc, hwloc >= 1.0,
+    [
+        AC_DEFINE(MR_HAVE_HWLOC)
+    ],
+    [
+        case "$LIBGRADES" in
+            $BEST_LLDS_BASE_GRADE.par.gc*)
+                MERCURY_MSG(["Warning: libhwloc not found, thread pinning in"])
+                MERCURY_MSG(["low-level C parallel grades may be less accurate."])
+                ;;
+            *)
+                ;;
+        esac
+    ])
+HWLOC_LIBS="$libhwloc_LIBS"
+HWLOC_CFLAGS="$libhwloc_CFLAGS"
+AC_SUBST(HWLOC_LIBS)
+AC_SUBST(HWLOC_CFLAGS)
+
+#-----------------------------------------------------------------------------#
+#
 # Check for flex and bison
 #
 
Index: runtime/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/Mmakefile,v
retrieving revision 1.152
diff -u -p -b -r1.152 Mmakefile
--- runtime/Mmakefile	30 Sep 2010 07:23:33 -0000	1.152
+++ runtime/Mmakefile	4 Oct 2011 12:19:52 -0000
@@ -252,10 +252,14 @@ LDLIBS		= $(SHARED_GC_LIBS)
 
 THREADLIBS	= \
 		` case "$(GRADE)" in					\
-		    *.par*|*.mps*) echo "-lpthread" ;;			\
+			*.mps*) echo $(THREAD_LIBS) ;;			\
+			*.hlc.par*) echo $(THREAD_LIBS) ;;		\
+			*.par*) echo "$(THREAD_LIBS) $(HWLOC_LIBS)" ;;	\
 		  esac							\
 		`
 
+CFLAGS += $(HWLOC_CFLAGS)
+
 $(HDR_CHECK_OBJS):	mercury_conf.h
 
 #-----------------------------------------------------------------------------#
Index: runtime/mercury_conf.h.in
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_conf.h.in,v
retrieving revision 1.72
diff -u -p -b -r1.72 mercury_conf.h.in
--- runtime/mercury_conf.h.in	6 Sep 2011 05:20:44 -0000	1.72
+++ runtime/mercury_conf.h.in	4 Oct 2011 14:31:39 -0000
@@ -274,6 +274,7 @@
 **	MR_HAVE_POSIX_SPAWN	we have the posix_spawn() function.
 **	MR_HAVE_FESETROUND	we have the fesetround() function.
 **	MR_HAVE_SCHED_SETAFFINITY we have the sched_setaffinity() function.
+**	MR_HAVE_SCHED_GETAFFINITY we have the sched_gettaffinity() function.
 **	MR_HAVE_SCHED_GETCPU	we have the sched_getcpu() function (glibc specific).
 **	MR_HAVE_SCHED_YIELD	we have the sched_yield() function.
 **	MR_HAVE_PTHREAD_MUTEXATTR_SETPSHARED we have the
@@ -342,6 +343,7 @@
 #undef	MR_HAVE_POSIX_SPAWN
 #undef	MR_HAVE_FESETROUND
 #undef	MR_HAVE_SCHED_SETAFFINITY
+#undef	MR_HAVE_SCHED_GETAFFINITY
 #undef	MR_HAVE_SCHED_GETCPU
 #undef	MR_HAVE_SCHED_YIELD
 #undef	MR_HAVE_PTHREAD_MUTEXATTR_SETPSHARED
@@ -420,6 +422,11 @@
 #undef MR_PTHREADS_WIN32
 
 /*
+** MR_HAVE_HWLOC is defined if the hwloc library is available.
+*/
+#undef MR_HAVE_HWLOC
+
+/*
 ** The bytecode files represent floats in 64-bit IEEE format.
 **
 ** MR_FLOAT_IS_64_BITS: defined iff the C type `float' is exactly 64 bits.
Index: runtime/mercury_conf_param.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_conf_param.h,v
retrieving revision 1.121
diff -u -p -b -r1.121 mercury_conf_param.h
--- runtime/mercury_conf_param.h	22 Aug 2011 07:56:10 -0000	1.121
+++ runtime/mercury_conf_param.h	4 Oct 2011 14:31:39 -0000
@@ -1067,4 +1067,15 @@
 
 /*---------------------------------------------------------------------------*/
 
+/*
+** MR_HAVE_THREAD_PINNING is defined if we can pin threads, either with
+** sched_setaffinity or hwloc.
+*/
+#if (defined(MR_HAVE_SCHED_SETAFFINITY) && \
+	defined(MR_HAVE_SCHED_GETAFFINITY)) || defined(MR_HAVE_HWLOC)
+    #define MR_HAVE_THREAD_PINNING
+#endif
+
+/*---------------------------------------------------------------------------*/
+
 #endif /* MERCURY_CONF_PARAM_H */
Index: runtime/mercury_context.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_context.c,v
retrieving revision 1.99
diff -u -p -b -r1.99 mercury_context.c
--- runtime/mercury_context.c	12 Sep 2011 08:09:24 -0000	1.99
+++ runtime/mercury_context.c	4 Oct 2011 14:32:11 -0000
@@ -46,6 +46,10 @@ ENDINIT
   #include <sys/timeb.h>    /* for _ftime() */
 #endif
 
+#if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_HWLOC)
+  #include <hwloc.h>
+#endif
+
 #include "mercury_memory_handlers.h"
 #include "mercury_context.h"
 #include "mercury_engine.h"             /* for `MR_memdebug' */
@@ -161,12 +165,19 @@ static MR_Integer       MR_profile_paral
 /*
 ** Local variables for thread pinning.
 */
-#ifdef MR_LL_PARALLEL_CONJ
-static MercuryLock      MR_next_cpu_lock;
+#if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_THREAD_PINNING)
 MR_bool                 MR_thread_pinning = MR_FALSE;
-static MR_Unsigned      MR_next_cpu = 0;
-/* This is initialised the first the MR_pin_primordial_thread() is called */
+
+static MercuryLock      MR_thread_pinning_lock;
+static unsigned         MR_num_threads_left_to_pin;
+static unsigned         MR_num_processors;
 MR_Unsigned             MR_primordial_thread_cpu;
+#ifdef MR_HAVE_HWLOC
+static hwloc_topology_t MR_hw_topology;
+static hwloc_cpuset_t   MR_hw_available_pus = NULL;
+#else /* MR_HAVE_SCHED_SETAFFINITY */
+static cpu_set_t        *MR_available_cpus;
+#endif
 #endif
 
 #if defined(MR_LL_PARALLEL_CONJ) && \
@@ -217,7 +228,7 @@ MR_SparkDeque           **MR_spark_deque
 
 #ifdef MR_LL_PARALLEL_CONJ
 /*
-** Try to wake up a sleeping message and tell it to do action. The engine
+** Try to wake up a sleeping engine and tell it to do action. The engine
 ** is only woken if the engine is in one of the states in the bitfield states.
 ** If the engine is woken, this function returns MR_TRUE, otherwise it
 ** returns MR_FALSE.
@@ -233,9 +244,35 @@ try_wake_engine(MR_EngineId engine_id, i
 static void
 MR_write_out_profiling_parallel_execution(void);
 
-#if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_SCHED_SETAFFINITY)
+#if defined(MR_LL_PARALLEL_CONJ)
 static void
+MR_setup_thread_pinning(void);
+
+static MR_bool
 MR_do_pin_thread(int cpu);
+
+/*
+** Determine which CPU this thread is currently running on.
+*/
+static int
+MR_current_cpu(void);
+
+/*
+** Reset or initialize the cpuset that tracks which CPUs are available for
+** binding.
+*/
+static void
+MR_reset_available_cpus(void);
+
+/*
+** Mark the given CPU as unavailable for thread pinning.  This may mark other
+** CPUs as unavailable, if, for instance they share resources with this
+** processor and we can place other tasks elsewhere to avoid this sharing.
+** These resources are usually only considered for hardware threads that share
+** cores.
+*/
+static void
+MR_make_cpu_unavailable(int cpu);
 #endif
 
 /*---------------------------------------------------------------------------*/
@@ -253,9 +290,6 @@ MR_init_context_stuff(void)
     pthread_mutex_init(&free_context_list_lock, MR_MUTEX_ATTR);
     pthread_mutex_init(&MR_pending_contexts_lock, MR_MUTEX_ATTR);
   #ifdef MR_LL_PARALLEL_CONJ
-    #ifdef MR_HAVE_SCHED_SETAFFINITY
-    pthread_mutex_init(&MR_next_cpu_lock, MR_MUTEX_ATTR);
-    #endif
     #ifdef MR_DEBUG_RUNTIME_GRANULARITY_CONTROL
     pthread_mutex_init(&MR_par_cond_stats_lock, MR_MUTEX_ATTR);
     #endif
@@ -268,40 +302,10 @@ MR_init_context_stuff(void)
     MR_KEY_CREATE(&MR_backjump_next_choice_id_key, (void *)0);
   #endif
 
-    /*
-    ** If MR_num_threads is unset, configure it to match number of processors
-    ** on the system. If we do this, then we prepare to set processor
-    ** affinities later on.
-    */
-    if (MR_num_threads == 0) {
-      #if defined(MR_HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
-        long result;
-
-        result = sysconf(_SC_NPROCESSORS_ONLN);
-        if (result < 1) {
-            /* We couldn't determine the number of processors. */
-            MR_num_threads = 1;
-        } else {
-            MR_num_threads = result;
-            /*
-            ** On systems that don't support sched_setaffinity, we don't try
-            ** to automatically enable thread pinning. This prevents a runtime
-            ** warning that could unnecessarily confuse the user.
-            **/
-          #if defined(MR_LL_PARALLEL_CONJ) && \
-              defined(MR_HAVE_SCHED_SETAFFINITY)
-            /*
-            ** Comment this back in to enable thread pinning by default
-            ** if we autodetected the correct number of CPUs.
-            */
-            /* MR_thread_pinning = MR_TRUE; */
-          #endif
-        }
-      #else /* ! defined(MR_HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) */
-        MR_num_threads = 1;
-      #endif /* ! defined(MR_HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) */
-    }
   #ifdef MR_LL_PARALLEL_CONJ
+    #if defined(MR_HAVE_THREAD_PINNING)
+    MR_setup_thread_pinning();
+    #endif
     MR_granularity_wsdeque_length =
         MR_granularity_wsdeque_length_factor * MR_num_threads;
 
@@ -329,101 +333,352 @@ MR_init_context_stuff(void)
 ** Pin the primordial thread first to the CPU it is currently using
 ** (if support is available for thread pinning).
 */
-#if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ)
-unsigned
-MR_pin_primordial_thread(void)
+#if defined(MR_HAVE_THREAD_PINNING) && defined(MR_LL_PARALLEL_CONJ)
+static unsigned
+MR_pin_thread_no_locking(void)
 {
     unsigned    cpu;
-    int         temp;
+    unsigned    i = 0;
 
+    cpu = MR_current_cpu();
+#ifdef MR_DEBUG_THREAD_PINNING
+    fprintf(stderr, "Currently running on cpu %d\n", cpu);
+#endif
+
+    for (i = 0; i < MR_num_processors && MR_thread_pinning; i++) {
+        if (MR_do_pin_thread((cpu + i) % MR_num_processors)) {
+#ifdef MR_DEBUG_THREAD_PINNING
+            fprintf(stderr, "Pinned to cpu %d\n", (cpu + i) % MR_num_processors);
+            fprintf(stderr, "Now running on cpu %d\n", MR_current_cpu());
+#endif
+            MR_num_threads_left_to_pin--;
+            MR_make_cpu_unavailable((cpu + i) % MR_num_processors);
+            break;
+        }
+        if (!MR_thread_pinning) {
     /*
-    ** We don't need locking to pin the primordial thread as it is called
-    ** before any other threads exist.
-    */
-    /*
-    ** We go through the motions of thread pinning even when thread pinning is
-    ** not supported as the allocation of CPUs to threads may be used later.
+            ** If MR_thread_pinning becomes false then an error prevented us
+            ** from pinning the thread.
+            ** When we fail to pin a thread but MR_thread_pinning remains true
+            ** it means that that CPU has already had a thread pinned to it.
     */
-  #ifdef MR_HAVE_SCHED_GETCPU
-    temp = sched_getcpu();
-    if (temp == -1) {
-        MR_primordial_thread_cpu = 0;
-      #ifdef MR_HAVE_SCHED_SET_AFFINITY
-        if (MR_thread_pinning) {
-            perror("Warning: unable to determine the current CPU for "
-                "the primordial thread: ");
+            fprintf(stderr, "Couldn't pin Mercury engine to processor");
+            break;
         }
-      #endif
-    } else {
-        MR_primordial_thread_cpu = temp;
     }
-  #else
-    MR_primordial_thread_cpu = 0;
-  #endif
-  #ifdef MR_HAVE_SCHED_SET_AFFINITY
-    if (MR_thread_pinning) {
-        MR_do_pin_thread(MR_primordial_thread_cpu);
-    }
-  #endif
-    return MR_primordial_thread_cpu;
+    return (cpu + 1) % MR_num_processors;
 }
-#endif /* defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ) */
 
-#if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ)
 unsigned
 MR_pin_thread(void)
 {
     unsigned cpu;
 
+    MR_LOCK(&MR_thread_pinning_lock, "MR_pin_thread");
+    cpu = MR_pin_thread_no_locking();
+    MR_UNLOCK(&MR_thread_pinning_lock, "MR_pin_thread");
+
+    return cpu;
+}
+
+void
+MR_pin_primordial_thread(void)
+{
     /*
-    ** We go through the motions of thread pinning even when thread pinning
-    ** is not supported, as the allocation of CPUs to threads may be
-    ** used later.
+    ** We don't need locking to pin the primordial thread as it is called
+    ** before any other threads exist.
     */
-    MR_LOCK(&MR_next_cpu_lock, "MR_pin_thread");
-    if (MR_next_cpu == MR_primordial_thread_cpu) {
+    MR_primordial_thread_cpu = MR_pin_thread_no_locking();
+}
+
+static void MR_setup_thread_pinning(void)
+{
+    unsigned num_processors;
+
+#ifdef MR_HAVE_HWLOC
+    if (-1 == hwloc_topology_init(&MR_hw_topology)) {
+        MR_fatal_error("Error allocating libhwloc topology object");
+    }
+    if (-1 == hwloc_topology_load(MR_hw_topology)) {
+        MR_fatal_error("Error detecting hardware topology (hwloc)");
+    }
+#endif
+
+    /*
+    ** Setup num processors
+    */
+    MR_reset_available_cpus();
+#ifdef MR_HAVE_HWLOC
+    num_processors = hwloc_cpuset_weight(MR_hw_available_pus);
+#elif defined(MR_HAVE_SCHED_GETAFFINITY)
+    /*
+    ** This looks redundant but its not.  MR_num_processors is a guess that was
+    ** gathered by using sysconf.  But the number of CPUs in the CPU_SET is the
+    ** actual number of CPUs that this process is restricted to.
+    */
+  #if defined(MR_HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
+    num_processors = sysconf(_SC_NPROCESSORS_ONLN);
+  #else
+    /*
+    ** The user may have supplied MR_num_processors
+    */
+    num_processors = (MR_num_processors > 1 ? MR_num_processors : 1)
+  #endif
+    num_processors = CPU_COUNT_S(num_processors, MR_available_cpus);
+#endif
+    MR_num_processors = num_processors;
+
         /*
-        ** Skip the CPU that the primordial thread was pinned on.
+    ** If MR_num_threads is unset, configure it to match number of processors
+    ** on the system. If we do this, then we prepare to set processor
+    ** affinities later on.
         */
-        MR_next_cpu++;
+    if (MR_num_threads == 0) {
+        MR_num_threads = num_processors;
     }
-    cpu = MR_next_cpu++;
-    MR_UNLOCK(&MR_next_cpu_lock, "MR_pin_thread");
+    MR_num_threads_left_to_pin = MR_num_threads;
+
+#ifdef MR_DEBUG_THREAD_PINNING
+    fprintf(stderr, "Detected %d available processors, will use %d threads\n",
+        MR_num_processors, MR_num_threads);
+#endif
+
+    pthread_mutex_init(&MR_thread_pinning_lock, MR_MUTEX_ATTR);
+
+  /*
+  ** Comment this back in to enable thread pinning by default
+  ** if we autodetected the number of CPUs without error.
+  */
+#if 0
+    if (MR_num_threads > 1) {
+        MR_thread_pinning = MR_TRUE;
+    }
+#endif
+}
+
+/*
+** Determine which CPU this thread is currently running on.
+*/
+static int MR_current_cpu(void)
+{
+#if defined(MR_HAVE_SCHED_GETCPU)
+    int         os_cpu;
+#if defined(MR_HAVE_HWLOC)
+    hwloc_obj_t pu;
+#endif
+
+    os_cpu = sched_getcpu();
+    if (-1 == os_cpu) {
+        os_cpu = 0;
 
-#ifdef MR_HAVE_SCHED_SETAFFINITY
     if (MR_thread_pinning) {
-        MR_do_pin_thread(cpu);
+            perror("Warning: unable to determine the current CPU for "
+                "this thread: ");
+        }
     }
+
+#if defined(MR_HAVE_HWLOC)
+    pu = hwloc_get_pu_obj_by_os_index(MR_hw_topology, os_cpu);
+    return pu->logical_index;
+#else
+    return os_cpu;
 #endif
 
-    return cpu;
+#else /* ! MR_HAVE_SCHED_GETCPU */
+    /* We have no idea! */
+    return 0;
+#endif
 }
-#endif /* defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ) */
 
-#if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_SCHED_SETAFFINITY)
-static void
+static MR_bool
 MR_do_pin_thread(int cpu)
 {
-    cpu_set_t   cpus;
+    /*
+    ** Make sure that we're allowed to bind to this CPU.
+    */
+#if defined(MR_HAVE_HWLOC)
+    hwloc_obj_t pu;
 
-    if (cpu < CPU_SETSIZE) {
-        CPU_ZERO(&cpus);
-        CPU_SET(cpu, &cpus);
-        if (sched_setaffinity(0, sizeof(cpu_set_t), &cpus) == -1) {
+    if (hwloc_cpuset_iszero(MR_hw_available_pus)) {
+        /*
+        ** Each available CPU already has a thread pinned to it.  Reset the
+        ** available_pus set so that we can oversubscribe CPUs but still
+        ** attempt to balance load.
+        */
+        MR_reset_available_cpus();
+    }
+
+    pu = hwloc_get_obj_by_type(MR_hw_topology, HWLOC_OBJ_PU, cpu);
+    if (!hwloc_cpuset_intersects(MR_hw_available_pus, pu->cpuset)) {
+        return MR_FALSE;
+    }
+#elif defined(MR_HAVE_SCHED_SETAFFINITY)
+    if (CPU_COUNT_S(MR_num_processors, MR_available_cpus) == 0) {
+        /*
+        ** As above, reset the available cpus.
+        */
+        MR_reset_available_cpus();
+    }
+    if (!CPU_ISSET_S(cpu, MR_num_processors, MR_available_cpus)) {
+        return MR_FALSE;
+    }
+#endif
+
+#if defined(MR_HAVE_HWLOC)
+    errno = hwloc_set_cpubind(MR_hw_topology, pu->cpuset,
+        HWLOC_CPUBIND_THREAD);
+    if (errno != 0) {
+        perror("Warning: Couldn't set CPU affinity: ");
+        MR_thread_pinning = MR_FALSE;
+        return MR_FALSE;
+    }
+#elif defined(MR_HAVE_SCHED_SETAFFINITY)
+    cpu_set_t   *cpus;
+
+    cpus = CPU_ALLOC(MR_num_processors);
+
+    CPU_ZERO_S(MR_num_processors, cpus);
+    CPU_SET_S(cpu, MR_num_processors, cpus);
+    if (sched_setaffinity(0, CPU_ALLOC_SIZE(MR_num_processors), cpus) == -1) {
             perror("Warning: Couldn't set CPU affinity: ");
             /*
             ** If this failed once, it will probably fail again,
             ** so we disable it.
             */
             MR_thread_pinning = MR_FALSE;
+        return MR_FALSE;
         }
-    } else {
-        perror("Warning: Couldn't set CPU affinity due to a static "
-            "system limit: ");
+#endif
+
+    return MR_TRUE;
+}
+
+static void MR_reset_available_cpus(void)
+{
+#if defined(MR_HAVE_HWLOC)
+    hwloc_cpuset_t  inherited_binding;
+
+    /*
+    ** Gather the cpuset that our parent process bound this process to.
+    **
+    ** (For information about how to deliberately restrict a process and it's
+    ** sub-processors to a set of CPUs on Linux see cpuset(7).
+    */
+    inherited_binding = hwloc_cpuset_alloc();
+    hwloc_get_cpubind(MR_hw_topology, inherited_binding, HWLOC_CPUBIND_PROCESS);
+
+    /*
+    ** Set the available processors to the union of inherited_binding and the
+    ** cpuset we're allowed to use as reported by libhwloc.  In my tests with
+    ** libhwloc_1.0-1 (Debian) hwloc reported that all cpus on the system are
+    ** avaliable, it didn't exclude cpus not in the processor's cpuset(7).
+    */
+    if (MR_hw_available_pus == NULL) {
+        MR_hw_available_pus = hwloc_cpuset_alloc();
+    }
+    hwloc_cpuset_and(MR_hw_available_pus, inherited_binding,
+        hwloc_topology_get_allowed_cpuset(MR_hw_topology));
+
+    hwloc_cpuset_free(inherited_binding);
+#elif defined(MR_HAVE_SCHED_GETAFFINITY)
+    unsigned num_processors;
+
+  #if defined(MR_HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
+    num_processors = sysconf(_SC_NPROCESSORS_ONLN);
+  #else
+    /*
+    ** The user may have supplied MR_num_processors
+    */
+    num_processors = (MR_num_processors > 1 ? MR_num_processors : 1)
+  #endif
+
+    if (MR_available_cpus == NULL) {
+        MR_available_cpus = CPU_ALLOC(num_processors);
+    }
+
+    if (-1 == sched_getaffinity(0, CPU_ALLOC_SIZE(num_processors),
+        MR_available_cpus))
+    {
+        perror("Couldn't get CPU affinity");
         MR_thread_pinning = MR_FALSE;
+        CPU_FREE(MR_available_cpus);
+        MR_available_cpus = NULL;
     }
+#endif
 }
+
+#if defined(MR_HAVE_HWLOC)
+static MR_bool MR_make_pu_unavailable(const struct hwloc_obj *pu);
+#endif
+
+static void MR_make_cpu_unavailable(int cpu)
+{
+#if defined(MR_HAVE_HWLOC)
+    hwloc_obj_t pu;
+    pu = hwloc_get_obj_by_type(MR_hw_topology, HWLOC_OBJ_PU, cpu);
+    MR_make_pu_unavailable(pu);
+#elif defined(MR_HAVE_SCHED_SETAFFINITY)
+    CPU_CLR_S(cpu, MR_num_processors, MR_available_cpus);
 #endif
+}
+
+#if defined(MR_HAVE_HWLOC)
+static MR_bool MR_make_pu_unavailable(const struct hwloc_obj *pu) {
+    hwloc_obj_t core;
+    static int  siblings_to_make_unavailable;
+    int         i;
+
+#ifdef MR_DEBUG_THREAD_PINNING
+    char *      cpusetstr;
+
+    hwloc_cpuset_asprintf(&cpusetstr, MR_hw_available_pus);
+    fprintf(stderr, "Old available CPU set: %s\n", cpusetstr);
+    free(cpusetstr);
+    hwloc_cpuset_asprintf(&cpusetstr, pu->cpuset);
+    fprintf(stderr, "Making this CPU set unavailable: %s\n", cpusetstr);
+    free(cpusetstr);
+#endif
+
+    hwloc_cpuset_andnot(MR_hw_available_pus, MR_hw_available_pus, pu->cpuset);
+
+#ifdef MR_DEBUG_THREAD_PINNING
+    hwloc_cpuset_asprintf(&cpusetstr, MR_hw_available_pus);
+    fprintf(stderr, "New available CPU set: %s\n", cpusetstr);
+    free(cpusetstr);
+#endif
+
+    siblings_to_make_unavailable = hwloc_cpuset_weight(MR_hw_available_pus) -
+        MR_num_threads_left_to_pin;
+
+    if (siblings_to_make_unavailable > 0) {
+        /*
+        ** Remove sibling processing units that share a core with the one we've just removed.
+        */
+        core = pu->parent;
+        if (core->type != HWLOC_OBJ_CORE) {
+            return MR_FALSE;
+        }
+
+        for (i = 0;
+             (i < core->arity && siblings_to_make_unavailable > 0);
+             i++) {
+            if (core->children[i] == pu) {
+                continue;
+            }
+            if (hwloc_cpuset_intersects(core->children[i]->cpuset,
+                    MR_hw_available_pus)) {
+                if (!MR_make_pu_unavailable(core->children[i])) {
+                    return MR_FALSE;
+                }
+            }
+        }
+    }
+
+    return MR_TRUE;
+}
+#endif
+
+#endif /* MR_HAVE_THREAD_PINNING && MR_LL_PARALLEL_CONJ */
 
 void
 MR_finalize_context_stuff(void)
Index: runtime/mercury_context.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_context.h,v
retrieving revision 1.69
diff -u -p -b -r1.69 mercury_context.h
--- runtime/mercury_context.h	12 Sep 2011 04:51:17 -0000	1.69
+++ runtime/mercury_context.h	4 Oct 2011 12:19:52 -0000
@@ -468,13 +468,20 @@ extern  void        MR_init_context_stuf
 ** be pinned to if pinning was both enabled and supported.  That is a valid
 ** value is always returned even if the thread is not actually pinned.
 */
-#if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ)
-extern unsigned
+#if defined(MR_LL_PARALLEL_CONJ)
+#if defined(MR_HAVE_THREAD_PINNING)
+extern void
 MR_pin_primordial_thread(void);
 extern unsigned
 MR_pin_thread(void);
 
 /*
+** The CPU that the primordial thread is running on.
+*/
+extern MR_Unsigned        MR_primordial_thread_cpu;
+#endif
+
+/*
 ** Shutdown all the engines.
 */
 extern void
Index: runtime/mercury_thread.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_thread.c,v
retrieving revision 1.49
diff -u -p -b -r1.49 mercury_thread.c
--- runtime/mercury_thread.c	24 May 2011 04:16:48 -0000	1.49
+++ runtime/mercury_thread.c	4 Oct 2011 12:19:52 -0000
@@ -115,7 +115,7 @@ MR_init_thread(MR_when_to_use when_to_us
     MercuryEngine   *eng;
 
 #ifdef MR_THREAD_SAFE
-  #ifdef MR_LL_PARALLEL_CONJ
+  #if defined(MR_LL_PARALLEL_CONJ) && defined(MR_HAVE_THREAD_PINNING)
     unsigned        cpu;
   #endif
 
@@ -129,13 +129,17 @@ MR_init_thread(MR_when_to_use when_to_us
   #ifdef MR_LL_PARALLEL_CONJ
     switch (when_to_use) {
         case MR_use_later:
+#ifdef MR_HAVE_THREAD_PINNING
             cpu = MR_pin_thread();
+#endif
             break;
         case MR_use_now:
             /*
             ** Don't pin the primordial thread here, it's already been done.
             */
+#ifdef MR_HAVE_THREAD_PINNING
             cpu = MR_primordial_thread_cpu;
+#endif
             break;
         /*
         ** TODO: We may use the cpu value here to determine which CPUs which
Index: runtime/mercury_thread.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_thread.h,v
retrieving revision 1.34
diff -u -p -b -r1.34 mercury_thread.h
--- runtime/mercury_thread.h	13 Apr 2011 13:19:42 -0000	1.34
+++ runtime/mercury_thread.h	4 Oct 2011 12:19:52 -0000
@@ -211,11 +211,6 @@ MR_null_thread(void);
   */
   extern MercuryThreadKey   MR_exception_handler_key;
 
-  /*
-  ** The CPU that the primordial thread is running on.
-  */
-  extern MR_Unsigned        MR_primordial_thread_cpu;
-
 #else /* not MR_THREAD_SAFE */
 
   #define MR_LOCK(nothing, from)        do { } while (0)
Index: runtime/mercury_wrapper.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_wrapper.c,v
retrieving revision 1.225
diff -u -p -b -r1.225 mercury_wrapper.c
--- runtime/mercury_wrapper.c	27 Sep 2011 00:49:27 -0000	1.225
+++ runtime/mercury_wrapper.c	4 Oct 2011 12:19:52 -0000
@@ -667,7 +667,9 @@ mercury_runtime_init(int argc, char **ar
 #else
 
 #if defined(MR_LL_PARALLEL_CONJ)
+#if defined(MR_HAVE_THREAD_PINNING)
     MR_pin_primordial_thread();
+#endif
   #if defined(MR_THREADSCOPE)
     /*
     ** We must setup threadscope before we setup the first engine.
Index: scripts/ml.in
===================================================================
RCS file: /home/mercury1/repository/mercury/scripts/ml.in,v
retrieving revision 1.127
diff -u -p -b -r1.127 ml.in
--- scripts/ml.in	3 Oct 2011 16:09:13 -0000	1.127
+++ scripts/ml.in	4 Oct 2011 12:19:52 -0000
@@ -50,6 +50,7 @@ LDFLAGS_FOR_THREADS="@LDFLAGS_FOR_THREAD
 LDFLAGS_FOR_TRACE="@LDFLAGS_FOR_TRACE@"
 LD_LIBFLAGS_FOR_THREADS="@LD_LIBFLAGS_FOR_THREADS@"
 THREAD_LIBS="@THREAD_LIBS@"
+HWLOC_LIBS="@HWLOC_LIBS@"
 TRACE_BASE_LIBS_SYSTEM="@TRACE_BASE_LIBS_SYSTEM@"
 
 TMPDIR=${TMPDIR=/tmp}
@@ -370,6 +371,13 @@ case $use_thread_libs.$make_shared_lib i
 		THREAD_LIBS=""
 		;;
 esac
+case "$GRADE" in
+	hlc.*.par*)
+		;;
+	*.par*)
+		THREAD_LIBS="$THREAD_LIBS $HWLOC_LIBS"
+		;;
+esac
 
 # Set the correct flags if we're to use the MS Visual C runtime.
 use_msvcrt=@USE_MSVCRT@
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <http://lists.mercurylang.org/archives/reviews/attachments/20111005/d217b698/attachment.sig>


More information about the reviews mailing list