Fix and code refactoring of lock elision workaround (Linux/x64)
authorIvan Maidanski <ivmai@mail.ru>
Sat, 19 Jul 2014 08:52:54 +0000 (12:52 +0400)
committerIvan Maidanski <ivmai@mail.ru>
Wed, 10 Sep 2014 23:18:59 +0000 (03:18 +0400)
* configure.ac (HAVE_LIBC_VERSION_H, HAVE_GNU_GET_LIBC_VERSION): Remove
(revert change in previous commit).
* include/private/gcconfig.h (GLIBC_2_19_TSX_BUG): New macro defined
for Linux/x86_64 (if Glibc used) to workaround a bug in Glibc lock
elision implementation.
* pthread_support.c: Move include of gnu/libc-version.h to gcconfig.h
(used to check whether lock elision workaround needed).
* misc.c (GC_init): Reformat code.
* pthread_support.c (mark_mutex): Initialize (to
PTHREAD_MUTEX_INITIALIZER) even lock elision workaround is needed
(revert change in previous commit).
* pthread_support.c (parse_version): New static function (defined only
if GLIBC_2_19_TSX_BUG).
* pthread_support.c (GC_setup_mark_lock): Use parse_version to check
target Glibc version properly; do not reinitialize mutex unless
workaround needed; call ABORT (with the appropriate message) in case
of a failure in pthread_mutexattr_init/settype, pthread_mutex_init.

configure.ac
include/private/gcconfig.h
misc.c
pthread_support.c

index ddb2466..45ab693 100644 (file)
@@ -658,19 +658,6 @@ case "$host" in
  *) AC_MSG_RESULT(no) ;;
 esac
 
-dnl Check for specific glibc functions and definitions that we need to for
-dnl the glibc 2.19 workaround.
-HAVE_LIBC_VERSION_H=no
-HAVE_GNU_GET_LIBC_VERSION=no
-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)
-    ;;
-esac
-AC_SUBST(HAVE_LIBC_VERSION_H)
-AC_SUBST(HAVE_GNU_GET_LIBC_VERSION)
-
 dnl Include defines that have become de facto standard.
 dnl ALL_INTERIOR_POINTERS and NO_EXECUTE_PERMISSION can be overridden
 dnl in the startup code.
index 549f041..72bef85 100644 (file)
           /* FIXME: This seems to be fixed in GLibc v2.14.              */
 #         define GETCONTEXT_FPU_EXCMASK_BUG
 #       endif
+#       if defined(__GLIBC__)
+          /* Workaround lock elision implementation for some glibc.     */
+#         define GLIBC_2_19_TSX_BUG
+#         include <gnu/libc-version.h> /* for gnu_get_libc_version() */
+#       endif
 #   endif
 #   ifdef DARWIN
 #     define OS_TYPE "DARWIN"
diff --git a/misc.c b/misc.c
index e27c77c..32cbe24 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -888,8 +888,8 @@ 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();
+#   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);
index 45ab351..db86ec8 100644 (file)
   typedef unsigned int sem_t;
 #endif /* GC_DGUX386_THREADS */
 
-#ifdef HAVE_LIBC_VERSION_H
-# include <gnu/libc-version.h>
-#endif
-
 /* Undefine macros used to redirect pthread primitives. */
 # undef pthread_create
 # ifndef GC_NO_PTHREAD_SIGMASK
@@ -1983,59 +1979,59 @@ GC_INNER void GC_lock(void)
   /* defined.                                                           */
   static pthread_mutex_t mark_mutex =
         {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}};
-#elif defined(HAVE_GNU_GET_LIBC_VERSION) && defined(HAVE_LIBC_VERSION_H)
-  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(HAVE_GNU_GET_LIBC_VERSION) && defined(HAVE_LIBC_VERSION_H)
-    pthread_mutexattr_t attr;
-    char *version_str = NULL;
-    char *strtok_save;
-    char *version_part;
-    char *version_str;
-
-    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;
-            }
-        }
-    }
+#ifdef GLIBC_2_19_TSX_BUG
+  /* Parse string like <major>[.<minor>[<tail>]] and return major value. */
+  static int parse_version(int *pminor, const char *pverstr) {
+    char *endp;
+    unsigned long value = strtoul(pverstr, &endp, 10);
+    int major = (int)value;
 
-    if (0 != pthread_mutex_init(&mark_mutex, &attr)) {
-        goto error;
+    if (major < 0 || (char *)pverstr == endp || (unsigned)major != value) {
+      /* Parse error */
+      return -1;
     }
-    pthread_mutexattr_destroy(&attr);
-    if (NULL != version_str) {
-        free(version_str);
+    if (*endp != '.') {
+      /* No minor part. */
+      *pminor = -1;
+    } else {
+      value = strtoul(endp + 1, &endp, 10);
+      *pminor = (int)value;
+      if (*pminor < 0 || (unsigned)(*pminor) != value) {
+        return -1;
+      }
     }
-    return;
+    return major;
+  }
+#endif /* GLIBC_2_19_TSX_BUG */
 
-error:
-    perror("Error setting up marker mutex");
-    exit(1);
-#endif /* HAVE_GNU_GET_LIBC_VERSION && HAVE_LIBC_VERSION_H */
+GC_INNER void GC_setup_mark_lock(void)
+{
+# ifdef GLIBC_2_19_TSX_BUG
+    pthread_mutexattr_t mattr;
+    int glibc_minor = -1;
+    int glibc_major = parse_version(&glibc_minor, gnu_get_libc_version());
+
+    if (glibc_major > 2 || (glibc_major == 2 && glibc_minor >= 19)) {
+      /* TODO: disable this workaround for glibc with fixed TSX */
+      /* This disables lock elision to workaround a bug in glibc 2.19+  */
+      if (0 != pthread_mutexattr_init(&mattr)) {
+        ABORT("pthread_mutexattr_init failed");
+      }
+      if (0 != pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK)) {
+        ABORT("pthread_mutexattr_settype failed");
+      }
+      if (0 != pthread_mutex_init(&mark_mutex, &mattr)) {
+        ABORT("pthread_mutex_init failed");
+      }
+      pthread_mutexattr_destroy(&mattr);
+    }
+# endif
 }
 
 GC_INNER void GC_acquire_mark_lock(void)