summaryrefslogtreecommitdiff
path: root/pthread_support.c
diff options
context:
space:
mode:
authorivmai <ivmai>2010-08-14 10:06:17 +0200
committerIvan Maidanski <ivmai@mail.ru>2011-07-26 19:06:54 +0200
commit9fe5bf2f4a698aeb4d24322844a113a0d0178a26 (patch)
tree76bdf6b856af7e9e5584741edf8c66c48aedeb58 /pthread_support.c
parent0bf3ec4a9e0ed4ff241c57c36cb63544a6899513 (diff)
2010-08-14 Ivan Maidanski <ivmai@mail.ru> (with help from Hans Boehm)
* include/gc_pthread_redirects.h: Test GC_PTHREADS and GC_H at the beginning of the file. * include/gc_pthread_redirects.h (GC_PTHREAD_EXIT_ATTRIBUTE): New macro (defined only for Linux and Solaris). * include/gc_pthread_redirects.h (GC_pthread_cancel, GC_pthread_exit): Declare new API function (only if GC_PTHREAD_EXIT_ATTRIBUTE). * include/gc_pthread_redirects.h (pthread_cancel, pthread_exit): Redirect (if GC_PTHREAD_EXIT_ATTRIBUTE). * include/private/pthread_support.h (DISABLED_GC): New macro. * pthread_support.c (pthread_cancel, pthread_exit): Restore original definition or declare "real" function (if needed and GC_PTHREAD_EXIT_ATTRIBUTE). * pthread_support.c (GC_pthread_cancel_t, GC_pthread_exit_t): Declare new types if needed. * pthread_support.c (GC_pthread_cancel, GC_pthread_exit): New function definition (only if GC_PTHREAD_EXIT_ATTRIBUTE). * pthread_support.c (GC_init_real_syms): Initialise pointers to the "real" pthread_cancel and pthread_exit (only if GC_PTHREAD_EXIT_ATTRIBUTE). * pthread_support.c (GC_unregister_my_thread): Enable collections if DISABLED_GC was set (only if GC_PTHREAD_EXIT_ATTRIBUTE). * pthread_support.c (pthread_cancel, pthread_exit): New wrapped function definition (only if GC_PTHREAD_EXIT_ATTRIBUTE defined). * pthread_support.c (GC_start_routine): Refine the comment. * extra/threadlibs.c (main): Adjust --wrap (add "read", "pthread_exit", "pthread_cancel" but remove "sleep"). * doc/README.linux (GC_USE_LD_WRAP): Ditto. * doc/README.linux: Expand all tabs to spaces; remove trailing spaces at EOLn.
Diffstat (limited to 'pthread_support.c')
-rw-r--r--pthread_support.c101
1 files changed, 99 insertions, 2 deletions
diff --git a/pthread_support.c b/pthread_support.c
index 6b3480d..0c89466 100644
--- a/pthread_support.c
+++ b/pthread_support.c
@@ -96,6 +96,10 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
# if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS)
# undef pthread_sigmask
# endif
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+# undef pthread_cancel
+# undef pthread_exit
+# endif
# undef pthread_join
# undef pthread_detach
# if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \
@@ -104,6 +108,10 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
# define pthread_create __pthread_create
# define pthread_join __pthread_join
# define pthread_detach __pthread_detach
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+# define pthread_cancel __pthread_cancel
+# define pthread_exit __pthread_exit
+# endif
# endif
#ifdef GC_USE_LD_WRAP
@@ -116,6 +124,10 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
# if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS)
int REAL_FUNC(pthread_sigmask)(int, const sigset_t *, sigset_t *);
# endif
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+ int REAL_FUNC(pthread_cancel)(pthread_t);
+ void REAL_FUNC(pthread_exit)(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
+# endif
#else
# ifdef GC_USE_DLOPEN_WRAP
# include <dlfcn.h>
@@ -134,6 +146,12 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
static GC_pthread_join_t REAL_FUNC(pthread_join);
typedef int (* GC_pthread_detach_t)(pthread_t);
static GC_pthread_detach_t REAL_FUNC(pthread_detach);
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+ typedef int (* GC_pthread_cancel_t)(pthread_t);
+ static GC_pthread_cancel_t REAL_FUNC(pthread_cancel);
+ typedef void (* GC_pthread_exit_t)(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
+ static GC_pthread_exit_t REAL_FUNC(pthread_exit);
+# endif
# else
# define WRAP_FUNC(f) GC_##f
# if !defined(GC_DGUX386_THREADS)
@@ -169,6 +187,18 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
{
return pthread_detach(t);
}
+
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+ GC_API int GC_pthread_cancel(pthread_t t)
+ {
+ return pthread_cancel(t);
+ }
+
+ GC_API GC_PTHREAD_EXIT_ATTRIBUTE void GC_pthread_exit(void *retval)
+ {
+ pthread_exit(retval);
+ }
+# endif /* GC_PTHREAD_EXIT_ATTRIBUTE */
#endif /* Linker-based interception. */
#ifdef GC_USE_DLOPEN_WRAP
@@ -207,6 +237,12 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
dlsym(dl_handle, "pthread_join");
REAL_FUNC(pthread_detach) = (GC_pthread_detach_t)
dlsym(dl_handle, "pthread_detach");
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+ REAL_FUNC(pthread_cancel) = (GC_pthread_cancel_t)
+ dlsym(dl_handle, "pthread_cancel");
+ REAL_FUNC(pthread_exit) = (GC_pthread_exit_t)
+ dlsym(dl_handle, "pthread_exit");
+# endif
GC_syms_initialized = TRUE;
}
@@ -1055,6 +1091,13 @@ GC_API int GC_CALL GC_unregister_my_thread(void)
# if defined(THREAD_LOCAL_ALLOC)
GC_destroy_thread_local(&(me->tlfs));
# endif
+# ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+ /* Handle DISABLED_GC flag which is set by the */
+ /* intercepted pthread_cancel or pthread_exit. */
+ if ((me -> flags & DISABLED_GC) != 0) {
+ GC_dont_gc--;
+ }
+# endif
if (me -> flags & DETACHED) {
GC_delete_thread(pthread_self());
} else {
@@ -1091,7 +1134,7 @@ GC_API int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval)
/* can't have been recycled by pthreads. */
UNLOCK();
result = REAL_FUNC(pthread_join)(thread, retval);
-# if defined (GC_FREEBSD_THREADS)
+# if defined(GC_FREEBSD_THREADS)
/* On FreeBSD, the wrapped pthread_join() sometimes returns (what
appears to be) a spurious EINTR which caused the test and real code
to gratuitously fail. Having looked at system pthread library source
@@ -1134,6 +1177,60 @@ GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread)
return result;
}
+#ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+ /* We should deal with the fact that apparently on Solaris and, */
+ /* probably, on some Linux we can't collect while a thread is */
+ /* exiting, since signals aren't handled properly. This currently */
+ /* gives rise to deadlocks. The only workaround seen is to intercept */
+ /* pthread_cancel() and pthread_exit(), and disable the collections */
+ /* until the thread exit handler is called. That's ugly, because we */
+ /* risk growing the heap unnecessarily. But it seems that we don't */
+ /* really have an option in that the process is not in a fully */
+ /* functional state while a thread is exiting. */
+
+ GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread)
+ {
+# ifdef CANCEL_SAFE
+ GC_thread thread_gc_id;
+ DCL_LOCK_STATE;
+# endif
+
+ INIT_REAL_SYMS();
+# ifdef CANCEL_SAFE
+ LOCK();
+ thread_gc_id = GC_lookup_thread(thread);
+ /* We test DISABLED_GC because pthread_exit could be called at */
+ /* the same time. (If thread_gc_id is NULL then pthread_cancel */
+ /* should return ESRCH.) */
+ if (thread_gc_id != 0 && (thread_gc_id -> flags & DISABLED_GC) == 0) {
+ thread_gc_id -> flags |= DISABLED_GC;
+ GC_dont_gc++;
+ }
+ UNLOCK();
+# endif
+ return REAL_FUNC(pthread_cancel)(thread);
+ }
+
+ GC_API GC_PTHREAD_EXIT_ATTRIBUTE void WRAP_FUNC(pthread_exit)(void *retval)
+ {
+ GC_thread me;
+ DCL_LOCK_STATE;
+
+ INIT_REAL_SYMS();
+ LOCK();
+ me = GC_lookup_thread(pthread_self());
+ /* We test DISABLED_GC because someone else could call */
+ /* pthread_cancel at the same time. */
+ if (me != 0 && (me -> flags & DISABLED_GC) == 0) {
+ me -> flags |= DISABLED_GC;
+ GC_dont_gc++;
+ }
+ UNLOCK();
+
+ REAL_FUNC(pthread_exit)(retval);
+ }
+#endif /* GC_PTHREAD_EXIT_ATTRIBUTE */
+
GC_INNER GC_bool GC_in_thread_creation = FALSE;
/* Protected by allocation lock. */
@@ -1250,7 +1347,7 @@ STATIC void * GC_start_routine(void * arg)
/* GC_get_stack_base may call pthread_getattr_np, which can */
/* unfortunately call realloc, which may allocate from an */
/* unregistered thread. This is unpleasant, since it might */
- /* force heap growth. */
+ /* force heap growth (or, even, heap overflow). */
GC_disable();
# endif
if (GC_get_stack_base(&sb) != GC_SUCCESS)