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