(root)/
gcc-13.2.0/
libgcc/
config/
gthr-vxworks-tls.c
       1  /* Copyright (C) 2002-2023 Free Software Foundation, Inc.
       2     Contributed by Zack Weinberg <zack@codesourcery.com>
       3  
       4  This file is part of GCC.
       5  
       6  GCC is free software; you can redistribute it and/or modify it under
       7  the terms of the GNU General Public License as published by the Free
       8  Software Foundation; either version 3, or (at your option) any later
       9  version.
      10  
      11  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      12  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14  for more details.
      15  
      16  Under Section 7 of GPL version 3, you are granted additional
      17  permissions described in the GCC Runtime Library Exception, version
      18  3.1, as published by the Free Software Foundation.
      19  
      20  You should have received a copy of the GNU General Public License and
      21  a copy of the GCC Runtime Library Exception along with this program;
      22  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      23  <http://www.gnu.org/licenses/>.  */
      24  
      25  /* Threads compatibility routines for libgcc2 for VxWorks.
      26     These are out-of-line routines called from gthr-vxworks.h.
      27  
      28     This file provides the TLS related support routines, calling specific
      29     VxWorks kernel entry points for this purpose.  */
      30  
      31  #include "tconfig.h"
      32  #include "tsystem.h"
      33  #include "gthr.h"
      34  
      35  #if defined(__GTHREADS)
      36  
      37  #include <vxWorks.h>
      38  #ifndef __RTP__
      39  #include <vxLib.h>
      40  #endif
      41  #include <taskLib.h>
      42  #ifndef __RTP__
      43  #include <taskHookLib.h>
      44  #else
      45  #include <errno.h>
      46  #endif
      47  
      48  #include <_vxworks-versions.h>
      49  
      50  /* Thread-local storage.
      51  
      52     A gthread TLS key is simply an offset in an array, the address of which
      53     we store in a single pointer field associated with the current task.
      54  
      55     On VxWorks 7, we have direct support for __thread variables and use
      56     such a variable as the pointer "field".  On other versions, we resort
      57     to __gthread_get_tls_data and __gthread_set_tls_data functions provided
      58     by the kernel.
      59  
      60     There is also a global array which records which keys are valid and
      61     which have destructors.
      62  
      63     A task delete hook is installed to execute key destructors.  The routines
      64     __gthread_enter_tls_dtor_context and __gthread_leave_tls_dtor_context,
      65     which are also provided by the kernel, ensure that it is safe to call
      66     free() on memory allocated by the task being deleted.  This is a no-op on
      67     VxWorks 5, but a major undertaking on AE.
      68  
      69     The task delete hook is only installed when at least one thread
      70     has TLS data.  This is a necessary precaution, to allow this module
      71     to be unloaded - a module with a hook can not be removed.
      72  
      73     Since this interface is used to allocate only a small number of
      74     keys, the table size is small and static, which simplifies the
      75     code quite a bit.  Revisit this if and when it becomes necessary.  */
      76  
      77  #define MAX_KEYS 4
      78  
      79  /* This is the structure pointed to by the pointer returned
      80     by __gthread_get_tls_data.  */
      81  struct tls_data
      82  {
      83    int *owner;
      84    void *values[MAX_KEYS];
      85    unsigned int generation[MAX_KEYS];
      86  };
      87  
      88  /* To make sure we only delete TLS data associated with this object,
      89     include a pointer to a local variable in the TLS data object.  */
      90  static int self_owner;
      91  
      92  /* Flag to check whether the delete hook is installed.  Once installed
      93     it is only removed when unloading this module.  */
      94  static volatile int delete_hook_installed;
      95  
      96  /* TLS data access internal API.  A straight __thread variable starting with
      97     VxWorks 7, a pointer returned by kernel provided routines otherwise.  And
      98     on VxWorks 6, the kernel expects us to notify entry/exit of regions
      99     handling such variables by calls to kernel provided __gthread routines.  */
     100  
     101  #if _VXWORKS_MAJOR_GE(7)
     102  
     103  static __thread struct tls_data *__gthread_tls_data;
     104  
     105  #define VX_GET_TLS_DATA() __gthread_tls_data
     106  #define VX_SET_TLS_DATA(x) __gthread_tls_data = (x)
     107  
     108  #else
     109  
     110  extern void *__gthread_get_tls_data (void);
     111  extern void __gthread_set_tls_data (void *data);
     112  
     113  #define VX_GET_TLS_DATA() __gthread_get_tls_data()
     114  #define VX_SET_TLS_DATA(x) __gthread_set_tls_data(x)
     115  
     116  #endif
     117  
     118  #if _VXWORKS_MAJOR_EQ(6)
     119  
     120  extern void __gthread_enter_tls_dtor_context (void);
     121  extern void __gthread_leave_tls_dtor_context (void);
     122  
     123  #define VX_ENTER_TLS_DTOR() __gthread_enter_tls_dtor_context ()
     124  #define VX_LEAVE_TLS_DTOR() __gthread_leave_tls_dtor_context ()
     125  
     126  #else
     127  
     128  #define VX_ENTER_TLS_DTOR()
     129  #define VX_LEAVE_TLS_DTOR()
     130  
     131  #endif
     132  
     133  /* This is a global structure which records all of the active keys.
     134  
     135     A key is potentially valid (i.e. has been handed out by
     136     __gthread_key_create) iff its generation count in this structure is
     137     even.  In that case, the matching entry in the dtors array is a
     138     routine to be called when a thread terminates with a valid,
     139     non-NULL specific value for that key.
     140  
     141     A key is actually valid in a thread T iff the generation count
     142     stored in this structure is equal to the generation count stored in
     143     T's specific-value structure.  */
     144  
     145  typedef void (*tls_dtor) (void *);
     146  
     147  struct tls_keys
     148  {
     149    tls_dtor dtor[MAX_KEYS];
     150    unsigned int generation[MAX_KEYS];
     151  };
     152  
     153  #define KEY_VALID_P(key) !(tls_keys.generation[key] & 1)
     154  
     155  /* Note: if MAX_KEYS is increased, this initializer must be updated
     156     to match.  All the generation counts begin at 1, which means no
     157     key is valid.  */
     158  static struct tls_keys tls_keys =
     159  {
     160    { NULL, NULL, NULL, NULL },
     161    { 1, 1, 1, 1 }
     162  };
     163  
     164  /* This lock protects the tls_keys structure.  */
     165  static __gthread_mutex_t tls_lock;
     166  
     167  static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT;
     168  
     169  /* Internal routines.  */
     170  
     171  /* The task TCB has just been deleted.  Call the destructor
     172     function for each TLS key that has both a destructor and
     173     a non-NULL specific value in this thread.
     174  
     175     This routine does not need to take tls_lock; the generation
     176     count protects us from calling a stale destructor.  It does
     177     need to read tls_keys.dtor[key] atomically.  */
     178  
     179  void
     180  tls_delete_hook (void *tcb ATTRIBUTE_UNUSED)
     181  {
     182    struct tls_data *data;
     183    __gthread_key_t key;
     184  
     185    data = VX_GET_TLS_DATA();
     186  
     187    if (data && data->owner == &self_owner)
     188      {
     189        VX_ENTER_TLS_DTOR();
     190        for (key = 0; key < MAX_KEYS; key++)
     191  	{
     192  	  if (data->generation[key] == tls_keys.generation[key])
     193  	    {
     194  	      tls_dtor dtor = tls_keys.dtor[key];
     195  
     196  	      if (dtor)
     197  		dtor (data->values[key]);
     198  	    }
     199  	}
     200        free (data);
     201  
     202        VX_LEAVE_TLS_DTOR();
     203        VX_SET_TLS_DATA(NULL);
     204      }
     205  }
     206  
     207  /* Initialize global data used by the TLS system.  */
     208  static void
     209  tls_init (void)
     210  {
     211    __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock);
     212  }
     213  
     214  static void tls_destructor (void) __attribute__ ((destructor));
     215  static void
     216  tls_destructor (void)
     217  {
     218  #ifdef __RTP__
     219    /* All threads but this one should have exited by now.  */
     220    tls_delete_hook (NULL);
     221  #endif
     222    /* Unregister the hook.  */
     223    if (delete_hook_installed)
     224      taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
     225  
     226    if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR)
     227      semDelete (tls_lock);
     228  }
     229  
     230  /* External interface */
     231  
     232  /* Store in KEYP a value which can be passed to __gthread_setspecific/
     233     __gthread_getspecific to store and retrieve a value which is
     234     specific to each calling thread.  If DTOR is not NULL, it will be
     235     called when a thread terminates with a non-NULL specific value for
     236     this key, with the value as its sole argument.  */
     237  
     238  int
     239  __gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor)
     240  {
     241    __gthread_key_t key;
     242  
     243    __gthread_once (&tls_init_guard, tls_init);
     244  
     245    if (__gthread_mutex_lock (&tls_lock) == ERROR)
     246      return errno;
     247  
     248    for (key = 0; key < MAX_KEYS; key++)
     249      if (!KEY_VALID_P (key))
     250        goto found_slot;
     251  
     252    /* no room */
     253    __gthread_mutex_unlock (&tls_lock);
     254    return EAGAIN;
     255  
     256   found_slot:
     257    tls_keys.generation[key]++;  /* making it even */
     258    tls_keys.dtor[key] = dtor;
     259    *keyp = key;
     260    __gthread_mutex_unlock (&tls_lock);
     261    return 0;
     262  }
     263  
     264  /* Invalidate KEY; it can no longer be used as an argument to
     265     setspecific/getspecific.  Note that this does NOT call destructor
     266     functions for any live values for this key.  */
     267  int
     268  __gthread_key_delete (__gthread_key_t key)
     269  {
     270    if (key >= MAX_KEYS)
     271      return EINVAL;
     272  
     273    __gthread_once (&tls_init_guard, tls_init);
     274  
     275    if (__gthread_mutex_lock (&tls_lock) == ERROR)
     276      return errno;
     277  
     278    if (!KEY_VALID_P (key))
     279      {
     280        __gthread_mutex_unlock (&tls_lock);
     281        return EINVAL;
     282      }
     283  
     284    tls_keys.generation[key]++;  /* making it odd */
     285    tls_keys.dtor[key] = 0;
     286  
     287    __gthread_mutex_unlock (&tls_lock);
     288    return 0;
     289  }
     290  
     291  /* Retrieve the thread-specific value for KEY.  If it has never been
     292     set in this thread, or KEY is invalid, returns NULL.
     293  
     294     It does not matter if this function races with key_create or
     295     key_delete; the worst that can happen is you get a value other than
     296     the one that a serialized implementation would have provided.  */
     297  
     298  void *
     299  __gthread_getspecific (__gthread_key_t key)
     300  {
     301    struct tls_data *data;
     302  
     303    if (key >= MAX_KEYS)
     304      return 0;
     305  
     306    data = VX_GET_TLS_DATA();
     307  
     308    if (!data)
     309      return 0;
     310  
     311    if (data->generation[key] != tls_keys.generation[key])
     312      return 0;
     313  
     314    return data->values[key];
     315  }
     316  
     317  /* Set the thread-specific value for KEY.  If KEY is invalid, or
     318     memory allocation fails, returns -1, otherwise 0.
     319  
     320     The generation count protects this function against races with
     321     key_create/key_delete; the worst thing that can happen is that a
     322     value is successfully stored into a dead generation (and then
     323     immediately becomes invalid).  However, we do have to make sure
     324     to read tls_keys.generation[key] atomically.  */
     325  
     326  int
     327  __gthread_setspecific (__gthread_key_t key, void *value)
     328  {
     329    struct tls_data *data;
     330    unsigned int generation;
     331  
     332    if (key >= MAX_KEYS)
     333      return EINVAL;
     334  
     335    data = VX_GET_TLS_DATA();
     336  
     337    if (!data)
     338      {
     339        if (!delete_hook_installed)
     340  	{
     341  	  /* Install the delete hook.  */
     342  	  if (__gthread_mutex_lock (&tls_lock) == ERROR)
     343  	    return ENOMEM;
     344  	  if (!delete_hook_installed)
     345  	    {
     346  	      taskDeleteHookAdd ((FUNCPTR)tls_delete_hook);
     347  	      delete_hook_installed = 1;
     348  	    }
     349  	  __gthread_mutex_unlock (&tls_lock);
     350  	}
     351  
     352        data = malloc (sizeof (struct tls_data));
     353        if (!data)
     354  	return ENOMEM;
     355  
     356        memset (data, 0, sizeof (struct tls_data));
     357        data->owner = &self_owner;
     358  
     359        VX_SET_TLS_DATA(data);
     360      }
     361  
     362    generation = tls_keys.generation[key];
     363  
     364    if (generation & 1)
     365      return EINVAL;
     366  
     367    data->generation[key] = generation;
     368    data->values[key] = value;
     369  
     370    return 0;
     371  }
     372  #endif /* __GTHREADS */