From: Olivier Hainque Date: Tue, 12 Dec 2006 11:05:54 +0000 (+0000) Subject: vxlib.c: Extract tls related routines into ... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=c0da9742e547dc0c83a1056062fe1deb8177971b;p=gcc.git vxlib.c: Extract tls related routines into ... * config/vxlib.c: Extract tls related routines into ... * config/vxlib-tls.c: New file. * config/t-vxworks: Add vxlib-tls.c to LIB2FUNCS_EXTRA. From-SVN: r119771 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4c150cc4c34..6c7c1d0e3b1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2006-12-12 Olivier Hainque + + * config/vxlib.c: Extract tls related routines into ... + * config/vxlib-tls.c: New file. + * config/t-vxworks: Add vxlib-tls.c to LIB2FUNCS_EXTRA. + 2006-12-12 Sa Liu Ben Elliston diff --git a/gcc/config/t-vxworks b/gcc/config/t-vxworks index 677b3b45443..1eac999d870 100644 --- a/gcc/config/t-vxworks +++ b/gcc/config/t-vxworks @@ -11,8 +11,8 @@ TARGET_LIBGCC2_CFLAGS = # Don't build libgcc.a with debug info LIBGCC2_DEBUG_CFLAGS = -# Extra libgcc2 module used by gthr-vxworks.h functions -LIB2FUNCS_EXTRA = $(srcdir)/config/vxlib.c +# Extra libgcc2 modules used by gthr-vxworks.h functions +LIB2FUNCS_EXTRA = $(srcdir)/config/vxlib.c $(srcdir)/config/vxlib-tls.c # Some runtime modules need these. Can't set extra_headers in config.gcc # because the paths are always made absolute to the cpu config dir. diff --git a/gcc/config/vxlib-tls.c b/gcc/config/vxlib-tls.c new file mode 100644 index 00000000000..eba8f5cf547 --- /dev/null +++ b/gcc/config/vxlib-tls.c @@ -0,0 +1,351 @@ +/* Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Contributed by Zack Weinberg + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + +/* Threads compatibility routines for libgcc2 for VxWorks. + These are out-of-line routines called from gthr-vxworks.h. + + This file provides the TLS related support routines, calling specific + VxWorks kernel entry points for this purpose. The base VxWorks 5.x kernels + don't feature these entry points, and we provide gthr_supp_vxw_5x.c as an + option to fill this gap. Asking users to rebuild a kernel is not to be + taken lightly, still, so we have isolated these routines from the rest of + vxlib to ensure that the kernel dependencies are only dragged when really + necessary. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "gthr.h" + +#if defined(__GTHREADS) +#include +#ifndef __RTP__ +#include +#endif +#include +#ifndef __RTP__ +#include +#else +# include +#endif + +/* Thread-local storage. + + We reserve a field in the TCB to point to a dynamically allocated + array which is used to store TLS values. A TLS key is simply an + offset in this array. The exact location of the TCB field is not + known to this code nor to vxlib.c -- all access to it indirects + through the routines __gthread_get_tls_data and + __gthread_set_tls_data, which are provided by the VxWorks kernel. + + There is also a global array which records which keys are valid and + which have destructors. + + A task delete hook is installed to execute key destructors. The + routines __gthread_enter_tls_dtor_context and + __gthread_leave_tls_dtor_context, which are also provided by the + kernel, ensure that it is safe to call free() on memory allocated + by the task being deleted. (This is a no-op on VxWorks 5, but + a major undertaking on AE.) + + The task delete hook is only installed when at least one thread + has TLS data. This is a necessary precaution, to allow this module + to be unloaded - a module with a hook can not be removed. + + Since this interface is used to allocate only a small number of + keys, the table size is small and static, which simplifies the + code quite a bit. Revisit this if and when it becomes necessary. */ + +#define MAX_KEYS 4 + +/* This is the structure pointed to by the pointer returned + by __gthread_get_tls_data. */ +struct tls_data +{ + int *owner; + void *values[MAX_KEYS]; + unsigned int generation[MAX_KEYS]; +}; + +/* To make sure we only delete TLS data associated with this object, + include a pointer to a local variable in the TLS data object. */ +static int self_owner; + +/* The number of threads for this module which have active TLS data. + This is protected by tls_lock. */ +static int active_tls_threads; + +/* kernel provided routines */ +extern void *__gthread_get_tls_data (void); +extern void __gthread_set_tls_data (void *data); + +extern void __gthread_enter_tls_dtor_context (void); +extern void __gthread_leave_tls_dtor_context (void); + + +/* This is a global structure which records all of the active keys. + + A key is potentially valid (i.e. has been handed out by + __gthread_key_create) iff its generation count in this structure is + even. In that case, the matching entry in the dtors array is a + routine to be called when a thread terminates with a valid, + non-NULL specific value for that key. + + A key is actually valid in a thread T iff the generation count + stored in this structure is equal to the generation count stored in + T's specific-value structure. */ + +typedef void (*tls_dtor) (void *); + +struct tls_keys +{ + tls_dtor dtor[MAX_KEYS]; + unsigned int generation[MAX_KEYS]; +}; + +#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1) + +/* Note: if MAX_KEYS is increased, this initializer must be updated + to match. All the generation counts begin at 1, which means no + key is valid. */ +static struct tls_keys tls_keys = +{ + { 0, 0, 0, 0 }, + { 1, 1, 1, 1 } +}; + +/* This lock protects the tls_keys structure. */ +static __gthread_mutex_t tls_lock; + +static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT; + +/* Internal routines. */ + +/* The task TCB has just been deleted. Call the destructor + function for each TLS key that has both a destructor and + a non-NULL specific value in this thread. + + This routine does not need to take tls_lock; the generation + count protects us from calling a stale destructor. It does + need to read tls_keys.dtor[key] atomically. */ + +static void +tls_delete_hook (void *tcb ATTRIBUTE_UNUSED) +{ + struct tls_data *data = __gthread_get_tls_data (); + __gthread_key_t key; + + if (data && data->owner == &self_owner) + { + __gthread_enter_tls_dtor_context (); + for (key = 0; key < MAX_KEYS; key++) + { + if (data->generation[key] == tls_keys.generation[key]) + { + tls_dtor dtor = tls_keys.dtor[key]; + + if (dtor) + dtor (data->values[key]); + } + } + free (data); + + /* We can't handle an error here, so just leave the thread + marked as loaded if one occurs. */ + if (__gthread_mutex_lock (&tls_lock) != ERROR) + { + active_tls_threads--; + if (active_tls_threads == 0) + taskDeleteHookDelete ((FUNCPTR)tls_delete_hook); + __gthread_mutex_unlock (&tls_lock); + } + + __gthread_set_tls_data (0); + __gthread_leave_tls_dtor_context (); + } +} + +/* Initialize global data used by the TLS system. */ +static void +tls_init (void) +{ + __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock); +} + +static void tls_destructor (void) __attribute__ ((destructor)); +static void +tls_destructor (void) +{ +#ifdef __RTP__ + /* All threads but this one should have exited by now. */ + tls_delete_hook (NULL); +#else + /* Unregister the hook forcibly. The counter of active threads may + be incorrect, because constructors (like the C++ library's) and + destructors (like this one) run in the context of the shell rather + than in a task spawned from this module. */ + taskDeleteHookDelete ((FUNCPTR)tls_delete_hook); +#endif + + if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR) + semDelete (tls_lock); +} + +/* External interface */ + +/* Store in KEYP a value which can be passed to __gthread_setspecific/ + __gthread_getspecific to store and retrieve a value which is + specific to each calling thread. If DTOR is not NULL, it will be + called when a thread terminates with a non-NULL specific value for + this key, with the value as its sole argument. */ + +int +__gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor) +{ + __gthread_key_t key; + + __gthread_once (&tls_init_guard, tls_init); + + if (__gthread_mutex_lock (&tls_lock) == ERROR) + return errno; + + for (key = 0; key < MAX_KEYS; key++) + if (!KEY_VALID_P (key)) + goto found_slot; + + /* no room */ + __gthread_mutex_unlock (&tls_lock); + return EAGAIN; + + found_slot: + tls_keys.generation[key]++; /* making it even */ + tls_keys.dtor[key] = dtor; + *keyp = key; + __gthread_mutex_unlock (&tls_lock); + return 0; +} + +/* Invalidate KEY; it can no longer be used as an argument to + setspecific/getspecific. Note that this does NOT call destructor + functions for any live values for this key. */ +int +__gthread_key_delete (__gthread_key_t key) +{ + if (key >= MAX_KEYS) + return EINVAL; + + __gthread_once (&tls_init_guard, tls_init); + + if (__gthread_mutex_lock (&tls_lock) == ERROR) + return errno; + + if (!KEY_VALID_P (key)) + { + __gthread_mutex_unlock (&tls_lock); + return EINVAL; + } + + tls_keys.generation[key]++; /* making it odd */ + tls_keys.dtor[key] = 0; + + __gthread_mutex_unlock (&tls_lock); + return 0; +} + +/* Retrieve the thread-specific value for KEY. If it has never been + set in this thread, or KEY is invalid, returns NULL. + + It does not matter if this function races with key_create or + key_delete; the worst that can happen is you get a value other than + the one that a serialized implementation would have provided. */ + +void * +__gthread_getspecific (__gthread_key_t key) +{ + struct tls_data *data; + + if (key >= MAX_KEYS) + return 0; + + data = __gthread_get_tls_data (); + + if (!data) + return 0; + + if (data->generation[key] != tls_keys.generation[key]) + return 0; + + return data->values[key]; +} + +/* Set the thread-specific value for KEY. If KEY is invalid, or + memory allocation fails, returns -1, otherwise 0. + + The generation count protects this function against races with + key_create/key_delete; the worst thing that can happen is that a + value is successfully stored into a dead generation (and then + immediately becomes invalid). However, we do have to make sure + to read tls_keys.generation[key] atomically. */ + +int +__gthread_setspecific (__gthread_key_t key, void *value) +{ + struct tls_data *data; + unsigned int generation; + + if (key >= MAX_KEYS) + return EINVAL; + + data = __gthread_get_tls_data (); + if (!data) + { + if (__gthread_mutex_lock (&tls_lock) == ERROR) + return ENOMEM; + if (active_tls_threads == 0) + taskDeleteHookAdd ((FUNCPTR)tls_delete_hook); + active_tls_threads++; + __gthread_mutex_unlock (&tls_lock); + + data = malloc (sizeof (struct tls_data)); + if (!data) + return ENOMEM; + + memset (data, 0, sizeof (struct tls_data)); + data->owner = &self_owner; + __gthread_set_tls_data (data); + } + + generation = tls_keys.generation[key]; + + if (generation & 1) + return EINVAL; + + data->generation[key] = generation; + data->values[key] = value; + + return 0; +} +#endif /* __GTHREADS */ diff --git a/gcc/config/vxlib.c b/gcc/config/vxlib.c index 8625981a315..2c6a5c08672 100644 --- a/gcc/config/vxlib.c +++ b/gcc/config/vxlib.c @@ -87,300 +87,4 @@ __gthread_once (__gthread_once_t *guard, void (*func)(void)) return 0; } -/* Thread-local storage. - - We reserve a field in the TCB to point to a dynamically allocated - array which is used to store TLS values. A TLS key is simply an - offset in this array. The exact location of the TCB field is not - known to this code nor to vxlib.c -- all access to it indirects - through the routines __gthread_get_tls_data and - __gthread_set_tls_data, which are provided by the VxWorks kernel. - - There is also a global array which records which keys are valid and - which have destructors. - - A task delete hook is installed to execute key destructors. The - routines __gthread_enter_tls_dtor_context and - __gthread_leave_tls_dtor_context, which are also provided by the - kernel, ensure that it is safe to call free() on memory allocated - by the task being deleted. (This is a no-op on VxWorks 5, but - a major undertaking on AE.) - - The task delete hook is only installed when at least one thread - has TLS data. This is a necessary precaution, to allow this module - to be unloaded - a module with a hook can not be removed. - - Since this interface is used to allocate only a small number of - keys, the table size is small and static, which simplifies the - code quite a bit. Revisit this if and when it becomes necessary. */ - -#define MAX_KEYS 4 - -/* This is the structure pointed to by the pointer returned - by __gthread_get_tls_data. */ -struct tls_data -{ - int *owner; - void *values[MAX_KEYS]; - unsigned int generation[MAX_KEYS]; -}; - -/* To make sure we only delete TLS data associated with this object, - include a pointer to a local variable in the TLS data object. */ -static int self_owner; - -/* The number of threads for this module which have active TLS data. - This is protected by tls_lock. */ -static int active_tls_threads; - -/* kernel provided routines */ -extern void *__gthread_get_tls_data (void); -extern void __gthread_set_tls_data (void *data); - -extern void __gthread_enter_tls_dtor_context (void); -extern void __gthread_leave_tls_dtor_context (void); - - -/* This is a global structure which records all of the active keys. - - A key is potentially valid (i.e. has been handed out by - __gthread_key_create) iff its generation count in this structure is - even. In that case, the matching entry in the dtors array is a - routine to be called when a thread terminates with a valid, - non-NULL specific value for that key. - - A key is actually valid in a thread T iff the generation count - stored in this structure is equal to the generation count stored in - T's specific-value structure. */ - -typedef void (*tls_dtor) (void *); - -struct tls_keys -{ - tls_dtor dtor[MAX_KEYS]; - unsigned int generation[MAX_KEYS]; -}; - -#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1) - -/* Note: if MAX_KEYS is increased, this initializer must be updated - to match. All the generation counts begin at 1, which means no - key is valid. */ -static struct tls_keys tls_keys = -{ - { 0, 0, 0, 0 }, - { 1, 1, 1, 1 } -}; - -/* This lock protects the tls_keys structure. */ -static __gthread_mutex_t tls_lock; - -static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT; - -/* Internal routines. */ - -/* The task TCB has just been deleted. Call the destructor - function for each TLS key that has both a destructor and - a non-NULL specific value in this thread. - - This routine does not need to take tls_lock; the generation - count protects us from calling a stale destructor. It does - need to read tls_keys.dtor[key] atomically. */ - -static void -tls_delete_hook (void *tcb ATTRIBUTE_UNUSED) -{ - struct tls_data *data = __gthread_get_tls_data (); - __gthread_key_t key; - - if (data && data->owner == &self_owner) - { - __gthread_enter_tls_dtor_context (); - for (key = 0; key < MAX_KEYS; key++) - { - if (data->generation[key] == tls_keys.generation[key]) - { - tls_dtor dtor = tls_keys.dtor[key]; - - if (dtor) - dtor (data->values[key]); - } - } - free (data); - - /* We can't handle an error here, so just leave the thread - marked as loaded if one occurs. */ - if (__gthread_mutex_lock (&tls_lock) != ERROR) - { - active_tls_threads--; - if (active_tls_threads == 0) - taskDeleteHookDelete ((FUNCPTR)tls_delete_hook); - __gthread_mutex_unlock (&tls_lock); - } - - __gthread_set_tls_data (0); - __gthread_leave_tls_dtor_context (); - } -} - -/* Initialize global data used by the TLS system. */ -static void -tls_init (void) -{ - __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock); -} - -static void tls_destructor (void) __attribute__ ((destructor)); -static void -tls_destructor (void) -{ -#ifdef __RTP__ - /* All threads but this one should have exited by now. */ - tls_delete_hook (NULL); -#else - /* Unregister the hook forcibly. The counter of active threads may - be incorrect, because constructors (like the C++ library's) and - destructors (like this one) run in the context of the shell rather - than in a task spawned from this module. */ - taskDeleteHookDelete ((FUNCPTR)tls_delete_hook); -#endif - - if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR) - semDelete (tls_lock); -} - -/* External interface */ - -/* Store in KEYP a value which can be passed to __gthread_setspecific/ - __gthread_getspecific to store and retrieve a value which is - specific to each calling thread. If DTOR is not NULL, it will be - called when a thread terminates with a non-NULL specific value for - this key, with the value as its sole argument. */ - -int -__gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor) -{ - __gthread_key_t key; - - __gthread_once (&tls_init_guard, tls_init); - - if (__gthread_mutex_lock (&tls_lock) == ERROR) - return errno; - - for (key = 0; key < MAX_KEYS; key++) - if (!KEY_VALID_P (key)) - goto found_slot; - - /* no room */ - __gthread_mutex_unlock (&tls_lock); - return EAGAIN; - - found_slot: - tls_keys.generation[key]++; /* making it even */ - tls_keys.dtor[key] = dtor; - *keyp = key; - __gthread_mutex_unlock (&tls_lock); - return 0; -} - -/* Invalidate KEY; it can no longer be used as an argument to - setspecific/getspecific. Note that this does NOT call destructor - functions for any live values for this key. */ -int -__gthread_key_delete (__gthread_key_t key) -{ - if (key >= MAX_KEYS) - return EINVAL; - - __gthread_once (&tls_init_guard, tls_init); - - if (__gthread_mutex_lock (&tls_lock) == ERROR) - return errno; - - if (!KEY_VALID_P (key)) - { - __gthread_mutex_unlock (&tls_lock); - return EINVAL; - } - - tls_keys.generation[key]++; /* making it odd */ - tls_keys.dtor[key] = 0; - - __gthread_mutex_unlock (&tls_lock); - return 0; -} - -/* Retrieve the thread-specific value for KEY. If it has never been - set in this thread, or KEY is invalid, returns NULL. - - It does not matter if this function races with key_create or - key_delete; the worst that can happen is you get a value other than - the one that a serialized implementation would have provided. */ - -void * -__gthread_getspecific (__gthread_key_t key) -{ - struct tls_data *data; - - if (key >= MAX_KEYS) - return 0; - - data = __gthread_get_tls_data (); - - if (!data) - return 0; - - if (data->generation[key] != tls_keys.generation[key]) - return 0; - - return data->values[key]; -} - -/* Set the thread-specific value for KEY. If KEY is invalid, or - memory allocation fails, returns -1, otherwise 0. - - The generation count protects this function against races with - key_create/key_delete; the worst thing that can happen is that a - value is successfully stored into a dead generation (and then - immediately becomes invalid). However, we do have to make sure - to read tls_keys.generation[key] atomically. */ - -int -__gthread_setspecific (__gthread_key_t key, void *value) -{ - struct tls_data *data; - unsigned int generation; - - if (key >= MAX_KEYS) - return EINVAL; - - data = __gthread_get_tls_data (); - if (!data) - { - if (__gthread_mutex_lock (&tls_lock) == ERROR) - return ENOMEM; - if (active_tls_threads == 0) - taskDeleteHookAdd ((FUNCPTR)tls_delete_hook); - active_tls_threads++; - __gthread_mutex_unlock (&tls_lock); - - data = malloc (sizeof (struct tls_data)); - if (!data) - return ENOMEM; - - memset (data, 0, sizeof (struct tls_data)); - data->owner = &self_owner; - __gthread_set_tls_data (data); - } - - generation = tls_keys.generation[key]; - - if (generation & 1) - return EINVAL; - - data->generation[key] = generation; - data->values[key] = value; - - return 0; -} #endif /* __GTHREADS */