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