This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[mudflap] remove a couple of race conditions
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Sat, 16 Jul 2005 19:43:07 -0700
- Subject: [mudflap] remove a couple of race conditions
I started looking into the hordes of mudflap timeouts on alpha. Most of
them were actually due to the machine in question still having a LinuxThreads
library installed for use with -static. And in that case it really does
take 7 minutes to spawn and reap 10,000 threads. I then upgraded the system
to use nptl only, and the same test runs in 15 seconds. :-/
But in the process of looking, I found at least 3 race conditions in the
mudflap pthread_create wrapper. I spoke with fche, and he didn't have any
recollection of what purpose the local stack allocation stuff was supposed
to serve, and okayed its deletion. I also make use of TLS when available,
which allows essentially *all* of the hair to go away. When TLS isn't
available, we continue to use local mechanisms to store thread-local state.
I did make a valiant attempt to use pthread_setspecific, but the interaction
between pthread_setspecific, __wrap_free, and thread shutdown was too weird.
I'd always wind up deallocating the stuff that indicated thread state, then
try to deallocate something else, and bomb.
I think the timeouts on some of the tests are a bit tight yet -- we set a
timeout of 3 seconds on pass39-frag.c -- and it still randomly fails on my
alpha. But we seem to be golden otherwise.
Tested on i686-linux (rhel3, linuxthread static, nptl dynamic, no tls),
x86_64-linux (fc4, nptl and tls), and alpha-linux (ac1, nptl and tls).
r~
* acinclude.m4: New file.
* configure.ac: Invoke LIBMUDFLAP_CHECK_TLS.
* configure, config.h.in, Makefile.in, testsuite/Makefile.in: Rebuild.
* mf-hooks1.c (__mf_0fn_malloc): Move body from ...
(__mf_0fn_calloc): ... here.
* mf-hooks3.c (struct pthread_info): Remove.
(__mf_pthread_info, __mf_pthread_info_idx): Remove.
(LIBMUDFLAPTH_THREADS_MAX): Set to 1021.
(struct mf_thread_data): New.
(mf_thread_data, mf_thread_data_lock): New.
(__mf_allocate_blank_threadinfo): Remove.
(__mf_find_threadinfo): Rewrite and simplify. Only use if TLS is
not available.
(__mf_state_perthread): Remove.
(__mf_get_state, __mf_set_state): New.
(__mf_pthread_cleanup): Use &errno, rather than saved pointer.
Update mf_thread_data killing procedure.
(__mf_pthread_spawner): Similarly.
(__mf_0fn_pthread_create): Only use wrapper if necessary. Remove
code to allocate thread stack space.
(__mf_0fn_pthread_join, pthread_join): Remove.
(__mf_0fn_pthread_exit, pthread_exit): Remove.
* mf-impl.h (dyn_pthread_join, dyn_pthread_exit): Remove.
(__mf_state_1): Rename from __mf_state; use TLS when available.
(__mf_get_state, __mf_set_state): New. Update all users.
* mf-runtime.c (begin_recursion_protect1): New.
(BEGIN_RECURSION_PROTECT): Use it.
(__mf_state_1): Rename from __mf_state; use TLS when available.
(threads_active_p): Remove.
(__mf_usage): Compute it directly.
* gcc.c (MFWRAP_SPEC): Don't wrap pthread_join or pthread_exit.
Index: acinclude.m4
===================================================================
RCS file: acinclude.m4
diff -N acinclude.m4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ acinclude.m4 17 Jul 2005 02:14:41 -0000
@@ -0,0 +1,72 @@
+dnl Check whether the target supports TLS.
+AC_DEFUN([LIBMUDFLAP_CHECK_TLS], [
+ LIBMUDFLAP_ENABLE(tls, yes, [Use thread-local storage])
+ AC_CACHE_CHECK([whether the target supports thread-local storage],
+ have_tls, [
+ AC_RUN_IFELSE([__thread int a; int b; int main() { return a = b; }],
+ [dnl If the test case passed with dynamic linking, try again with
+ dnl static linking. This fails at least with some older Red Hat
+ dnl releases.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="-static $LDFLAGS"
+ AC_RUN_IFELSE([__thread int a; int b; int main() { return a = b; }],
+ [have_tls=yes], [have_tls=no], [])
+ LDFLAGS="$save_LDFLAGS"],
+ [have_tls=no],
+ [AC_COMPILE_IFELSE([__thread int foo;], [have_tls=yes], [have_tls=no])]
+ )])
+ if test "$enable_tls $have_tls" = "yes yes"; then
+ AC_DEFINE(HAVE_TLS, 1,
+ [Define to 1 if the target supports thread-local storage.])
+ fi])
+
+dnl ----------------------------------------------------------------------
+dnl This whole bit snagged from libgfortran.
+
+sinclude(../libtool.m4)
+dnl The lines below arrange for aclocal not to bring an installed
+dnl libtool.m4 into aclocal.m4, while still arranging for automake to
+dnl add a definition of LIBTOOL to Makefile.in.
+ifelse(,,,[AC_SUBST(LIBTOOL)
+AC_DEFUN([AM_PROG_LIBTOOL])
+AC_DEFUN([AC_LIBTOOL_DLOPEN])
+AC_DEFUN([AC_PROG_LD])
+])
+
+dnl ----------------------------------------------------------------------
+dnl This whole bit snagged from libstdc++-v3.
+
+dnl
+dnl LIBMUDFLAP_ENABLE
+dnl (FEATURE, DEFAULT, HELP-ARG, HELP-STRING)
+dnl (FEATURE, DEFAULT, HELP-ARG, HELP-STRING, permit a|b|c)
+dnl (FEATURE, DEFAULT, HELP-ARG, HELP-STRING, SHELL-CODE-HANDLER)
+dnl
+dnl See docs/html/17_intro/configury.html#enable for documentation.
+dnl
+m4_define([LIBMUDFLAP_ENABLE],[dnl
+m4_define([_g_switch],[--enable-$1])dnl
+m4_define([_g_help],[AC_HELP_STRING(_g_switch$3,[$4 @<:@default=$2@:>@])])dnl
+ AC_ARG_ENABLE($1,_g_help,
+ m4_bmatch([$5],
+ [^permit ],
+ [[
+ case "$enableval" in
+ m4_bpatsubst([$5],[permit ])) ;;
+ *) AC_MSG_ERROR(Unknown argument to enable/disable $1) ;;
+ dnl Idea for future: generate a URL pointing to
+ dnl "onlinedocs/configopts.html#whatever"
+ esac
+ ]],
+ [^$],
+ [[
+ case "$enableval" in
+ yes|no) ;;
+ *) AC_MSG_ERROR(Argument to enable/disable $1 must be yes or no) ;;
+ esac
+ ]],
+ [[$5]]),
+ [enable_]m4_bpatsubst([$1],-,_)[=][$2])
+m4_undefine([_g_switch])dnl
+m4_undefine([_g_help])dnl
+])
Index: configure.ac
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/configure.ac,v
retrieving revision 1.11
diff -u -p -d -r1.11 configure.ac
--- configure.ac 21 Mar 2005 17:44:38 -0000 1.11
+++ configure.ac 17 Jul 2005 02:14:43 -0000
@@ -229,5 +229,8 @@ else
multilib_arg=
fi
+# See if we support thread-local storage.
+LIBMUDFLAP_CHECK_TLS
+
AC_CONFIG_FILES([Makefile testsuite/Makefile testsuite/mfconfig.exp])
AC_OUTPUT
Index: mf-hooks1.c
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/mf-hooks1.c,v
retrieving revision 1.5
diff -u -p -d -r1.5 mf-hooks1.c
--- mf-hooks1.c 22 Mar 2005 02:14:32 -0000 1.5
+++ mf-hooks1.c 17 Jul 2005 02:14:43 -0000
@@ -79,7 +79,19 @@ Software Foundation, 59 Temple Place - S
void *
__mf_0fn_malloc (size_t c)
{
- /* fprintf (stderr, "0fn malloc c=%lu\n", c); */
+ enum foo { BS = 4096, NB=10 };
+ static char bufs[NB][BS];
+ static unsigned bufs_used[NB];
+ unsigned i;
+
+ for (i=0; i<NB; i++)
+ {
+ if (! bufs_used[i] && c < BS)
+ {
+ bufs_used[i] = 1;
+ return & bufs[i][0];
+ }
+ }
return NULL;
}
#endif
@@ -114,21 +126,7 @@ WRAPPER(void *, malloc, size_t c)
void *
__mf_0fn_calloc (size_t c, size_t n)
{
- enum foo { BS = 4096, NB=10 };
- static char bufs[NB][BS];
- static unsigned bufs_used[NB];
- unsigned i;
-
- /* fprintf (stderr, "0fn calloc c=%lu n=%lu\n", c, n); */
- for (i=0; i<NB; i++)
- {
- if (! bufs_used[i] && (c*n) < BS)
- {
- bufs_used[i] = 1;
- return & bufs[i][0];
- }
- }
- return NULL;
+ return __mf_0fn_malloc (c * n);
}
#endif
@@ -194,7 +192,7 @@ WRAPPER(void *, realloc, void *buf, size
/* Ensure heap wiping doesn't occur during this peculiar
unregister/reregister pair. */
LOCKTH ();
- __mf_state = reentrant;
+ __mf_set_state (reentrant);
saved_wipe_heap = __mf_opts.wipe_heap;
__mf_opts.wipe_heap = 0;
@@ -212,7 +210,7 @@ WRAPPER(void *, realloc, void *buf, size
/* Restore previous setting. */
__mf_opts.wipe_heap = saved_wipe_heap;
- __mf_state = active;
+ __mf_set_state (active);
UNLOCKTH ();
return result;
Index: mf-hooks3.c
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/mf-hooks3.c,v
retrieving revision 1.7
diff -u -p -d -r1.7 mf-hooks3.c
--- mf-hooks3.c 22 Mar 2005 02:14:32 -0000 1.7
+++ mf-hooks3.c 17 Jul 2005 02:14:43 -0000
@@ -1,5 +1,5 @@
/* Mudflap: narrow-pointer bounds-checking by tree rewriting.
- Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Contributed by Frank Ch. Eigler <fche@redhat.com>
and Graydon Hoare <graydon@redhat.com>
@@ -52,14 +52,10 @@ Software Foundation, 59 Temple Place - S
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/mman.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
-#include <limits.h>
-#include <sched.h>
-#include <fcntl.h>
+#include <stdbool.h>
#include "mf-runtime.h"
#include "mf-impl.h"
@@ -68,276 +64,214 @@ Software Foundation, 59 Temple Place - S
#error "Do not compile this file with -fmudflap!"
#endif
-
-/* Multithreading support hooks. */
-
-
-
#ifndef LIBMUDFLAPTH
#error "pthreadstuff is to be included only in libmudflapth"
#endif
+/* ??? Why isn't this done once in the header files. */
+DECLARE(void *, malloc, size_t sz);
+DECLARE(void, free, void *ptr);
+DECLARE(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr,
+ void * (*start) (void *), void *arg);
-/* Describe a thread (dead or alive). */
-struct pthread_info
-{
- short used_p; /* Is this slot in use? */
- short dead_p; /* Is this thread dead? */
- pthread_t self; /* The thread id. */
+/* Multithreading support hooks. */
- /* If libmudflapth allocated the stack, store its adjusted base/size. */
- void *stack;
- size_t stack_size;
- /* The _alloc fields store unadjusted values from the moment of allocation. */
- void *stack_alloc;
- size_t stack_size_alloc;
- int *thread_errno;
- enum __mf_state_enum state;
-};
+#ifndef HAVE_TLS
+/* We don't have TLS. Ordinarily we could use pthread keys, but since we're
+ commandeering malloc/free that presents a few problems. The first is that
+ we'll recurse from __mf_get_state to pthread_setspecific to malloc back to
+ __mf_get_state during thread startup. This can be solved with clever uses
+ of a mutex. The second problem is that thread shutdown is indistinguishable
+ from thread startup, since libpthread is deallocating our state variable.
+ I've no good solution for this.
+ Which leaves us to handle this mess by totally by hand. */
-/* Describe the startup information for a new user thread. */
-struct pthread_start_info
-{
- /* The user's thread entry point and argument. */
- void * (*user_fn)(void *);
- void *user_arg;
+/* Yes, we want this prime. If pthread_t is a pointer, it's almost always
+ page aligned, and if we use a smaller power of 2, this results in "%N"
+ being the worst possible hash -- all threads hash to zero. */
+#define LIBMUDFLAPTH_THREADS_MAX 1021
- /* Set by user thread when this startup struct may be disposed of. */
- struct pthread_info *thread_info;
+struct mf_thread_data
+{
+ pthread_t self;
+ unsigned char used_p;
+ unsigned char state;
};
+static struct mf_thread_data mf_thread_data[LIBMUDFLAPTH_THREADS_MAX];
+static pthread_mutex_t mf_thread_data_lock = PTHREAD_MUTEX_INITIALIZER;
+/* Try to identify the main thread when filling in mf_thread_data. We
+ should always be called at least once from the main thread before
+ any new threads are spawned. */
+static int main_seen_p;
+#define PTHREAD_HASH(p) ((unsigned long) (p) % LIBMUDFLAPTH_THREADS_MAX)
-/* To avoid dynamic memory allocation, use static array to store these
- thread description structs. The second (_idx) array is used as a
- simple caching hash table, mapping PTHREAD_HASH(thread) to its
- index in __mf_pthread_info[]. */
-
-#define LIBMUDFLAPTH_THREADS_MAX 1024
-static struct pthread_info __mf_pthread_info[LIBMUDFLAPTH_THREADS_MAX];
-static unsigned __mf_pthread_info_idx[LIBMUDFLAPTH_THREADS_MAX];
-#define PTHREAD_HASH(p) ((unsigned) (p) % LIBMUDFLAPTH_THREADS_MAX)
-
-
-/* Find any old empty entry in __mf_pthread_info; mark it used and
- return it. Return NULL if there are no more available slots. */
-struct pthread_info*
-__mf_allocate_blank_threadinfo (unsigned* idx)
+static struct mf_thread_data *
+__mf_find_threadinfo (int alloc)
{
- static unsigned probe = LIBMUDFLAPTH_THREADS_MAX-1;
- unsigned probe_at_start = probe;
- static pthread_mutex_t mutex =
-#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
- PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
-#else
- PTHREAD_MUTEX_INITIALIZER;
+ pthread_t self = pthread_self ();
+ unsigned long hash = PTHREAD_HASH (self);
+ unsigned long rehash;
+
+#ifdef __alpha__
+ /* Alpha has the loosest memory ordering rules of all. We need a memory
+ barrier to flush the reorder buffer before considering a *read* of a
+ shared variable. Since we're not always taking a lock, we have to do
+ this by hand. */
+ __sync_synchronize ();
#endif
- int rc;
- rc = pthread_mutex_lock (& mutex);
- assert (rc == 0);
+ rehash = hash;
+ while (1)
+ {
+ if (mf_thread_data[rehash].used_p && mf_thread_data[rehash].self == self)
+ return &mf_thread_data[rehash];
- /* Look for a blank spot starting one past the last one we found. */
- do
+ rehash += 7;
+ if (rehash >= LIBMUDFLAPTH_THREADS_MAX)
+ rehash -= LIBMUDFLAPTH_THREADS_MAX;
+ if (rehash == hash)
+ break;
+ }
+
+ if (alloc)
{
- probe = (probe + 1) % LIBMUDFLAPTH_THREADS_MAX;
- struct pthread_info* pi = & __mf_pthread_info [probe];
- if (! pi->used_p)
+ pthread_mutex_lock (&mf_thread_data_lock);
+
+ rehash = hash;
+ while (1)
{
- /* memset (pi, 0, sizeof (*pi)); */
- pi->used_p = 1;
- if (idx != NULL) *idx = probe;
- /* VERBOSE_TRACE ("allocated threadinfo slot %u\n", probe); */
- rc = pthread_mutex_unlock (& mutex);
- assert (rc == 0);
- return pi;
+ if (!mf_thread_data[rehash].used_p)
+ {
+ mf_thread_data[rehash].self = self;
+ __sync_synchronize ();
+ mf_thread_data[rehash].used_p = 1;
+
+ pthread_mutex_unlock (&mf_thread_data_lock);
+ return &mf_thread_data[rehash];
+ }
+
+ rehash += 7;
+ if (rehash >= LIBMUDFLAPTH_THREADS_MAX)
+ rehash -= LIBMUDFLAPTH_THREADS_MAX;
+ if (rehash == hash)
+ break;
}
+
+ pthread_mutex_unlock (&mf_thread_data_lock);
}
- while (probe != probe_at_start);
- rc = pthread_mutex_unlock (& mutex);
- assert (rc == 0);
return NULL;
}
-
-/* Find and return the pthread_info struct for the current thread.
- There might already be one in __mf_pthread_info for this thread, in
- which case return it. There may not be one (if this is a main
- thread, an auxiliary -lpthread manager, or an actual user thread
- making an early call into libmudflap. In these cases, create a new
- entry. If not it's not the main thread, put it into reentrant
- initial state.
-
- NB: VERBOSE_TRACE type functions are not generally safe to call
- from this context, since a new thread might just be "booting up",
- making printf unsafe to call.
-*/
-static struct pthread_info*
-__mf_find_threadinfo ()
+enum __mf_state_enum
+__mf_get_state (void)
{
- pthread_t it = pthread_self ();
- unsigned *hash = & __mf_pthread_info_idx [PTHREAD_HASH (it)];
- struct pthread_info *result = NULL;
- static pthread_t last;
- static int main_thread_seen_p;
-
- /* Check out the lookup cache; failing that, do a linear search
- around the table. */
- {
- struct pthread_info* pi = & __mf_pthread_info [*hash];
- unsigned i;
-
- if (pi->used_p && pi->self == it)
- result = pi;
- else for (i = 0; i < LIBMUDFLAPTH_THREADS_MAX; i++)
- {
- struct pthread_info* pi2 = & __mf_pthread_info [i];
- if (pi2->used_p && pi2->self == it)
- {
- *hash = i;
- result = pi2;
- break;
- }
- }
- }
-
- if (result == NULL)
- {
- /* Create a __mf_pthread_info record for the main thread. It's
- different from the auto-recognized worker bees because for
- example we can assume that it's a fully stack/errno-equipped
- thread. */
+ struct mf_thread_data *data = __mf_find_threadinfo (0);
+ if (data)
+ return data->state;
- /* This must be the main thread, until now unseen in libmudflap. */
- unsigned *hash = & __mf_pthread_info_idx [PTHREAD_HASH (it)];
- struct pthread_info* pi = __mf_allocate_blank_threadinfo (hash);
- assert (pi != NULL);
- assert (pi->used_p);
- result = pi;
- result->self = it;
+ /* The main thread needs to default to active state, so that the global
+ constructors are processed in the active state. Child threads should
+ be considered to be in the reentrant state, so that we don't wind up
+ doing Screwy Things inside the thread library; it'll get reset to
+ active state in __mf_pthread_spawner before user code is invoked.
- if (! main_thread_seen_p)
- {
- result->state = active;
- /* NB: leave result->thread_errno unset, as main thread's errno
- has already been registered in __mf_init. */
- /* NB: leave stack-related fields unset, to avoid
- deallocation. */
- main_thread_seen_p = 1;
- /* VERBOSE_TRACE ("identified self as main thread\n"); */
- }
- else
- {
- result->state = reentrant;
- /* NB: leave result->thread_errno unset, as worker thread's
- errno is unlikely to be used, and user threads fill them
- in during __mf_pthread_spawn(). */
- /* NB: leave stack-related fields unset, leaving pthread_create
- to fill them in for user threads, leaving them empty for
- other threads. */
- /* VERBOSE_TRACE ("identified self as new aux or user thread\n"); */
- }
- }
+ The trickiest bit here is that the LinuxThreads pthread_manager thread
+ should *always* be considered to be reentrant, so that none of our
+ hooks actually do anything. Why? Because that thread isn't a real
+ thread from the point of view of the thread library, and so lots of
+ stuff isn't initialized, leading to SEGV very quickly. Even calling
+ pthread_self is a bit suspect, but it happens to work. */
- if (last != it)
+ if (main_seen_p)
+ return reentrant;
+ else
{
- /*
- VERBOSE_TRACE ("found threadinfo for %u, slot %u\n",
- (unsigned) it,
- (unsigned) *hash);
- */
- last = it;
+ main_seen_p = 1;
+ data = __mf_find_threadinfo (1);
+ data->state = active;
+ return active;
}
+}
- assert (result != NULL);
- assert (result->self == it);
-
- return result;
+void
+__mf_set_state (enum __mf_state_enum new_state)
+{
+ struct mf_thread_data *data = __mf_find_threadinfo (1);
+ data->state = new_state;
}
+#endif
+/* The following two functions are used only with __mf_opts.heur_std_data.
+ We're interested in recording the location of the thread-local errno
+ variable.
+ Note that this doesn't handle TLS references in general; we have no
+ visibility into __tls_get_data for when that memory is allocated at
+ runtime. Hopefully we get to see the malloc or mmap operation that
+ eventually allocates the backing store. */
-/* Return a pointer to the per-thread __mf_state variable. */
-enum __mf_state_enum *
-__mf_state_perthread ()
+/* Describe the startup information for a new user thread. */
+struct mf_thread_start_info
{
- assert (! __mf_starting_p);
- return & (__mf_find_threadinfo()->state);
-}
+ /* The user's thread entry point and argument. */
+ void * (*user_fn)(void *);
+ void *user_arg;
+};
static void
__mf_pthread_cleanup (void *arg)
{
- struct pthread_info *pi = arg;
-
- /* XXX: This unregistration is not safe on platforms where distinct
- threads share errno (or at least its virtual address). */
- if (pi->thread_errno != NULL)
- __mf_unregister (pi->thread_errno, sizeof (int), __MF_TYPE_GUESS);
-
- /* XXX: Only detached threads should designate themselves as dead
- here. Non-detached threads are marked dead after their
- personalized pthread_join() call. */
- pi->state = reentrant;
- pi->dead_p = 1;
+ if (__mf_opts.heur_std_data)
+ __mf_unregister (&errno, sizeof (errno), __MF_TYPE_GUESS);
- VERBOSE_TRACE ("thread pi %p exiting\n", pi);
+#ifndef HAVE_TLS
+ struct mf_thread_data *data = __mf_find_threadinfo (0);
+ if (data)
+ data->used_p = 0;
+#endif
}
static void *
__mf_pthread_spawner (void *arg)
{
- struct pthread_info *pi = __mf_find_threadinfo ();
void *result = NULL;
- /* Turn off reentrancy indications. */
- assert (pi->state == reentrant);
- pi->state = active;
-
- VERBOSE_TRACE ("new user thread\n");
+#ifndef HAVE_TLS
+ __mf_set_state (active);
+#endif
+ /* NB: We could use __MF_TYPE_STATIC here, but we guess that the thread
+ errno is coming out of some dynamically allocated pool that we already
+ know of as __MF_TYPE_HEAP. */
if (__mf_opts.heur_std_data)
- {
- pi->thread_errno = & errno;
- __mf_register (pi->thread_errno, sizeof (int),
- __MF_TYPE_GUESS, "errno area (thread)");
- /* NB: we could use __MF_TYPE_STATIC above, but we guess that
- the thread errno is coming out of some dynamically allocated
- pool that we already know of as __MF_TYPE_HEAP. */
- }
+ __mf_register (&errno, sizeof (errno), __MF_TYPE_GUESS,
+ "errno area (thread)");
/* We considered using pthread_key_t objects instead of these
cleanup stacks, but they were less cooperative with the
interposed malloc hooks in libmudflap. */
- pthread_cleanup_push (& __mf_pthread_cleanup, pi);
-
- /* Call user thread */
- {
- /* Extract given entry point and argument. */
- struct pthread_start_info *psi = arg;
- void * (*user_fn)(void *) = psi->user_fn;
- void *user_arg = psi->user_arg;
+ /* ??? The pthread_key_t problem is solved above... */
+ pthread_cleanup_push (__mf_pthread_cleanup, NULL);
- /* Signal the main thread to resume. */
- psi->thread_info = pi;
+ /* Extract given entry point and argument. */
+ struct mf_thread_start_info *psi = arg;
+ void * (*user_fn)(void *) = psi->user_fn;
+ void *user_arg = psi->user_arg;
+ CALL_REAL (free, arg);
- result = (*user_fn)(user_arg);
- }
+ result = (*user_fn)(user_arg);
pthread_cleanup_pop (1 /* execute */);
- /* NB: there is a slight race here. The pthread_info field will now
- say this thread is dead, but it may still be running .. right
- here. We try to check for this possibility using the
- pthread_kill test below. */
-
return result;
}
@@ -357,245 +291,31 @@ __mf_0fn_pthread_create (pthread_t *thr,
WRAPPER(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr,
void * (*start) (void *), void *arg)
{
- DECLARE(int, munmap, void *p, size_t l);
- DECLARE(void *, mmap, void *p, size_t l, int prot, int flags, int fd, off_t of);
- DECLARE(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr,
- void * (*start) (void *), void *arg);
- int result;
- pthread_attr_t override_attr;
- void *override_stack;
- size_t override_stacksize;
- void *override_stack_alloc = (void *) 0;
- size_t override_stacksize_alloc = 0;
- unsigned i;
+ int result, need_wrapper = 0;
TRACE ("pthread_create\n");
- /* Garbage-collect dead threads' stacks. */
- LOCKTH ();
- for (i = 0; i < LIBMUDFLAPTH_THREADS_MAX; i++)
- {
- struct pthread_info *pi = & __mf_pthread_info [i];
- if (! pi->used_p)
- continue;
- if (! pi->dead_p)
- continue;
-
- /* VERBOSE_TRACE ("thread %u pi %p stack cleanup deferred (%u)\n",
- (unsigned) pi->self, pi, pi->dead_p); */
+#ifndef HAVE_TLS
+ need_wrapper = 1;
+#endif
+ need_wrapper |= __mf_opts.heur_std_data != 0;
- /* Delay actual deallocation by a few cycles, try to discourage the
- race mentioned at the end of __mf_pthread_spawner(). */
- if (pi->dead_p)
- pi->dead_p ++;
- if (pi->dead_p >= 10 /* XXX */)
- {
- if (pi->stack)
- CALL_REAL (munmap, pi->stack_alloc, pi->stack_size_alloc);
+ if (need_wrapper)
+ {
+ struct mf_thread_start_info *si = CALL_REAL (malloc, sizeof (*si));
- VERBOSE_TRACE ("slot %u freed, stack %p\n", i, pi->stack_alloc);
- memset (pi, 0, sizeof (*pi));
+ /* Fill in startup-control fields. */
+ si->user_fn = start;
+ si->user_arg = arg;
- /* One round of garbage collection is enough. */
- break;
- }
+ /* Actually create the thread. */
+ result = CALL_REAL (pthread_create, thr, attr, __mf_pthread_spawner, si);
}
- UNLOCKTH ();
-
- /* Let's allocate a stack for this thread, if one is not already
- supplied by the caller. We don't want to let e.g. the
- linuxthreads manager thread do this allocation. */
- if (attr != NULL)
- override_attr = *attr;
else
- pthread_attr_init (& override_attr);
-
- /* Get supplied attributes, if any. */
- /* XXX: consider using POSIX2K attr_getstack() */
- if (pthread_attr_getstackaddr (& override_attr, & override_stack) != 0 ||
- pthread_attr_getstacksize (& override_attr, & override_stacksize) != 0)
{
- override_stack = NULL;
- override_stacksize = 0;
+ /* If we're not handling heur_std_data, nothing special to do. */
+ result = CALL_REAL (pthread_create, thr, attr, start, arg);
}
- /* Do we need to allocate the new thread's stack? */
- if (__mf_opts.thread_stack && override_stack == NULL)
- {
- uintptr_t alignment = 256; /* power of two */
-
- /* Perturb the initial stack addresses slightly, to encourage
- threads to have nonconflicting entries in the lookup cache
- for their tracked stack objects. */
- static unsigned perturb = 0;
- const unsigned perturb_delta = 32;
- const unsigned perturb_count = 16;
- perturb += perturb_delta;
- if (perturb > perturb_delta*perturb_count) perturb = 0;
-
- /* Use glibc x86 defaults */
-/* Should have been defined in <limits.h> */
-#ifndef PTHREAD_STACK_MIN
-#define PTHREAD_STACK_MIN 65536
-#endif
- override_stacksize = max (PTHREAD_STACK_MIN, __mf_opts.thread_stack * 1024);
-
-
-#if defined(MAP_ANONYMOUS)
-#define MF_MAP_ANON MAP_ANONYMOUS
-#elif defined(MAP_ANON)
-#define MF_MAP_ANON MAP_ANON
-#endif
-
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *) -1)
-#endif
-
-#ifdef MF_MAP_ANON
- override_stack = CALL_REAL (mmap, NULL, override_stacksize,
- PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MF_MAP_ANON,
- 0, 0);
-#else
- /* Try mapping /dev/zero instead. */
- {
- static int zerofd = -1;
- if (zerofd == -1)
- zerofd = open ("/dev/zero", O_RDWR);
- if (zerofd == -1)
- override_stack = MAP_FAILED;
- else
- override_stack = CALL_REAL (mmap, NULL, override_stacksize,
- PROT_READ|PROT_WRITE,
- MAP_PRIVATE, zerofd, 0);
- }
-#endif
-
- if (override_stack == 0 || override_stack == MAP_FAILED)
- {
- errno = EAGAIN;
- return -1;
- }
-
- VERBOSE_TRACE ("thread stack alloc %p size %lu\n",
- override_stack, (unsigned long) override_stacksize);
-
- /* Save the original allocated values for later deallocation. */
- override_stack_alloc = override_stack;
- override_stacksize_alloc = override_stacksize;
-
- /* The stackaddr pthreads attribute is a candidate stack pointer.
- It must point near the top or the bottom of this buffer, depending
- on whether stack grows downward or upward, and suitably aligned.
- On the x86, it grows down, so we set stackaddr near the top. */
- /* XXX: port logic */
- override_stack = (void *)
- (((uintptr_t) override_stack + override_stacksize - alignment - perturb)
- & (~(uintptr_t)(alignment-1)));
-
- /* XXX: consider using POSIX2K attr_setstack() */
- if (pthread_attr_setstackaddr (& override_attr, override_stack) != 0 ||
- pthread_attr_setstacksize (& override_attr,
- override_stacksize - alignment - perturb) != 0)
- {
- /* This should not happen. */
- CALL_REAL (munmap, override_stack, override_stacksize);
- errno = EAGAIN;
- return -1;
- }
- }
-
- /* Actually start the child thread. */
- {
- struct pthread_start_info psi;
- struct pthread_info *pi = NULL;
-
- /* Fill in startup-control fields. */
- psi.user_fn = start;
- psi.user_arg = arg;
- psi.thread_info = NULL;
-
- /* Actually create the thread. */
- __mf_state = reentrant;
- result = CALL_REAL (pthread_create, thr, & override_attr,
- & __mf_pthread_spawner, (void *) & psi);
- __mf_state = active;
- /* We also hook pthread_join/pthread_exit to get into reentrant
- mode during thread shutdown/cleanup. */
-
- /* Wait until child thread has progressed far enough into its
- __mf_pthread_spawner() call. */
- while (1) /* XXX: timeout? */
- {
- volatile struct pthread_start_info *psip = & psi;
- pi = psip->thread_info;
- if (pi != NULL)
- break;
- sched_yield ();
- }
-
- /* Fill in remaining fields in pthread_info. */
- pi->stack = override_stack;
- pi->stack_size = override_stacksize;
- pi->stack_alloc = override_stack_alloc;
- pi->stack_size_alloc = override_stacksize_alloc;
- /* XXX: this might be too late for future heuristics that attempt
- to use thread stack bounds. We may need to put the new thread
- to sleep. */
- }
-
-
- /* May need to clean up if we created a pthread_attr_t of our own. */
- if (attr == NULL)
- pthread_attr_destroy (& override_attr); /* NB: this shouldn't deallocate stack */
-
return result;
}
-
-
-
-#if PIC
-/* A special bootstrap variant. */
-int
-__mf_0fn_pthread_join (pthread_t thr, void **rc)
-{
- return -1;
-}
-#endif
-
-
-#undef pthread_join
-WRAPPER(int, pthread_join, pthread_t thr, void **rc)
-{
- DECLARE(int, pthread_join, pthread_t thr, void **rc);
- int result;
-
- TRACE ("pthread_join\n");
- __mf_state = reentrant;
- result = CALL_REAL (pthread_join, thr, rc);
- __mf_state = active;
-
- return result;
-}
-
-
-#if PIC
-/* A special bootstrap variant. */
-void
-__mf_0fn_pthread_exit (void *rc)
-{
-}
-#endif
-
-
-#undef pthread_exit
-WRAPPER(void, pthread_exit, void *rc)
-{
- DECLARE(void, pthread_exit, void *rc);
-
- TRACE ("pthread_exit\n");
- /* __mf_state = reentrant; */
- CALL_REAL (pthread_exit, rc);
- /* NOTREACHED */
- exit (0); /* Satisfy noreturn attribute of pthread_exit. */
-}
Index: mf-impl.h
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/mf-impl.h,v
retrieving revision 1.7
diff -u -p -d -r1.7 mf-impl.h
--- mf-impl.h 14 Jun 2005 18:36:50 -0000 1.7
+++ mf-impl.h 17 Jul 2005 02:14:43 -0000
@@ -207,9 +207,7 @@ enum __mf_dynamic_index
dyn_munmap, dyn_realloc,
dyn_INITRESOLVE, /* Marker for last init-time resolution. */
#ifdef LIBMUDFLAPTH
- dyn_pthread_create,
- dyn_pthread_join,
- dyn_pthread_exit
+ dyn_pthread_create
#endif
};
@@ -233,12 +231,25 @@ extern pthread_mutex_t __mf_biglock;
#define UNLOCKTH() do {} while (0)
#endif
-#ifdef LIBMUDFLAPTH
-extern enum __mf_state_enum *__mf_state_perthread ();
-#define __mf_state (* __mf_state_perthread ())
+#if defined(LIBMUDFLAPTH) && !defined(HAVE_TLS)
+extern enum __mf_state_enum __mf_get_state (void);
+extern void __mf_set_state (enum __mf_state_enum);
#else
-extern enum __mf_state_enum __mf_state;
+# ifdef LIBMUDFLAPTH
+extern __thread enum __mf_state_enum __mf_state_1;
+# else
+extern enum __mf_state_enum __mf_state_1;
+# endif
+static inline enum __mf_state_enum __mf_get_state (void)
+{
+ return __mf_state_1;
+}
+static inline void __mf_set_state (enum __mf_state_enum s)
+{
+ __mf_state_1 = s;
+}
#endif
+
extern int __mf_starting_p;
extern struct __mf_options __mf_opts;
@@ -362,7 +373,7 @@ ret __mfwrap_ ## fname (__VA_ARGS__)
{ \
return CALL_BACKUP(fname, __VA_ARGS__); \
} \
- else if (UNLIKELY (__mf_state == reentrant)) \
+ else if (UNLIKELY (__mf_get_state () == reentrant)) \
{ \
extern unsigned long __mf_reentrancy; \
__mf_reentrancy ++; \
Index: mf-runtime.c
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/mf-runtime.c,v
retrieving revision 1.23
diff -u -p -d -r1.23 mf-runtime.c
--- mf-runtime.c 15 Jun 2005 16:15:39 -0000 1.23
+++ mf-runtime.c 17 Jul 2005 02:14:44 -0000
@@ -141,20 +141,25 @@ static void mfsplay_tree_rebalance (mfsp
#define __MF_VIOL_WATCH 5
/* Protect against recursive calls. */
-#define BEGIN_RECURSION_PROTECT() do { \
- if (UNLIKELY (__mf_state == reentrant)) { \
- write (2, "mf: erroneous reentrancy detected in `", 38); \
- write (2, __PRETTY_FUNCTION__, strlen(__PRETTY_FUNCTION__)); \
- write (2, "'\n", 2); \
- abort (); } \
- __mf_state = reentrant; \
- } while (0)
-#define END_RECURSION_PROTECT() do { \
- __mf_state = active; \
- } while (0)
+static void
+begin_recursion_protect1 (const char *pf)
+{
+ if (__mf_get_state () == reentrant)
+ {
+ write (2, "mf: erroneous reentrancy detected in `", 38);
+ write (2, pf, strlen(pf));
+ write (2, "'\n", 2); \
+ abort ();
+ }
+ __mf_set_state (reentrant);
+}
+#define BEGIN_RECURSION_PROTECT() \
+ begin_recursion_protect1 (__PRETTY_FUNCTION__)
+#define END_RECURSION_PROTECT() \
+ __mf_set_state (active)
/* ------------------------------------------------------------------------ */
/* Required globals. */
@@ -169,15 +174,16 @@ unsigned char __mf_lc_shift = LOOKUP_CAC
#define LOOKUP_CACHE_SIZE (__mf_lc_mask + 1)
struct __mf_options __mf_opts;
-
int __mf_starting_p = 1;
-#ifndef LIBMUDFLAPTH
-enum __mf_state_enum __mf_state = active;
+
+#ifdef LIBMUDFLAPTH
+#ifdef HAVE_TLS
+__thread enum __mf_state_enum __mf_state_1 = active;
+#endif
#else
-/* See __mf_state_perthread() in mf-hooks.c. */
+enum __mf_state_enum __mf_state_1 = active;
#endif
-
#ifdef LIBMUDFLAPTH
pthread_mutex_t __mf_biglock =
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
@@ -196,7 +202,6 @@ pthread_mutex_t __mf_biglock =
#else
#define pthread_join NULL
#endif
-const void *threads_active_p = (void *) pthread_join;
#endif
@@ -442,7 +447,7 @@ __mf_usage ()
"any of the following options. Use `-no-OPTION' to disable options.\n"
"\n",
#if HAVE_PTHREAD_H
- (threads_active_p ? "multi-threaded " : "single-threaded "),
+ (pthread_join ? "multi-threaded " : "single-threaded "),
#else
"",
#endif
@@ -2211,7 +2216,7 @@ __mf_sigusr1_respond ()
if (__mf_sigusr1_received > __mf_sigusr1_handled)
{
__mf_sigusr1_handled ++;
- assert (__mf_state == reentrant);
+ assert (__mf_get_state () == reentrant);
__mfu_report ();
handler_installed = 0; /* We may need to re-enable signal; this might be a SysV library. */
}
Index: gcc.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/gcc.c,v
retrieving revision 1.465
diff -u -p -d -r1.465 gcc.c
--- gcc.c 12 Jul 2005 18:10:32 -0000 1.465
+++ gcc.c 17 Jul 2005 02:25:13 -0000
@@ -598,7 +598,7 @@ proper position among the other output f
#define MFWRAP_SPEC " %{static: %{fmudflap|fmudflapth: \
--wrap=malloc --wrap=free --wrap=calloc --wrap=realloc\
--wrap=mmap --wrap=munmap --wrap=alloca\
-} %{fmudflapth: --wrap=pthread_create --wrap=pthread_join --wrap=pthread_exit\
+} %{fmudflapth: --wrap=pthread_create\
}} %{fmudflap|fmudflapth: --wrap=main}"
#endif
#ifndef MFLIB_SPEC