mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			371 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Copyright (C) 2002-2016 Free Software Foundation, Inc.
 | |
|    Contributed by Zack Weinberg <zack@codesourcery.com>
 | |
| 
 | |
| 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 3, 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.
 | |
| 
 | |
| Under Section 7 of GPL version 3, you are granted additional
 | |
| permissions described in the GCC Runtime Library Exception, version
 | |
| 3.1, as published by the Free Software Foundation.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License and
 | |
| a copy of the GCC Runtime Library Exception along with this program;
 | |
| see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 | |
| <http://www.gnu.org/licenses/>.  */
 | |
| 
 | |
| /* 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 <vxWorks.h>
 | |
| #ifndef __RTP__
 | |
| #include <vxLib.h>
 | |
| #endif
 | |
| #include <taskLib.h>
 | |
| #ifndef __RTP__
 | |
| #include <taskHookLib.h>
 | |
| #else
 | |
| # include <errno.h>
 | |
| #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;
 | |
| 
 | |
| /* Flag to check whether the delete hook is installed.  Once installed
 | |
|    it is only removed when unloading this module.  */
 | |
| static volatile int delete_hook_installed;
 | |
| 
 | |
| /* 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);
 | |
| 
 | |
| #ifndef __RTP__
 | |
| 
 | |
| extern void *__gthread_get_tsd_data (WIND_TCB *tcb);
 | |
| extern void __gthread_set_tsd_data (WIND_TCB *tcb, void *data);
 | |
| extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb);
 | |
| extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb);
 | |
| 
 | |
| #endif /* __RTP__ */
 | |
| 
 | |
| /* 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_key_t key;
 | |
| 
 | |
| #ifdef __RTP__
 | |
|   data = __gthread_get_tls_data ();
 | |
| #else
 | |
|   /* In kernel mode, we can be called in the context of the thread
 | |
|      doing the killing, so must use the TCB to determine the data of
 | |
|      the thread being killed.  */
 | |
|   data = __gthread_get_tsd_data (tcb);
 | |
| #endif
 | |
|   
 | |
|   if (data && data->owner == &self_owner)
 | |
|     {
 | |
| #ifdef __RTP__
 | |
|       __gthread_enter_tls_dtor_context ();
 | |
| #else
 | |
|       __gthread_enter_tsd_dtor_context (tcb);
 | |
| #endif
 | |
|       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);
 | |
| #ifdef __RTP__
 | |
|       __gthread_leave_tls_dtor_context ();
 | |
| #else
 | |
|       __gthread_leave_tsd_dtor_context (tcb);
 | |
| #endif
 | |
| 
 | |
| #ifdef __RTP__
 | |
|       __gthread_set_tls_data (0);
 | |
| #else
 | |
|       __gthread_set_tsd_data (tcb, 0);
 | |
| #endif
 | |
|     }
 | |
| } 
 | |
| 
 | |
| /* 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);
 | |
| #endif
 | |
|   /* Unregister the hook.  */
 | |
|   if (delete_hook_installed)
 | |
|     taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
 | |
| 
 | |
|   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 (!delete_hook_installed)
 | |
| 	{
 | |
| 	  /* Install the delete hook.  */
 | |
| 	  if (__gthread_mutex_lock (&tls_lock) == ERROR)
 | |
| 	    return ENOMEM;
 | |
| 	  if (!delete_hook_installed)
 | |
| 	    {
 | |
| 	      taskDeleteHookAdd ((FUNCPTR)tls_delete_hook);
 | |
| 	      delete_hook_installed = 1;
 | |
| 	    }
 | |
| 	  __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 */
 |