[m-rev.] For review: Workaround Linux NTPL TSX bug (Mercury Bug: 334)

Paul Bone paul at bone.id.au
Thu Jun 26 18:25:28 AEST 2014


Branches: master, version-14_01-branch

Workaround Linux NTPL TSX bug (Mercury Bug: 334)

New versions of glibc on x86_64 attempt to use the TSX extension of newer
Intel processors.  This converts mutex-protected critical sections into
transactional memory critical sections.  However the implementation appears
to be buggy and the marker lock in Boehm GC causes an assertion to be
triggered.

I've been in contact with the author of the elision code in glibc and have
submitted this workaround to the Boehm GC list.  Until the bug is fixed or
worked-around upstream I'd like to fix it here so I can continue other work.

boehm_gc/include/private/pthread_support.h:
boehm_gc/pthread_support.c:
    Define GC_setup_mark_lock()  This procedure creates the lock specifying a
    pthread_mutexattr_t structure.  This is used to disable lock elision on
    Linux with glibc 2.19.

boehm_gc/misc.c:
    Call GC_setup_mark_lock() when initialising the collector.

configure.ac:
    If we're using Linux then check for the GNU extensions required to
    identify the version of glibc at runtime.  If available, pass
    -DGLIBC_ELISION_WORKAROUND in the CFLAGS for Boehm GC.
---
 boehm_gc/include/private/pthread_support.h |  2 ++
 boehm_gc/misc.c                            |  3 ++
 boehm_gc/pthread_support.c                 | 52 ++++++++++++++++++++++++++++++
 configure.ac                               | 24 ++++++++++++--
 4 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/boehm_gc/include/private/pthread_support.h b/boehm_gc/include/private/pthread_support.h
index 8820fc4..fd84216 100644
--- a/boehm_gc/include/private/pthread_support.h
+++ b/boehm_gc/include/private/pthread_support.h
@@ -141,6 +141,8 @@ GC_INNER GC_thread GC_start_rtn_prepare_thread(void *(**pstart)(void *),
                                         struct GC_stack_base *sb, void *arg);
 GC_INNER void GC_thread_exit_proc(void *);
 
+GC_INNER void GC_setup_mark_lock(void);
+
 #endif /* GC_PTHREADS && !GC_WIN32_THREADS */
 
 #endif /* GC_PTHREAD_SUPPORT_H */
diff --git a/boehm_gc/misc.c b/boehm_gc/misc.c
index 05438c2..1b65243 100644
--- a/boehm_gc/misc.c
+++ b/boehm_gc/misc.c
@@ -744,6 +744,9 @@ GC_API void GC_CALL GC_init(void)
         /* else */ InitializeCriticalSection (&GC_allocate_ml);
      }
 #   endif /* GC_WIN32_THREADS */
+#   if (defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS))
+     GC_setup_mark_lock();
+#   endif /* GC_PTHREADS */
 #   if (defined(MSWIN32) || defined(MSWINCE)) && defined(THREADS)
       InitializeCriticalSection(&GC_write_cs);
 #   endif
diff --git a/boehm_gc/pthread_support.c b/boehm_gc/pthread_support.c
index 216d4d2..988b387 100644
--- a/boehm_gc/pthread_support.c
+++ b/boehm_gc/pthread_support.c
@@ -93,6 +93,10 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
   typedef unsigned int sem_t;
 #endif /* GC_DGUX386_THREADS */
 
+#ifdef GLIBC_ELISION_WORKAROUND
+# include <gnu/libc-version.h>
+#endif
+
 /* Undefine macros used to redirect pthread primitives. */
 # undef pthread_create
 # ifndef GC_NO_PTHREAD_SIGMASK
@@ -1848,12 +1852,60 @@ GC_INNER void GC_lock(void)
   /* defined.                                                           */
   static pthread_mutex_t mark_mutex =
         {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}};
+#elif defined(GLIBC_ELISION_WORKAROUND)
+  static pthread_mutex_t mark_mutex;
 #else
   static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
 static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
 
+GC_INNER void GC_setup_mark_lock(void)
+{
+#if defined(GLIBC_ELISION_WORKAROUND)
+    pthread_mutexattr_t attr;
+    char *version_str = NULL;
+    char *strtok_save;
+    char *version_part;
+
+    if (0 != pthread_mutexattr_init(&attr)) {
+        goto error;
+    }
+
+    /*
+    ** Check for version 2.19 or greater.
+    */
+    version_str = strdup(gnu_get_libc_version());
+    version_part = strtok_r(version_str, ".", &strtok_save);
+    if ((NULL != version_part) && (2 <= atoi(version_part))) {
+        version_part = strtok_r(NULL, ".", &strtok_save);
+        if ((NULL != version_part) && (19 <= atoi(version_part))) {
+            /*
+             * Disable lock elision on this version of glibc.
+             */
+            if (0 != pthread_mutexattr_settype(&attr,
+                        PTHREAD_MUTEX_ERRORCHECK))
+            {
+                goto error;
+            }
+        }
+    }
+
+    if (0 != pthread_mutex_init(&mark_mutex, &attr)) {
+        goto error;
+    }
+    pthread_mutexattr_destroy(&attr);
+    if (NULL != version_str) {
+        free(version_str);
+    }
+    return;
+
+error:
+    perror("Error setting up marker mutex");
+    exit(1);
+#endif /* GLIBC_ELISION_WORKAROUND */
+}
+
 GC_INNER void GC_acquire_mark_lock(void)
 {
     GC_generic_lock(&mark_mutex);
diff --git a/configure.ac b/configure.ac
index dede274..a102b6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3069,7 +3069,26 @@ ENABLE_BOEHM_PARALLEL_MARK=
 # This following variable is used for passing any other C compiler
 # flags to the version of the Boehm GC built in parallel grades.
 #
-BOEHM_MISC_CFLAGS_FOR_THREADS=
+BOEHM_MISC_CFLAGS_FOR_THREADS=""
+
+# Check for specific glibc functions and definitions that we need to for
+# to work around a bug in glibc 2.19 that affects Boehm GC.
+
+case "${host}" in
+    *linux*)
+        AC_CHECK_HEADER([gnu/libc-version.h], HAVE_LIBC_VERSION_H=yes)
+        AC_CHECK_FUNC([gnu_get_libc_version], HAVE_GNU_GET_LIBC_VERSION=yes)
+        ;;
+    *)
+        HAVE_LIBC_VERSION_H=no
+        HAVE_GNU_GET_LIBC_VERSION=no
+        ;;
+esac
+if test "$HAVE_LIBC_VERSION_H" = "yes" -a \
+     "$HAVE_GNU_GET_LIBC_VERSION" = "yes" ; then
+    BOEHM_MISC_CFLAGS_FOR_THREADS="$BOEHM_MISC_CFLAGS_FOR_THREADS \
+        -DGLIBC_ELISION_WORKAROUND"
+fi
 
 case "$host" in
     *solaris*)
@@ -3093,7 +3112,8 @@ case "$host" in
         # (With the current collector these settings only appear to work
         # correctly on Linux.)
         # XXX disabled as it aborts on GC_free when --enable-gc-munmap is used
-        # BOEHM_MISC_CFLAGS_FOR_THREADS="-DHBLKSIZE=32768 -DCPP_LOG_HBLKSIZE=15"
+        # BOEHM_MISC_CFLAGS_FOR_THREADS="$BOEHM_MISC_CFLAGS_FOR_THREADS \
+        #   -DHBLKSIZE=32768 -DCPP_LOG_HBLKSIZE=15"
         ;;
 
     *cygwin*)
-- 
2.0.0




More information about the reviews mailing list