From: ivmai Date: Sat, 14 Aug 2010 08:06:17 +0000 (+0000) Subject: 2010-08-14 Ivan Maidanski (with help from Hans Boehm) X-Git-Tag: gc7_2alpha5-20110107~38 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9fe5bf2f4a698aeb4d24322844a113a0d0178a26;p=gc 2010-08-14 Ivan Maidanski (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. --- diff --git a/ChangeLog b/ChangeLog index a98bd6cf..9a558a93 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2010-08-14 Ivan Maidanski (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. + 2010-08-12 Ivan Maidanski (mostly really Mike Gran) * include/gc.h (GC_MALLOC_STUBBORN): Remove trailing ';' in the diff --git a/doc/README.linux b/doc/README.linux index 3c501814..e462e711 100644 --- a/doc/README.linux +++ b/doc/README.linux @@ -11,7 +11,7 @@ Incremental GC is generally supported. Dynamic libraries are supported on an ELF system. A static executable should be linked with the gcc option "-Wl,-defsym,_DYNAMIC=0". -The collector appears to work reliably with Linux threads, but beware +The collector appears to work reliably with Linux threads, but beware of older versions of glibc and gdb. The garbage collector uses SIGPWR and SIGXCPU if it is used with @@ -29,7 +29,7 @@ To use threads, you need to abide by the following requirements: 2) You must compile the collector with -DGC_LINUX_THREADS (or just -DGC_THREADS) and -D_REENTRANT specified in the Makefile. -3a) Every file that makes thread calls should define GC_LINUX_THREADS and +3a) Every file that makes thread calls should define GC_LINUX_THREADS and _REENTRANT and then include gc.h. Gc.h redefines some of the pthread primitives as macros which also provide the collector with information it requires. @@ -38,13 +38,13 @@ To use threads, you need to abide by the following requirements: with -DGC_USE_LD_WRAP, and to link the final program with (for ld) --wrap read --wrap dlopen --wrap pthread_create \ - --wrap pthread_join --wrap pthread_detach \ - --wrap pthread_sigmask --wrap sleep + --wrap pthread_join --wrap pthread_detach \ + --wrap pthread_sigmask --wrap pthread_exit --wrap pthread_cancel (for gcc) -Wl,--wrap -Wl,read -Wl,--wrap -Wl,dlopen -Wl,--wrap \ - -Wl,pthread_create -Wl,--wrap -Wl,pthread_join -Wl,--wrap \ - -Wl,pthread_detach -Wl,--wrap -Wl,pthread_sigmask \ - -Wl,--wrap -Wl,sleep + -Wl,pthread_create -Wl,--wrap -Wl,pthread_join -Wl,--wrap \ + -Wl,pthread_detach -Wl,--wrap -Wl,pthread_sigmask \ + -Wl,--wrap -Wl,pthread_exit -Wl,--wrap -Wl,pthread_cancel In any case, _REENTRANT should be defined during compilation. @@ -75,7 +75,7 @@ patches to correct the problem in 68040 buserror handler but it is not yet in any standard kernel. Here is a simple test program to detect whether the kernel has the -problem. It could be run as a separate check in configure or tested +problem. It could be run as a separate check in configure or tested upon startup. If it fails (return !0) than mprotect can't be used on that system. @@ -127,5 +127,3 @@ main() fprintf(stderr,"vmtest Ok\n"); exit(0); } - - diff --git a/extra/threadlibs.c b/extra/threadlibs.c index b14b8a7f..5184ef88 100644 --- a/extra/threadlibs.c +++ b/extra/threadlibs.c @@ -2,7 +2,7 @@ * Copyright (c) 1994 by Xerox Corporation. All rights reserved. * Copyright (c) 1996 by Silicon Graphics. All rights reserved. * Copyright (c) 1998 by Fergus Henderson. All rights reserved. - * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. * All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED @@ -23,10 +23,10 @@ int main(void) { # if defined(GC_USE_LD_WRAP) - printf("-Wl,--wrap -Wl,dlopen " + printf("-Wl,--wrap -Wl,read -Wl,--wrap -Wl,dlopen " "-Wl,--wrap -Wl,pthread_create -Wl,--wrap -Wl,pthread_join " - "-Wl,--wrap -Wl,pthread_detach " - "-Wl,--wrap -Wl,pthread_sigmask -Wl,--wrap -Wl,sleep\n"); + "-Wl,--wrap -Wl,pthread_detach -Wl,--wrap -Wl,pthread_sigmask " + "-Wl,--wrap -Wl,pthread_exit -Wl,--wrap -Wl,pthread_cancel\n"); # endif # if defined(GC_LINUX_THREADS) || defined(GC_IRIX_THREADS) \ || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) \ diff --git a/include/gc_pthread_redirects.h b/include/gc_pthread_redirects.h index 7e1162b6..9405646e 100644 --- a/include/gc_pthread_redirects.h +++ b/include/gc_pthread_redirects.h @@ -2,7 +2,7 @@ * Copyright (c) 1994 by Xerox Corporation. All rights reserved. * Copyright (c) 1996 by Silicon Graphics. All rights reserved. * Copyright (c) 1998 by Fergus Henderson. All rights reserved. - * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. * All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED @@ -19,6 +19,7 @@ /* calls. We arrange to do that here, if appropriate. */ /* Included from gc.h only. Included only if GC_PTHREADS. */ +#if defined(GC_H) && defined(GC_PTHREADS) /* We need to intercept calls to many of the threads primitives, so */ /* that we can locate thread stacks and stop the world. */ @@ -47,11 +48,27 @@ GC_API int GC_pthread_create(pthread_t *, const pthread_attr_t *, GC_API int GC_pthread_join(pthread_t, void ** /* retval */); GC_API int GC_pthread_detach(pthread_t); +#if !defined(GC_PTHREAD_EXIT_ATTRIBUTE) \ + && (defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS)) + /* Intercept pthread_cancel and pthread_exit on Linux and Solaris. */ +# if defined(__GNUC__) /* since GCC v2.7 */ +# define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__)) +# elif defined(__NORETURN) /* used in Solaris */ +# define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN +# else +# define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */ +# endif +#endif + +#ifdef GC_PTHREAD_EXIT_ATTRIBUTE + GC_API int GC_pthread_cancel(pthread_t); + GC_API void GC_pthread_exit(void *) GC_PTHREAD_EXIT_ATTRIBUTE; +#endif + #if !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP) - /* Unless the compiler supports #pragma extern_prefix, the Tru64 */ - /* UNIX redefines some POSIX thread functions to use */ - /* mangled names. Anyway, it's safe to undef them before */ - /* redefining. */ + /* Unless the compiler supports #pragma extern_prefix, the Tru64 */ + /* UNIX redefines some POSIX thread functions to use */ + /* mangled names. Anyway, it's safe to undef them before redefining. */ # undef pthread_create # undef pthread_join # undef pthread_detach @@ -68,4 +85,13 @@ GC_API int GC_pthread_detach(pthread_t); # undef dlopen # define dlopen GC_dlopen # endif + +# ifdef GC_PTHREAD_EXIT_ATTRIBUTE +# undef pthread_cancel +# define pthread_cancel GC_pthread_cancel +# undef pthread_exit +# define pthread_exit GC_pthread_exit +# endif #endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_PTHREADS */ diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h index 7b7902fa..5dca50c0 100644 --- a/include/private/pthread_support.h +++ b/include/private/pthread_support.h @@ -59,6 +59,9 @@ typedef struct GC_Thread_Rep { /* it unregisters itself, since it */ /* may not return a GC pointer. */ # define MAIN_THREAD 4 /* True for the original thread only. */ +# define DISABLED_GC 8 /* Collections are disabled while the */ + /* thread is exiting. */ + short thread_blocked; /* Protected by GC lock. */ /* Treated as a boolean value. If set, */ /* thread will acquire GC lock before */ diff --git a/pthread_support.c b/pthread_support.c index 6b3480d3..0c89466a 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 @@ -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; } @@ -1054,6 +1090,13 @@ GC_API int GC_CALL GC_unregister_my_thread(void) GC_ASSERT(!(me -> flags & FINISHED)); # 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()); @@ -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)