]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
- Performance improvement: eliminated busy waiting from thread creation.
authorBart Van Assche <bvanassche@acm.org>
Sat, 14 Feb 2009 12:12:57 +0000 (12:12 +0000)
committerBart Van Assche <bvanassche@acm.org>
Sat, 14 Feb 2009 12:12:57 +0000 (12:12 +0000)
- Applied DRD_() prefix to all names of functions that are not
  intercepts of client code.
- Removed superfluous include directive, namely #include <inttypes.h>.
- Removed hack for suppressing false positive reports on stdio / stderr
  because recently a suppression pattern was added for these races.
- Removed unused code and declarations.
- Added more comments.
- Updated copyright statement.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@9149

drd/drd_pthread_intercepts.c

index 8a661b7f6193ffd64c6bd9e5962fb05cce3ca491..3e78ef0a66487f3ab6464732475a254f1014c951 100644 (file)
@@ -1,13 +1,12 @@
 
 /*--------------------------------------------------------------------*/
-/*--- Client-space code for drd.          drd_pthread_intercepts.c ---*/
+/*--- Client-space code for DRD.          drd_pthread_intercepts.c ---*/
 /*--------------------------------------------------------------------*/
 
 /*
-  This file is part of drd, a data race detector.
+  This file is part of DRD, a data race detector.
 
-  Copyright (C) 2006-2008 Bart Van Assche
-  bart.vanassche@gmail.com
+  Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>.
 
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
    originates from Valgrind.
    ------------------------------------------------------------------ */
 
-// Make sure pthread_spinlock_t is available when compiling with older glibc
-// versions (2.3 or before).
+/*
+  Define _GNU_SOURCE to make sure that pthread_spinlock_t is available when
+  compiling with older glibc versions (2.3 or before).
+*/
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
 
-#include <assert.h>
-#include <inttypes.h>       // uintptr_t
-#include <pthread.h>
-#include <semaphore.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>         // confstr()
-#include "config.h"
-#include "drd_basics.h"
+#include <assert.h>         /* assert() */
+#include <pthread.h>        /* pthread_mutex_t */
+#include <semaphore.h>      /* sem_t */
+#include <stdio.h>          /* fprintf() */
+#include <stdlib.h>         /* malloc(), free() */
+#include <unistd.h>         /* confstr() */
+#include "config.h"         /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP etc. */
+#include "drd_basics.h"     /* DRD_() */
 #include "drd_clientreq.h"
-#include "pub_tool_redir.h"
+#include "pub_tool_redir.h" /* VG_WRAP_FUNCTION_ZZ() */
 
 
-// Defines.
+/* Defines. */
 
 #define PTH_FUNC(ret_ty, f, args...)                            \
   ret_ty VG_WRAP_FUNCTION_ZZ(libpthreadZdsoZd0,f)(args);        \
@@ -73,74 +73,78 @@ typedef struct
   void* (*start)(void*);
   void* arg;
   int   detachstate;
-#if 0
-  pthread_mutex_t mutex;
-  pthread_cond_t  cond;
-#else
-  int wrapper_started;
-#endif
 } VgPosixThreadArgs;
 
 
-/* Function declarations. */
+/* Local function declarations. */
 
-static void init(void) __attribute__((constructor));
-static void check_threading_library(void);
-static void vg_set_main_thread_state(void);
+static void DRD_(init)(void) __attribute__((constructor));
+static void DRD_(check_threading_library)(void);
+static void DRD_(set_main_thread_state)(void);
 
 
 /* Function definitions. */
 
-/** Shared library initialization function: the _init() function is called
- *  after dlopen() has loaded the shared library. This function must not
- *  be declared static.
+/**
+ * Shared library initialization function. The function init() is called after
+ * dlopen() has loaded the shared library with DRD client intercepts because
+ * the constructor attribute was specified in the declaration of this function.
+ * Note: do specify the -nostdlib option to gcc when linking this code into a
+ * shared library because doing so would cancel the effect of the constructor
+ * attribute ! Using the gcc option -nodefaultlibs is fine because this last
+ * option preserves the shared library initialization code that calls
+ * constructor and destructor functions.
  */
-static void init(void)
-{
-  check_threading_library();
-  vg_set_main_thread_state();
-  /* glibc up to and including version 2.8 triggers conflicting accesses   */
-  /* on stdout and stderr when sending output to one of these streams from */
-  /* more than one thread. Suppress data race reports on these objects.    */
-  DRD_IGNORE_VAR(*stdout);
-  DRD_IGNORE_VAR(*stderr);
-#if defined(HAVE_LIBC_FILE_LOCK)
-  DRD_IGNORE_VAR(*(pthread_mutex_t*)(stdout->_lock));
-  DRD_IGNORE_VAR(*(pthread_mutex_t*)(stderr->_lock));
-#endif
+static void DRD_(init)(void)
+{
+  DRD_(check_threading_library)();
+  DRD_(set_main_thread_state)();
 }
 
-static MutexT pthread_to_drd_mutex_type(const int kind)
+/**
+ * POSIX threads and DRD each have their own mutex type identification.
+ * Convert POSIX threads' mutex type to DRD's mutex type. In the code below
+ * if-statements are used to test the value of 'kind' instead of a switch
+ * statement because some of the PTHREAD_MUTEX_ macro's may have the same
+ * value.
+ */
+static MutexT DRD_(pthread_to_drd_mutex_type)(const int kind)
 {
-  switch (kind)
-  {
-    /* PTHREAD_MUTEX_RECURSIVE_NP */
-  case PTHREAD_MUTEX_RECURSIVE:
+  if (kind == PTHREAD_MUTEX_RECURSIVE)
     return mutex_type_recursive_mutex;
-    /* PTHREAD_MUTEX_ERRORCHECK_NP */
-  case PTHREAD_MUTEX_ERRORCHECK:
+  else if (kind == PTHREAD_MUTEX_ERRORCHECK)
     return mutex_type_errorcheck_mutex;
-    /* PTHREAD_MUTEX_TIMED_NP */
-    /* PTHREAD_MUTEX_NORMAL */
-  case PTHREAD_MUTEX_DEFAULT:
+  else if (kind == PTHREAD_MUTEX_NORMAL)
+    return mutex_type_default_mutex;
+  else if (kind == PTHREAD_MUTEX_DEFAULT)
+    return mutex_type_default_mutex;
 #if defined(HAVE_PTHREAD_MUTEX_ADAPTIVE_NP)
-  case PTHREAD_MUTEX_ADAPTIVE_NP:
-#endif
+  else if (kind == PTHREAD_MUTEX_ADAPTIVE_NP)
     return mutex_type_default_mutex;
+#endif
+  else
+  {
+    return mutex_type_invalid_mutex;
   }
-  return mutex_type_invalid_mutex;
 }
 
-/** @note The function mutex_type() has been declared inline in order
- *  to avoid that it shows up in call stacks.
+/**
+ * Read the mutex type stored in the client memory used for the mutex
+ * implementation.
+ *
+ * @note This function depends on the implementation of the POSIX threads
+ *   library -- the POSIX standard does not define the name of the member in
+ *   which the mutex type is stored.
+ * @note The function mutex_type() has been declared inline in order
+ *   to avoid that it shows up in call stacks (drd/tests/...exp* files).
  */
-static __inline__ MutexT mutex_type(pthread_mutex_t* mutex)
+static __inline__ MutexT DRD_(mutex_type)(pthread_mutex_t* mutex)
 {
 #if defined(HAVE_PTHREAD_MUTEX_T__M_KIND)
-  /* LinuxThreads. */
+  /* glibc + LinuxThreads. */
   const int kind = mutex->__m_kind;
 #elif defined(HAVE_PTHREAD_MUTEX_T__DATA__KIND)
-  /* NPTL. */
+  /* glibc + NPTL. */
   const int kind = mutex->__data.__kind;
 #else
   /* Another POSIX threads implementation. Regression tests will fail. */
@@ -149,17 +153,13 @@ static __inline__ MutexT mutex_type(pthread_mutex_t* mutex)
           "Did not recognize your POSIX threads implementation. Giving up.\n");
   assert(0);
 #endif
-  return pthread_to_drd_mutex_type(kind);
-}
-
-static void vg_start_suppression(const void* const p, size_t const size)
-{
-  int res;
-  VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_START_SUPPRESSION,
-                             p, size, 0, 0, 0);
+  return DRD_(pthread_to_drd_mutex_type)(kind);
 }
 
-static void vg_set_joinable(const pthread_t tid, const int joinable)
+/**
+ * Tell DRD whether 'tid' is a joinable thread or a detached thread.
+ */
+static void DRD_(set_joinable)(const pthread_t tid, const int joinable)
 {
   int res;
   assert(joinable == 0 || joinable == 1);
@@ -167,37 +167,42 @@ static void vg_set_joinable(const pthread_t tid, const int joinable)
                              tid, joinable, 0, 0, 0);
 }
 
+/**
+ * The function called from the thread created by pthread_create().
+ */
 static void* DRD_(thread_wrapper)(void* arg)
 {
   int res;
+  VgPosixThreadArgs* arg_ptr;
+  VgPosixThreadArgs arg_copy;
 
   VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_SUPPRESS_CURRENT_STACK,
                              0, 0, 0, 0, 0);
 
-  {
-    VgPosixThreadArgs* const arg_ptr = (VgPosixThreadArgs*)arg;
-    VgPosixThreadArgs const arg_copy = *arg_ptr;
-    void* result;
-
-#if 0
-    pthread_mutex_lock(arg_ptr->mutex);
-    pthread_cond_signal(arg_ptr->cond);
-    pthread_mutex_unlock(arg_ptr->mutex);
-#else
-    arg_ptr->wrapper_started = 1;
-#endif
+  arg_ptr = (VgPosixThreadArgs*)arg;
+  arg_copy = *arg_ptr;
 
-    VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SET_PTHREADID,
-                               pthread_self(), 0, 0, 0, 0);
-    vg_set_joinable(pthread_self(),
-                    arg_copy.detachstate == PTHREAD_CREATE_JOINABLE);
-    result = (arg_copy.start)(arg_copy.arg);
-    return result;
-  }
+  /*
+   * Free the memory 'arg_ptr' points at now such that it does not get
+   * leaked when the function called below throws a C++ exception.
+   */
+  free(arg_ptr);
+
+  VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SET_PTHREADID,
+                             pthread_self(), 0, 0, 0, 0);
+
+  DRD_(set_joinable)(pthread_self(),
+                     arg_copy.detachstate == PTHREAD_CREATE_JOINABLE);
+
+  return (arg_copy.start)(arg_copy.arg);
 }
 
-/** Return 1 if LinuxThread has been detected, and 0 otherwise. */
-static int detected_linuxthreads(void)
+/**
+ * Return 1 if the LinuxThread implementation has been detected, and 0
+ * otherwise. For more information about the confstr() function, see also
+ * http://www.opengroup.org/onlinepubs/009695399/functions/confstr.html
+ */
+static int DRD_(detected_linuxthreads)(void)
 {
 #if defined(linux)
 #if defined(_CS_GNU_LIBPTHREAD_VERSION)
@@ -217,35 +222,40 @@ static int detected_linuxthreads(void)
 #endif
 }
 
-/** Stop and print an error message in case a non-supported threading
- *  library (LinuxThreads) has been detected.
+/**
+ * Stop and print an error message in case a non-supported threading
+ * library implementation (LinuxThreads) has been detected.
  */
-static void check_threading_library(void)
+static void DRD_(check_threading_library)(void)
 {
-  if (detected_linuxthreads())
+  if (DRD_(detected_linuxthreads)())
   {
     if (getenv("LD_ASSUME_KERNEL"))
     {
       fprintf(stderr,
-              "Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
-              "the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
-              "after having unset the environment variable LD_ASSUME_KERNEL. Giving up.\n"
+"Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
+"the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
+"after having unset the environment variable LD_ASSUME_KERNEL. Giving up.\n"
               );
     }
     else
     {
       fprintf(stderr,
-              "Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
-              "the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
-              "after having upgraded to a newer version of your Linux distribution.\n"
-              "Giving up.\n"
+"Detected the LinuxThreads threading library. Sorry, but DRD only supports\n"
+"the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n"
+"after having upgraded to a newer version of your Linux distribution.\n"
+"Giving up.\n"
               );
     }
     abort();
   }
 }
 
-static void vg_set_main_thread_state(void)
+/**
+ * The main thread is the only thread not created by pthread_create().
+ * Update DRD's state information about the main thread.
+ */
+static void DRD_(set_main_thread_state)(void)
 {
   int res;
 
@@ -266,55 +276,43 @@ PTH_FUNC(int, pthreadZucreateZa, // pthread_create*
   int    res;
   int    ret;
   OrigFn fn;
-  VgPosixThreadArgs vgargs;
+  VgPosixThreadArgs* vgargs;
 
   VALGRIND_GET_ORIG_FN(fn);
 
-  vg_start_suppression(&vgargs.wrapper_started,
-                       sizeof(vgargs.wrapper_started));
-  vgargs.start = start;
-  vgargs.arg   = arg;
-  vgargs.wrapper_started = 0;
-  vgargs.detachstate = PTHREAD_CREATE_JOINABLE;
+  vgargs = malloc(sizeof *vgargs);
+  assert(vgargs);
+  vgargs->start = start;
+  vgargs->arg   = arg;
+  /*
+   * Find out whether the thread will be started as a joinable thread
+   * or as a detached thread. If no thread attributes have been specified,
+   * the new thread will be started as a joinable thread.
+   */
+  vgargs->detachstate = PTHREAD_CREATE_JOINABLE;
   if (attr)
   {
-    if (pthread_attr_getdetachstate(attr, &vgargs.detachstate) != 0)
+    if (pthread_attr_getdetachstate(attr, &vgargs->detachstate) != 0)
     {
       assert(0);
     }
   }
-  assert(vgargs.detachstate == PTHREAD_CREATE_JOINABLE
-         || vgargs.detachstate == PTHREAD_CREATE_DETACHED);
-#if 0
-  pthread_mutex_init(&vgargs.mutex, 0);
-  pthread_cond_init(&vgargs.cond, 0);
-  pthread_mutex_lock(&vgargs.mutex);
-#endif
+  assert(vgargs->detachstate == PTHREAD_CREATE_JOINABLE
+         || vgargs->detachstate == PTHREAD_CREATE_DETACHED);
+
   /* Suppress NPTL-specific conflicts between creator and created thread. */
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__DRD_STOP_RECORDING,
                              0, 0, 0, 0, 0);
-  CALL_FN_W_WWWW(ret, fn, thread, attr, DRD_(thread_wrapper), &vgargs);
+
+  CALL_FN_W_WWWW(ret, fn, thread, attr, DRD_(thread_wrapper), vgargs);
+
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__DRD_START_RECORDING,
                              0, 0, 0, 0, 0);
-#if 0
-  pthread_cond_wait(&vgargs.cond, &vgargs.mutex);
-  pthread_mutex_unlock(&vgargs.mutex);
-  pthread_cond_destroy(&vgargs.cond);
-  pthread_mutex_destroy(&vgargs.mutex);
-#else
-  // Yes, you see it correctly, busy waiting ... The problem is that
-  // POSIX threads functions cannot be called here -- the functions defined
-  // in this file (drd_intercepts.c) would be called instead of those in
-  // libpthread.so. This loop is necessary because vgargs is allocated on the
-  // stack, and the created thread reads it.
-  if (ret == 0)
-  {
-    while (! vgargs.wrapper_started)
-    {
-      sched_yield();
-    }
-  }
-#endif
+
+  /* Free the memory 'vgargs' points at if pthread_create() failed. */
+  if (ret != 0)
+    free(vgargs);
+
   return ret;
 }
 
@@ -346,7 +344,7 @@ PTH_FUNC(int, pthreadZudetach, pthread_t pt_thread)
     CALL_FN_W_W(ret, fn, pt_thread);
     if (ret == 0)
     {
-      vg_set_joinable(pt_thread, 0);
+      DRD_(set_joinable)(pt_thread, 0);
     }
   }
   return ret;
@@ -381,7 +379,8 @@ PTH_FUNC(int, pthreadZumutexZuinit,
   if (attr)
     pthread_mutexattr_gettype(attr, &mt);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_MUTEX_INIT,
-                             mutex, pthread_to_drd_mutex_type(mt), 0, 0, 0);
+                             mutex, DRD_(pthread_to_drd_mutex_type)(mt),
+                             0, 0, 0);
   CALL_FN_W_WW(ret, fn, mutex, attr);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_INIT,
                              mutex, 0, 0, 0, 0);
@@ -400,7 +399,7 @@ PTH_FUNC(int, pthreadZumutexZudestroy,
                              mutex, 0, 0, 0, 0);
   CALL_FN_W_W(ret, fn, mutex);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_DESTROY,
-                             mutex, mutex_type(mutex), 0, 0, 0);
+                             mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
   return ret;
 }
 
@@ -413,7 +412,7 @@ PTH_FUNC(int, pthreadZumutexZulock, // pthread_mutex_lock
   OrigFn fn;
   VALGRIND_GET_ORIG_FN(fn);
   VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__PRE_MUTEX_LOCK,
-                             mutex, mutex_type(mutex), 0, 0, 0);
+                             mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
   CALL_FN_W_W(ret, fn, mutex);
   VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__POST_MUTEX_LOCK,
                              mutex, ret == 0, 0, 0, 0);
@@ -429,7 +428,7 @@ PTH_FUNC(int, pthreadZumutexZutrylock, // pthread_mutex_trylock
   OrigFn fn;
   VALGRIND_GET_ORIG_FN(fn);
   VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__PRE_MUTEX_LOCK,
-                             mutex, mutex_type(mutex), 1, 0, 0);
+                             mutex, DRD_(mutex_type)(mutex), 1, 0, 0);
   CALL_FN_W_W(ret, fn, mutex);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_LOCK,
                              mutex, ret == 0, 0, 0, 0);
@@ -446,7 +445,7 @@ PTH_FUNC(int, pthreadZumutexZutimedlock, // pthread_mutex_timedlock
   OrigFn fn;
   VALGRIND_GET_ORIG_FN(fn);
   VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__PRE_MUTEX_LOCK,
-                             mutex, mutex_type(mutex), 0, 0, 0);
+                             mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
   CALL_FN_W_WW(ret, fn, mutex, abs_timeout);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_LOCK,
                              mutex, ret == 0, 0, 0, 0);
@@ -463,7 +462,7 @@ PTH_FUNC(int, pthreadZumutexZuunlock, // pthread_mutex_unlock
   VALGRIND_GET_ORIG_FN(fn);
   VALGRIND_DO_CLIENT_REQUEST(res, -1,
                              VG_USERREQ__PRE_MUTEX_UNLOCK,
-                             mutex, mutex_type(mutex), 0, 0, 0);
+                             mutex, DRD_(mutex_type)(mutex), 0, 0, 0);
   CALL_FN_W_W(ret, fn, mutex);
   VALGRIND_DO_CLIENT_REQUEST(res, -1,
                              VG_USERREQ__POST_MUTEX_UNLOCK,
@@ -514,7 +513,7 @@ PTH_FUNC(int, pthreadZucondZuwaitZa, // pthread_cond_wait*
   OrigFn fn;
   VALGRIND_GET_ORIG_FN(fn);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_COND_WAIT,
-                             cond, mutex, mutex_type(mutex), 0, 0);
+                             cond, mutex, DRD_(mutex_type)(mutex), 0, 0);
   CALL_FN_W_WW(ret, fn, cond, mutex);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_COND_WAIT,
                              cond, mutex, 1, 0, 0);
@@ -532,7 +531,7 @@ PTH_FUNC(int, pthreadZucondZutimedwaitZa, // pthread_cond_timedwait*
   OrigFn fn;
   VALGRIND_GET_ORIG_FN(fn);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_COND_WAIT,
-                             cond, mutex, mutex_type(mutex), 0, 0);
+                             cond, mutex, DRD_(mutex_type)(mutex), 0, 0);
   CALL_FN_W_WWW(ret, fn, cond, mutex, abstime);
   VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_COND_WAIT,
                              cond, mutex, 1, 0, 0);