xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/config/gthr-vxworks-tls.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* Copyright (C) 2002-2022 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
tls_delete_hook(void * tcb ATTRIBUTE_UNUSED)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
tls_init(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
tls_destructor(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
__gthread_key_create(__gthread_key_t * keyp,tls_dtor dtor)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
__gthread_key_delete(__gthread_key_t key)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 *
__gthread_getspecific(__gthread_key_t key)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
__gthread_setspecific(__gthread_key_t key,void * value)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 */
373