From 969120981e1ad51bc5f64462a74c3aa25a285aa3 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 8 Feb 2012 05:30:12 +0000 Subject: [PATCH] runtime: System-specific hack fix for x86_64 Solaris 10. Fixes problem in which setcontext changes all thread-specific information. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@183993 138bc75d-0d04-0410-961f-82ee72b054a4 --- libgo/config.h.in | 3 ++ libgo/configure | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libgo/configure.ac | 81 +++++++++++++++++++++++++++++++++++++++++++ libgo/runtime/proc.c | 55 +++++++++++++++++++++++++++++ 4 files changed, 237 insertions(+) diff --git a/libgo/config.h.in b/libgo/config.h.in index c9f497d..bd19873 100644 --- a/libgo/config.h.in +++ b/libgo/config.h.in @@ -189,6 +189,9 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define if setcontext clobbers TLS variables */ +#undef SETCONTEXT_CLOBBERS_TLS + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/libgo/configure b/libgo/configure index 94bf268..9b65c25 100755 --- a/libgo/configure +++ b/libgo/configure @@ -14756,6 +14756,104 @@ $as_echo "$libgo_cv_c_epoll_event_fd_offset" >&6; } STRUCT_EPOLL_EVENT_FD_OFFSET=${libgo_cv_c_epoll_event_fd_offset} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether setcontext clobbers TLS variables" >&5 +$as_echo_n "checking whether setcontext clobbers TLS variables... " >&6; } +if test "${libgo_cv_lib_setcontext_clobbers_tls+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + LIBS_hold="$LIBS" +LIBS="$LIBS $PTHREAD_LIBS" +if test "$cross_compiling" = yes; then : + case "$target" in + x86_64*-*-solaris2.10) libgo_cv_lib_setcontext_clobbers_tls=yes ;; + *) libgo_cv_lib_setcontext_clobbers_tls=no ;; + esac + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +#include + +__thread int tls; + +static char stack[10 * 1024 * 1024]; +static ucontext_t c; + +/* Called via makecontext/setcontext. */ + +static void +cfn (void) +{ + exit (tls); +} + +/* Called via pthread_create. */ + +static void * +tfn (void *dummy) +{ + /* The thread should still see this value after calling + setcontext. */ + tls = 0; + + setcontext (&c); + + /* The call to setcontext should not return. */ + abort (); +} + +int +main () +{ + pthread_t tid; + + /* The thread should not see this value. */ + tls = 1; + + if (getcontext (&c) < 0) + abort (); + + c.uc_stack.ss_sp = stack; + c.uc_stack.ss_flags = 0; + c.uc_stack.ss_size = sizeof stack; + c.uc_link = NULL; + makecontext (&c, cfn, 0); + + if (pthread_create (&tid, NULL, tfn, NULL) != 0) + abort (); + + if (pthread_join (tid, NULL) != 0) + abort (); + + /* The thread should have called exit. */ + abort (); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + libgo_cv_lib_setcontext_clobbers_tls=no +else + libgo_cv_lib_setcontext_clobbers_tls=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +LIBS="$LIBS_hold" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgo_cv_lib_setcontext_clobbers_tls" >&5 +$as_echo "$libgo_cv_lib_setcontext_clobbers_tls" >&6; } +if test "$libgo_cv_lib_setcontext_clobbers_tls" = "yes"; then + +$as_echo "#define SETCONTEXT_CLOBBERS_TLS 1" >>confdefs.h + +fi + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure diff --git a/libgo/configure.ac b/libgo/configure.ac index e815887..3de5b4a 100644 --- a/libgo/configure.ac +++ b/libgo/configure.ac @@ -549,6 +549,87 @@ AC_CACHE_CHECK([epoll_event data.fd offset], STRUCT_EPOLL_EVENT_FD_OFFSET=${libgo_cv_c_epoll_event_fd_offset} AC_SUBST(STRUCT_EPOLL_EVENT_FD_OFFSET) +dnl See whether setcontext changes the value of TLS variables. +AC_CACHE_CHECK([whether setcontext clobbers TLS variables], +[libgo_cv_lib_setcontext_clobbers_tls], +[LIBS_hold="$LIBS" +LIBS="$LIBS $PTHREAD_LIBS" +AC_RUN_IFELSE( + [AC_LANG_SOURCE([ +#include +#include +#include +#include + +__thread int tls; + +static char stack[[10 * 1024 * 1024]]; +static ucontext_t c; + +/* Called via makecontext/setcontext. */ + +static void +cfn (void) +{ + exit (tls); +} + +/* Called via pthread_create. */ + +static void * +tfn (void *dummy) +{ + /* The thread should still see this value after calling + setcontext. */ + tls = 0; + + setcontext (&c); + + /* The call to setcontext should not return. */ + abort (); +} + +int +main () +{ + pthread_t tid; + + /* The thread should not see this value. */ + tls = 1; + + if (getcontext (&c) < 0) + abort (); + + c.uc_stack.ss_sp = stack; + c.uc_stack.ss_flags = 0; + c.uc_stack.ss_size = sizeof stack; + c.uc_link = NULL; + makecontext (&c, cfn, 0); + + if (pthread_create (&tid, NULL, tfn, NULL) != 0) + abort (); + + if (pthread_join (tid, NULL) != 0) + abort (); + + /* The thread should have called exit. */ + abort (); +} +])], +[libgo_cv_lib_setcontext_clobbers_tls=no], +[libgo_cv_lib_setcontext_clobbers_tls=yes], +[case "$target" in + x86_64*-*-solaris2.10) libgo_cv_lib_setcontext_clobbers_tls=yes ;; + *) libgo_cv_lib_setcontext_clobbers_tls=no ;; + esac +]) +LIBS="$LIBS_hold" +]) +if test "$libgo_cv_lib_setcontext_clobbers_tls" = "yes"; then + AC_DEFINE(SETCONTEXT_CLOBBERS_TLS, 1, + [Define if setcontext clobbers TLS variables]) +fi + AC_CACHE_SAVE if test ${multilib} = yes; then diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 9225f82..04412bd 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -60,6 +60,54 @@ G runtime_g0; // idle goroutine for m0 static __thread G *g; static __thread M *m; +#ifndef SETCONTEXT_CLOBBERS_TLS + +static inline void +initcontext(void) +{ +} + +static inline void +fixcontext(ucontext_t *c __attribute__ ((unused))) +{ +} + +# else + +# if defined(__x86_64__) && defined(__sun__) + +// x86_64 Solaris 10 and 11 have a bug: setcontext switches the %fs +// register to that of the thread which called getcontext. The effect +// is that the address of all __thread variables changes. This bug +// also affects pthread_self() and pthread_getspecific. We work +// around it by clobbering the context field directly to keep %fs the +// same. + +static __thread greg_t fs; + +static inline void +initcontext(void) +{ + ucontext_t c; + + getcontext(&c); + fs = c.uc_mcontext.gregs[REG_FSBASE]; +} + +static inline void +fixcontext(ucontext_t* c) +{ + c->uc_mcontext.gregs[REG_FSBASE] = fs; +} + +# else + +# error unknown case for SETCONTEXT_CLOBBERS_TLS + +# endif + +#endif + // We can not always refer to the TLS variables directly. The // compiler will call tls_get_addr to get the address of the variable, // and it may hold it in a register across a call to schedule. When @@ -248,7 +296,9 @@ runtime_gogo(G* newg) #endif g = newg; newg->fromgogo = true; + fixcontext(&newg->context); setcontext(&newg->context); + runtime_throw("gogo setcontext returned"); } // Save context and call fn passing g as a parameter. This is like @@ -287,6 +337,7 @@ runtime_mcall(void (*pfn)(G*)) m->g0->entry = (byte*)pfn; m->g0->param = g; g = m->g0; + fixcontext(&m->g0->context); setcontext(&m->g0->context); runtime_throw("runtime: mcall function returned"); } @@ -312,6 +363,8 @@ runtime_schedinit(void) m->curg = g; g->m = m; + initcontext(); + m->nomemprof++; runtime_mallocinit(); mcommoninit(m); @@ -844,6 +897,8 @@ runtime_mstart(void* mp) m = (M*)mp; g = m->g0; + initcontext(); + g->entry = nil; g->param = nil; -- 2.7.4