From: Julian Seward Date: Sat, 12 Oct 2002 16:42:35 +0000 (+0000) Subject: Finish off support for Red Hat 8 thread-specific-data. Unfortunately X-Git-Tag: svn/VALGRIND_1_9_4~219 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=99c3bf9dbb17f4aaf351d18f16d43faee6487158;p=thirdparty%2Fvalgrind.git Finish off support for Red Hat 8 thread-specific-data. Unfortunately the appearance of libc_internal_tsd_address requires a redesign, since the existing scheme doesn't make it sensible to take the address of a specific-data value. New scheme is that the ThreadState structure carries not the table of specifics, but merely a pointer to such. This is allocated from the client-side library, thus residing in client-visible memory and so addresses can validlyh be taken. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1208 --- diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c index 70abb179df..048f175bca 100644 --- a/coregrind/arch/x86-linux/vg_libpthread.c +++ b/coregrind/arch/x86-linux/vg_libpthread.c @@ -99,6 +99,9 @@ int my_do_syscall3 ( int syscallno, extern __locale_t __uselocale ( __locale_t ); #endif +static +void init_libc_tsd_keys ( void ); + /* --------------------------------------------------------------------- Helpers. We have to be pretty self-sufficient. @@ -229,6 +232,27 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn ) __FILE__, __LINE__, \ __PRETTY_FUNCTION__), 0))) +static +void my_free ( void* ptr ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__FREE, ptr, 0, 0, 0); + my_assert(res == 0); +} + + +static +void* my_malloc ( int nbytes ) +{ + void* res; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__MALLOC, nbytes, 0, 0, 0); + my_assert(res != (void*)0); + return res; +} + + /* --------------------------------------------------------------------- Pass pthread_ calls to Valgrind's request mechanism. @@ -252,6 +276,24 @@ void pthread_error ( const char* msg ) } +/* --------------------------------------------------- + Here so it can be inlined without complaint. + ------------------------------------------------ */ + +__inline__ +pthread_t pthread_self(void) +{ + int tid; + ensure_valgrind("pthread_self"); + VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + if (tid < 1 || tid >= VG_N_THREADS) + barf("pthread_self: invalid ThreadId"); + return tid; +} + + /* --------------------------------------------------- THREAD ATTRIBUTES ------------------------------------------------ */ @@ -443,6 +485,7 @@ void thread_exit_wrapper ( void* ret_val ) int detached, res; CleanupEntry cu; pthread_key_t key; + void** specifics_ptr; /* Run this thread's cleanup handlers. */ while (1) { @@ -471,6 +514,15 @@ void thread_exit_wrapper ( void* ret_val ) my_assert(res == -1); } + /* Free up my specifics space, if any. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + pthread_self(), 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */ + if (specifics_ptr != NULL) + my_free(specifics_ptr); + /* Decide on my final disposition. */ VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */, VG_USERREQ__SET_OR_GET_DETACH, @@ -515,7 +567,6 @@ static __attribute__((noreturn)) void thread_wrapper ( NewThreadInfo* info ) { - int res; int attr__detachstate; void* (*root_fn) ( void* ); void* arg; @@ -526,9 +577,7 @@ void thread_wrapper ( NewThreadInfo* info ) arg = info->arg; /* Free up the arg block that pthread_create malloced. */ - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__FREE, info, 0, 0, 0); - my_assert(res == 0); + my_free(info); /* Minimally observe the attributes supplied. */ if (attr__detachstate != PTHREAD_CREATE_DETACHED @@ -537,6 +586,15 @@ void thread_wrapper ( NewThreadInfo* info ) if (attr__detachstate == PTHREAD_CREATE_DETACHED) pthread_detach(pthread_self()); +# ifdef GLIBC_2_3 + /* Set this thread's locale to the global (default) locale. A hack + in support of glibc-2.3. This does the biz for the all new + threads; the root thread is done with a horrible hack in + init_libc_tsd_keys() below. + */ + __uselocale(LC_GLOBAL_LOCALE); +# endif + /* The root function might not return. But if it does we simply move along to thread_exit_wrapper. All other ways out for the thread (cancellation, or calling pthread_exit) lead there @@ -581,11 +639,13 @@ pthread_create (pthread_t *__restrict __thredd, ensure_valgrind("pthread_create"); + /* make sure the tsd keys, and hence locale info, are initialised + before we get into complications making new threads. */ + init_libc_tsd_keys(); + /* Allocate space for the arg block. thread_wrapper will free it. */ - VALGRIND_MAGIC_SEQUENCE(info, NULL /* default */, - VG_USERREQ__MALLOC, - sizeof(NewThreadInfo), 0, 0, 0); + info = my_malloc(sizeof(NewThreadInfo)); my_assert(info != NULL); if (__attr) @@ -626,19 +686,6 @@ void pthread_exit(void *retval) } -pthread_t pthread_self(void) -{ - int tid; - ensure_valgrind("pthread_self"); - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - if (tid < 1 || tid >= VG_N_THREADS) - barf("pthread_self: invalid ThreadId"); - return tid; -} - - int pthread_detach(pthread_t th) { int res; @@ -1219,43 +1266,140 @@ int pause ( void ) THREAD-SPECIFICs ------------------------------------------------ */ +static +int key_is_valid (pthread_key_t key) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 2 /* default */, + VG_USERREQ__PTHREAD_KEY_VALIDATE, + key, 0, 0, 0); + my_assert(res != 2); + return res; +} + + +/* Returns NULL if thread is invalid. Otherwise, if the thread + already has a specifics area, return that. Otherwise allocate it + one. */ +static +void** get_or_allocate_specifics_ptr ( pthread_t thread ) +{ + int res, i; + void** specifics_ptr; + ensure_valgrind("get_or_allocate_specifics_ptr"); + + /* Returns zero if the thread has no specific_ptr. One if thread + is invalid. Otherwise, the specific_ptr value. This is + allocated with my_malloc and so is aligned and cannot be + confused with 1 or 3. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + thread, 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + + if (specifics_ptr == (void**)1) + return NULL; /* invalid thread */ + + if (specifics_ptr != NULL) + return specifics_ptr; /* already has a specifics ptr. */ + + /* None yet ... allocate a new one. Should never fail. */ + specifics_ptr = my_malloc( VG_N_THREAD_KEYS * sizeof(void*) ); + my_assert(specifics_ptr != NULL); + + VALGRIND_MAGIC_SEQUENCE(res, -1 /* default */, + VG_USERREQ__PTHREAD_SETSPECIFIC_PTR, + specifics_ptr, 0, 0, 0); + my_assert(res == 0); + + /* POSIX sez: "Upon thread creation, the value NULL shall be + associated with all defined keys in the new thread." This + allocation is in effect a delayed allocation of the specific + data for a thread, at its first-use. Hence we initialise it + here. */ + for (i = 0; i < VG_N_THREAD_KEYS; i++) { + specifics_ptr[i] = NULL; + } + + return specifics_ptr; +} + + int __pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) { - int res; + void** specifics_ptr; + int res, i; ensure_valgrind("pthread_key_create"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + + /* This writes *key if successful. It should never fail. */ + VALGRIND_MAGIC_SEQUENCE(res, 1 /* default */, VG_USERREQ__PTHREAD_KEY_CREATE, key, destr_function, 0, 0); + my_assert(res == 0); + + /* POSIX sez: "Upon key creation, the value NULL shall be + associated with the new key in all active threads." */ + for (i = 0; i < VG_N_THREADS; i++) { + specifics_ptr = get_or_allocate_specifics_ptr(i); + /* we get NULL if i is an invalid thread. */ + if (specifics_ptr != NULL) + specifics_ptr[*key] = NULL; + } + return res; } int pthread_key_delete(pthread_key_t key) { - static int moans = N_MOANS; - if (moans-- > 0) - ignored("pthread_key_delete"); + int res; + ensure_valgrind("pthread_key_create"); + if (!key_is_valid(key)) + return EINVAL; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_KEY_DELETE, + key, 0, 0, 0); + my_assert(res == 0); return 0; } int __pthread_setspecific(pthread_key_t key, const void *pointer) { - int res; + void** specifics_ptr; ensure_valgrind("pthread_setspecific"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_SETSPECIFIC, - key, pointer, 0, 0); - return res; + + if (!key_is_valid(key)) + return EINVAL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + specifics_ptr[key] = (void*)pointer; + return 0; } void * __pthread_getspecific(pthread_key_t key) { - int res; + void** specifics_ptr; ensure_valgrind("pthread_getspecific"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_GETSPECIFIC, - key, 0 , 0, 0); - return (void*)res; + + if (!key_is_valid(key)) + return NULL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + return specifics_ptr[key]; +} + + +static +void ** __pthread_getspecific_addr(pthread_key_t key) +{ + void** specifics_ptr; + ensure_valgrind("pthread_getspecific_addr"); + + if (!key_is_valid(key)) + return NULL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + return &(specifics_ptr[key]); } @@ -1424,9 +1568,11 @@ enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, static int libc_specifics_inited = 0; static pthread_mutex_t libc_specifics_inited_mx = PTHREAD_MUTEX_INITIALIZER; + /* These are the keys we must initialise the first time. */ static pthread_key_t libc_specifics_keys[_LIBC_TSD_KEY_N]; + /* Initialise the keys, if they are not already initialised. */ static void init_libc_tsd_keys ( void ) @@ -1440,12 +1586,12 @@ void init_libc_tsd_keys ( void ) return; /* Take the lock. */ - res = pthread_mutex_lock(&libc_specifics_inited_mx); + res = __pthread_mutex_lock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: lock"); /* Now test again, to be sure there is no mistake. */ if (libc_specifics_inited != 0) { - res = pthread_mutex_unlock(&libc_specifics_inited_mx); + res = __pthread_mutex_unlock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: unlock(1)"); return; } @@ -1453,7 +1599,7 @@ void init_libc_tsd_keys ( void ) /* Actually do the initialisation. */ /* printf("INIT libc specifics\n"); */ for (i = 0; i < _LIBC_TSD_KEY_N; i++) { - res = pthread_key_create(&k, NULL); + res = __pthread_key_create(&k, NULL); if (res != 0) barf("init_libc_tsd_keys: create"); libc_specifics_keys[i] = k; } @@ -1463,12 +1609,19 @@ void init_libc_tsd_keys ( void ) # ifdef GLIBC_2_3 /* Set the initialising thread's locale to the global (default) - locale. A hack in support of glibc-2.3. */ + locale. A hack in support of glibc-2.3. This does the biz for + the root thread. For all other threads we run this in + thread_wrapper(), which does the real work of + pthread_create(). */ + /* assert that we are the root thread. I don't know if this is + really a valid assertion to make; if it breaks I'll reconsider + it. */ + my_assert(pthread_self() == 1); __uselocale(LC_GLOBAL_LOCALE); # endif /* Unlock and return. */ - res = pthread_mutex_unlock(&libc_specifics_inited_mx); + res = __pthread_mutex_unlock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: unlock"); } @@ -1482,7 +1635,7 @@ libc_internal_tsd_set ( enum __libc_tsd_key_t key, if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) barf("libc_internal_tsd_set: invalid key"); init_libc_tsd_keys(); - res = pthread_setspecific(libc_specifics_keys[key], pointer); + res = __pthread_setspecific(libc_specifics_keys[key], pointer); if (res != 0) barf("libc_internal_tsd_set: setspecific failed"); return 0; } @@ -1495,13 +1648,12 @@ libc_internal_tsd_get ( enum __libc_tsd_key_t key ) if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) barf("libc_internal_tsd_get: invalid key"); init_libc_tsd_keys(); - v = pthread_getspecific(libc_specifics_keys[key]); + v = __pthread_getspecific(libc_specifics_keys[key]); /* if (v == NULL) barf("libc_internal_tsd_set: getspecific failed"); */ return v; } - int (*__libc_internal_tsd_set) (enum __libc_tsd_key_t key, const void * pointer) = libc_internal_tsd_set; @@ -1511,6 +1663,26 @@ void* (*__libc_internal_tsd_get) = libc_internal_tsd_get; +#ifdef GLIBC_2_3 +/* This one was first spotted be me in the glibc-2.2.93 sources. */ +static void** +libc_internal_tsd_address ( enum __libc_tsd_key_t key ) +{ + void** v; + /* printf("ADDR ADDR ADDR key %d\n", key); */ + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) + barf("libc_internal_tsd_address: invalid key"); + init_libc_tsd_keys(); + v = __pthread_getspecific_addr(libc_specifics_keys[key]); + return v; +} + +void ** (*__libc_internal_tsd_address) + (enum __libc_tsd_key_t key) + = libc_internal_tsd_address; +#endif + + /* --------------------------------------------------------------------- These are here (I think) because they are deemed cancellation points by POSIX. For the moment we'll simply pass the call along diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h index 164e896610..f9a7a40ec0 100644 --- a/coregrind/vg_include.h +++ b/coregrind/vg_include.h @@ -360,13 +360,14 @@ extern Bool VG_(is_empty_arena) ( ArenaId aid ); #define VG_USERREQ__PTHREAD_COND_BROADCAST 0x3012 #define VG_USERREQ__PTHREAD_KEY_CREATE 0x3013 #define VG_USERREQ__PTHREAD_KEY_DELETE 0x3014 -#define VG_USERREQ__PTHREAD_SETSPECIFIC 0x3015 -#define VG_USERREQ__PTHREAD_GETSPECIFIC 0x3016 +#define VG_USERREQ__PTHREAD_SETSPECIFIC_PTR 0x3015 +#define VG_USERREQ__PTHREAD_GETSPECIFIC_PTR 0x3016 #define VG_USERREQ__READ_MILLISECOND_TIMER 0x3017 #define VG_USERREQ__PTHREAD_SIGMASK 0x3018 #define VG_USERREQ__SIGWAIT 0x3019 #define VG_USERREQ__PTHREAD_KILL 0x301A #define VG_USERREQ__PTHREAD_YIELD 0x301B +#define VG_USERREQ__PTHREAD_KEY_VALIDATE 0x301C #define VG_USERREQ__CLEANUP_PUSH 0x3020 #define VG_USERREQ__CLEANUP_POP 0x3021 @@ -562,8 +563,13 @@ struct _ThreadState { Int custack_used; CleanupEntry custack[VG_N_CLEANUPSTACK]; - /* thread-specific data */ - void* specifics[VG_N_THREAD_KEYS]; + /* A pointer to the thread's-specific-data. This is handled almost + entirely from vg_libpthread.c. We just provide hooks to get and + set this ptr. This is either NULL, indicating the thread has + read/written none of its specifics so far, OR points to a + void*[VG_N_THREAD_KEYS], allocated and deallocated in + vg_libpthread.c. */ + void** specifics_ptr; /* This thread's blocked-signals mask. Semantics is that for a signal to be delivered to this thread, the signal must not be diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c index 70abb179df..048f175bca 100644 --- a/coregrind/vg_libpthread.c +++ b/coregrind/vg_libpthread.c @@ -99,6 +99,9 @@ int my_do_syscall3 ( int syscallno, extern __locale_t __uselocale ( __locale_t ); #endif +static +void init_libc_tsd_keys ( void ); + /* --------------------------------------------------------------------- Helpers. We have to be pretty self-sufficient. @@ -229,6 +232,27 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn ) __FILE__, __LINE__, \ __PRETTY_FUNCTION__), 0))) +static +void my_free ( void* ptr ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__FREE, ptr, 0, 0, 0); + my_assert(res == 0); +} + + +static +void* my_malloc ( int nbytes ) +{ + void* res; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__MALLOC, nbytes, 0, 0, 0); + my_assert(res != (void*)0); + return res; +} + + /* --------------------------------------------------------------------- Pass pthread_ calls to Valgrind's request mechanism. @@ -252,6 +276,24 @@ void pthread_error ( const char* msg ) } +/* --------------------------------------------------- + Here so it can be inlined without complaint. + ------------------------------------------------ */ + +__inline__ +pthread_t pthread_self(void) +{ + int tid; + ensure_valgrind("pthread_self"); + VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + if (tid < 1 || tid >= VG_N_THREADS) + barf("pthread_self: invalid ThreadId"); + return tid; +} + + /* --------------------------------------------------- THREAD ATTRIBUTES ------------------------------------------------ */ @@ -443,6 +485,7 @@ void thread_exit_wrapper ( void* ret_val ) int detached, res; CleanupEntry cu; pthread_key_t key; + void** specifics_ptr; /* Run this thread's cleanup handlers. */ while (1) { @@ -471,6 +514,15 @@ void thread_exit_wrapper ( void* ret_val ) my_assert(res == -1); } + /* Free up my specifics space, if any. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + pthread_self(), 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */ + if (specifics_ptr != NULL) + my_free(specifics_ptr); + /* Decide on my final disposition. */ VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */, VG_USERREQ__SET_OR_GET_DETACH, @@ -515,7 +567,6 @@ static __attribute__((noreturn)) void thread_wrapper ( NewThreadInfo* info ) { - int res; int attr__detachstate; void* (*root_fn) ( void* ); void* arg; @@ -526,9 +577,7 @@ void thread_wrapper ( NewThreadInfo* info ) arg = info->arg; /* Free up the arg block that pthread_create malloced. */ - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__FREE, info, 0, 0, 0); - my_assert(res == 0); + my_free(info); /* Minimally observe the attributes supplied. */ if (attr__detachstate != PTHREAD_CREATE_DETACHED @@ -537,6 +586,15 @@ void thread_wrapper ( NewThreadInfo* info ) if (attr__detachstate == PTHREAD_CREATE_DETACHED) pthread_detach(pthread_self()); +# ifdef GLIBC_2_3 + /* Set this thread's locale to the global (default) locale. A hack + in support of glibc-2.3. This does the biz for the all new + threads; the root thread is done with a horrible hack in + init_libc_tsd_keys() below. + */ + __uselocale(LC_GLOBAL_LOCALE); +# endif + /* The root function might not return. But if it does we simply move along to thread_exit_wrapper. All other ways out for the thread (cancellation, or calling pthread_exit) lead there @@ -581,11 +639,13 @@ pthread_create (pthread_t *__restrict __thredd, ensure_valgrind("pthread_create"); + /* make sure the tsd keys, and hence locale info, are initialised + before we get into complications making new threads. */ + init_libc_tsd_keys(); + /* Allocate space for the arg block. thread_wrapper will free it. */ - VALGRIND_MAGIC_SEQUENCE(info, NULL /* default */, - VG_USERREQ__MALLOC, - sizeof(NewThreadInfo), 0, 0, 0); + info = my_malloc(sizeof(NewThreadInfo)); my_assert(info != NULL); if (__attr) @@ -626,19 +686,6 @@ void pthread_exit(void *retval) } -pthread_t pthread_self(void) -{ - int tid; - ensure_valgrind("pthread_self"); - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - if (tid < 1 || tid >= VG_N_THREADS) - barf("pthread_self: invalid ThreadId"); - return tid; -} - - int pthread_detach(pthread_t th) { int res; @@ -1219,43 +1266,140 @@ int pause ( void ) THREAD-SPECIFICs ------------------------------------------------ */ +static +int key_is_valid (pthread_key_t key) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 2 /* default */, + VG_USERREQ__PTHREAD_KEY_VALIDATE, + key, 0, 0, 0); + my_assert(res != 2); + return res; +} + + +/* Returns NULL if thread is invalid. Otherwise, if the thread + already has a specifics area, return that. Otherwise allocate it + one. */ +static +void** get_or_allocate_specifics_ptr ( pthread_t thread ) +{ + int res, i; + void** specifics_ptr; + ensure_valgrind("get_or_allocate_specifics_ptr"); + + /* Returns zero if the thread has no specific_ptr. One if thread + is invalid. Otherwise, the specific_ptr value. This is + allocated with my_malloc and so is aligned and cannot be + confused with 1 or 3. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + thread, 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + + if (specifics_ptr == (void**)1) + return NULL; /* invalid thread */ + + if (specifics_ptr != NULL) + return specifics_ptr; /* already has a specifics ptr. */ + + /* None yet ... allocate a new one. Should never fail. */ + specifics_ptr = my_malloc( VG_N_THREAD_KEYS * sizeof(void*) ); + my_assert(specifics_ptr != NULL); + + VALGRIND_MAGIC_SEQUENCE(res, -1 /* default */, + VG_USERREQ__PTHREAD_SETSPECIFIC_PTR, + specifics_ptr, 0, 0, 0); + my_assert(res == 0); + + /* POSIX sez: "Upon thread creation, the value NULL shall be + associated with all defined keys in the new thread." This + allocation is in effect a delayed allocation of the specific + data for a thread, at its first-use. Hence we initialise it + here. */ + for (i = 0; i < VG_N_THREAD_KEYS; i++) { + specifics_ptr[i] = NULL; + } + + return specifics_ptr; +} + + int __pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) { - int res; + void** specifics_ptr; + int res, i; ensure_valgrind("pthread_key_create"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + + /* This writes *key if successful. It should never fail. */ + VALGRIND_MAGIC_SEQUENCE(res, 1 /* default */, VG_USERREQ__PTHREAD_KEY_CREATE, key, destr_function, 0, 0); + my_assert(res == 0); + + /* POSIX sez: "Upon key creation, the value NULL shall be + associated with the new key in all active threads." */ + for (i = 0; i < VG_N_THREADS; i++) { + specifics_ptr = get_or_allocate_specifics_ptr(i); + /* we get NULL if i is an invalid thread. */ + if (specifics_ptr != NULL) + specifics_ptr[*key] = NULL; + } + return res; } int pthread_key_delete(pthread_key_t key) { - static int moans = N_MOANS; - if (moans-- > 0) - ignored("pthread_key_delete"); + int res; + ensure_valgrind("pthread_key_create"); + if (!key_is_valid(key)) + return EINVAL; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_KEY_DELETE, + key, 0, 0, 0); + my_assert(res == 0); return 0; } int __pthread_setspecific(pthread_key_t key, const void *pointer) { - int res; + void** specifics_ptr; ensure_valgrind("pthread_setspecific"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_SETSPECIFIC, - key, pointer, 0, 0); - return res; + + if (!key_is_valid(key)) + return EINVAL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + specifics_ptr[key] = (void*)pointer; + return 0; } void * __pthread_getspecific(pthread_key_t key) { - int res; + void** specifics_ptr; ensure_valgrind("pthread_getspecific"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_GETSPECIFIC, - key, 0 , 0, 0); - return (void*)res; + + if (!key_is_valid(key)) + return NULL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + return specifics_ptr[key]; +} + + +static +void ** __pthread_getspecific_addr(pthread_key_t key) +{ + void** specifics_ptr; + ensure_valgrind("pthread_getspecific_addr"); + + if (!key_is_valid(key)) + return NULL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + return &(specifics_ptr[key]); } @@ -1424,9 +1568,11 @@ enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, static int libc_specifics_inited = 0; static pthread_mutex_t libc_specifics_inited_mx = PTHREAD_MUTEX_INITIALIZER; + /* These are the keys we must initialise the first time. */ static pthread_key_t libc_specifics_keys[_LIBC_TSD_KEY_N]; + /* Initialise the keys, if they are not already initialised. */ static void init_libc_tsd_keys ( void ) @@ -1440,12 +1586,12 @@ void init_libc_tsd_keys ( void ) return; /* Take the lock. */ - res = pthread_mutex_lock(&libc_specifics_inited_mx); + res = __pthread_mutex_lock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: lock"); /* Now test again, to be sure there is no mistake. */ if (libc_specifics_inited != 0) { - res = pthread_mutex_unlock(&libc_specifics_inited_mx); + res = __pthread_mutex_unlock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: unlock(1)"); return; } @@ -1453,7 +1599,7 @@ void init_libc_tsd_keys ( void ) /* Actually do the initialisation. */ /* printf("INIT libc specifics\n"); */ for (i = 0; i < _LIBC_TSD_KEY_N; i++) { - res = pthread_key_create(&k, NULL); + res = __pthread_key_create(&k, NULL); if (res != 0) barf("init_libc_tsd_keys: create"); libc_specifics_keys[i] = k; } @@ -1463,12 +1609,19 @@ void init_libc_tsd_keys ( void ) # ifdef GLIBC_2_3 /* Set the initialising thread's locale to the global (default) - locale. A hack in support of glibc-2.3. */ + locale. A hack in support of glibc-2.3. This does the biz for + the root thread. For all other threads we run this in + thread_wrapper(), which does the real work of + pthread_create(). */ + /* assert that we are the root thread. I don't know if this is + really a valid assertion to make; if it breaks I'll reconsider + it. */ + my_assert(pthread_self() == 1); __uselocale(LC_GLOBAL_LOCALE); # endif /* Unlock and return. */ - res = pthread_mutex_unlock(&libc_specifics_inited_mx); + res = __pthread_mutex_unlock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: unlock"); } @@ -1482,7 +1635,7 @@ libc_internal_tsd_set ( enum __libc_tsd_key_t key, if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) barf("libc_internal_tsd_set: invalid key"); init_libc_tsd_keys(); - res = pthread_setspecific(libc_specifics_keys[key], pointer); + res = __pthread_setspecific(libc_specifics_keys[key], pointer); if (res != 0) barf("libc_internal_tsd_set: setspecific failed"); return 0; } @@ -1495,13 +1648,12 @@ libc_internal_tsd_get ( enum __libc_tsd_key_t key ) if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) barf("libc_internal_tsd_get: invalid key"); init_libc_tsd_keys(); - v = pthread_getspecific(libc_specifics_keys[key]); + v = __pthread_getspecific(libc_specifics_keys[key]); /* if (v == NULL) barf("libc_internal_tsd_set: getspecific failed"); */ return v; } - int (*__libc_internal_tsd_set) (enum __libc_tsd_key_t key, const void * pointer) = libc_internal_tsd_set; @@ -1511,6 +1663,26 @@ void* (*__libc_internal_tsd_get) = libc_internal_tsd_get; +#ifdef GLIBC_2_3 +/* This one was first spotted be me in the glibc-2.2.93 sources. */ +static void** +libc_internal_tsd_address ( enum __libc_tsd_key_t key ) +{ + void** v; + /* printf("ADDR ADDR ADDR key %d\n", key); */ + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) + barf("libc_internal_tsd_address: invalid key"); + init_libc_tsd_keys(); + v = __pthread_getspecific_addr(libc_specifics_keys[key]); + return v; +} + +void ** (*__libc_internal_tsd_address) + (enum __libc_tsd_key_t key) + = libc_internal_tsd_address; +#endif + + /* --------------------------------------------------------------------- These are here (I think) because they are deemed cancellation points by POSIX. For the moment we'll simply pass the call along diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c index d257f9f357..acdb9f3750 100644 --- a/coregrind/vg_scheduler.c +++ b/coregrind/vg_scheduler.c @@ -610,7 +610,6 @@ void increment_epoch ( void ) static void mostly_clear_thread_record ( ThreadId tid ) { - Int j; vg_assert(tid >= 0 && tid < VG_N_THREADS); VG_(threads)[tid].ldt = NULL; VG_(threads)[tid].tid = tid; @@ -629,8 +628,7 @@ void mostly_clear_thread_record ( ThreadId tid ) VG_(threads)[tid].n_signals_returned = 0; VG_(ksigemptyset)(&VG_(threads)[tid].sig_mask); VG_(ksigemptyset)(&VG_(threads)[tid].sigs_waited_for); - for (j = 0; j < VG_N_THREAD_KEYS; j++) - VG_(threads)[tid].specifics[j] = NULL; + VG_(threads)[tid].specifics_ptr = NULL; } @@ -2799,6 +2797,32 @@ Bool is_valid_key ( ThreadKey k ) return True; } + +/* Return in %EDX a value of 1 if the key is valid, else 0. */ +static +void do_pthread_key_validate ( ThreadId tid, + pthread_key_t key ) +{ + Char msg_buf[100]; + + if (VG_(clo_trace_pthread_level) >= 1) { + VG_(sprintf)(msg_buf, "pthread_key_validate key %p", + key ); + print_pthread_event(tid, msg_buf); + } + + vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey)); + vg_assert(VG_(is_valid_tid)(tid) + && VG_(threads)[tid].status == VgTs_Runnable); + + if (is_valid_key((ThreadKey)key)) { + SET_EDX(tid, 1); + } else { + SET_EDX(tid, 0); + } +} + + static void do_pthread_key_create ( ThreadId tid, pthread_key_t* key, @@ -2864,66 +2888,54 @@ void do_pthread_key_delete ( ThreadId tid, pthread_key_t key ) } vg_thread_keys[key].inuse = False; - - /* Optional. We're not required to do this, although it shouldn't - make any difference to programs which use the key/specifics - functions correctly. */ -# if 1 - for (tid = 1; tid < VG_N_THREADS; tid++) { - if (VG_(threads)[tid].status != VgTs_Empty) - VG_(threads)[tid].specifics[key] = NULL; - } -# endif + SET_EDX(tid, 0); } +/* Get the .specific_ptr for a thread. Return 1 if the thread-slot + isn't in use, so that client-space can scan all thread slots. 1 + cannot be confused with NULL or a legitimately-aligned specific_ptr + value. */ static -void do_pthread_getspecific ( ThreadId tid, pthread_key_t key ) +void do_pthread_getspecific_ptr ( ThreadId tid ) { - Char msg_buf[100]; + void** specifics_ptr; + Char msg_buf[100]; + if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "pthread_getspecific key %d", - key ); + VG_(sprintf)(msg_buf, "pthread_getspecific_ptr" ); print_pthread_event(tid, msg_buf); } - vg_assert(VG_(is_valid_tid)(tid) - && VG_(threads)[tid].status == VgTs_Runnable); + vg_assert(VG_(is_valid_or_empty_tid)(tid)); - if (!is_valid_key(key)) { - VG_(record_pthread_error)( tid, - "pthread_getspecific: key is invalid"); - SET_EDX(tid, (UInt)NULL); + if (VG_(threads)[tid].status == VgTs_Empty) { + SET_EDX(tid, 1); return; } - SET_EDX(tid, (UInt)VG_(threads)[tid].specifics[key]); + specifics_ptr = VG_(threads)[tid].specifics_ptr; + vg_assert(specifics_ptr == NULL + || IS_ALIGNED4_ADDR(specifics_ptr)); + + SET_EDX(tid, (UInt)specifics_ptr); } static -void do_pthread_setspecific ( ThreadId tid, - pthread_key_t key, - void *pointer ) +void do_pthread_setspecific_ptr ( ThreadId tid, void** ptr ) { Char msg_buf[100]; if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "pthread_setspecific key %d, ptr %p", - key, pointer ); + VG_(sprintf)(msg_buf, "pthread_setspecific_ptr ptr %p", + ptr ); print_pthread_event(tid, msg_buf); } vg_assert(VG_(is_valid_tid)(tid) && VG_(threads)[tid].status == VgTs_Runnable); - if (!is_valid_key(key)) { - VG_(record_pthread_error)( tid, - "pthread_setspecific: key is invalid"); - SET_EDX(tid, EINVAL); - return; - } - - VG_(threads)[tid].specifics[key] = pointer; + VG_(threads)[tid].specifics_ptr = ptr; SET_EDX(tid, 0); } @@ -2951,10 +2963,20 @@ void do__get_key_destr_and_spec ( ThreadId tid, return; } VG_TRACK( pre_mem_write, Vg_CorePThread, & VG_(threads)[tid], - "get_key_destr_and_spec", (Addr)cu, + "get_key_destr_and_spec: cu", (Addr)cu, sizeof(CleanupEntry) ); + cu->fn = vg_thread_keys[key].destructor; - cu->arg = VG_(threads)[tid].specifics[key]; + if (VG_(threads)[tid].specifics_ptr == NULL) { + cu->arg = NULL; + } else { + VG_TRACK( pre_mem_read, Vg_CorePThread, & VG_(threads)[tid], + "get_key_destr_and_spec: key", + (Addr)(&VG_(threads)[tid].specifics_ptr[key]), + sizeof(void*) ); + cu->arg = VG_(threads)[tid].specifics_ptr[key]; + } + VG_TRACK( post_mem_write, (Addr)cu, sizeof(CleanupEntry) ); SET_EDX(tid, 0); } @@ -3266,8 +3288,8 @@ void do_client_request ( ThreadId tid ) do_pthread_mutex_unlock( tid, (void *)(arg[1]) ); break; - case VG_USERREQ__PTHREAD_GETSPECIFIC: - do_pthread_getspecific ( tid, (UInt)(arg[1]) ); + case VG_USERREQ__PTHREAD_GETSPECIFIC_PTR: + do_pthread_getspecific_ptr ( tid ); break; case VG_USERREQ__SET_CANCELTYPE: @@ -3322,6 +3344,11 @@ void do_client_request ( ThreadId tid ) (pthread_cond_t *)(arg[1]) ); break; + case VG_USERREQ__PTHREAD_KEY_VALIDATE: + do_pthread_key_validate ( tid, + (pthread_key_t)(arg[1]) ); + break; + case VG_USERREQ__PTHREAD_KEY_CREATE: do_pthread_key_create ( tid, (pthread_key_t*)(arg[1]), @@ -3333,10 +3360,9 @@ void do_client_request ( ThreadId tid ) (pthread_key_t)(arg[1]) ); break; - case VG_USERREQ__PTHREAD_SETSPECIFIC: - do_pthread_setspecific ( tid, - (pthread_key_t)(arg[1]), - (void*)(arg[2]) ); + case VG_USERREQ__PTHREAD_SETSPECIFIC_PTR: + do_pthread_setspecific_ptr ( tid, + (void**)(arg[1]) ); break; case VG_USERREQ__PTHREAD_SIGMASK: