|
From: <sv...@va...> - 2009-02-14 13:31:02
|
Author: bart
Date: 2009-02-14 12:12:57 +0000 (Sat, 14 Feb 2009)
New Revision: 9149
Log:
- Performance improvement: eliminated busy waiting from thread creation.
- 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.
Modified:
trunk/drd/drd_pthread_intercepts.c
Modified: trunk/drd/drd_pthread_intercepts.c
===================================================================
--- trunk/drd/drd_pthread_intercepts.c 2009-02-14 11:54:42 UTC (rev 9148)
+++ trunk/drd/drd_pthread_intercepts.c 2009-02-14 12:12:57 UTC (rev 9149)
@@ -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
- bar...@gm...
+ Copyright (C) 2006-2009 Bart Van Assche <bar...@gm...>.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -40,26 +39,27 @@
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 @@
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)
+static void DRD_(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
+ 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,55 +153,56 @@
"Did not recognize your POSIX threads implementation. Giving up.\n");
assert(0);
#endif
- return pthread_to_drd_mutex_type(kind);
+ return DRD_(pthread_to_drd_mutex_type)(kind);
}
-static void vg_start_suppression(const void* const p, size_t const size)
+/**
+ * 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;
- VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_START_SUPPRESSION,
- p, size, 0, 0, 0);
-}
-
-static void vg_set_joinable(const pthread_t tid, const int joinable)
-{
- int res;
assert(joinable == 0 || joinable == 1);
VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__SET_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;
+ arg_ptr = (VgPosixThreadArgs*)arg;
+ arg_copy = *arg_ptr;
-#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
+ /*
+ * 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);
- vg_set_joinable(pthread_self(),
- arg_copy.detachstate == PTHREAD_CREATE_JOINABLE);
- result = (arg_copy.start)(arg_copy.arg);
- return result;
- }
+ 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 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
|