145
|
1 /* Copyright (C) 2002-2020 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. */
|
|
98
|
|
99 #if _VXWORKS_MAJOR_GE(7)
|
|
100
|
|
101 static __thread struct tls_data *__gthread_tls_data;
|
|
102
|
|
103 #define VX_GET_TLS_DATA() __gthread_tls_data
|
|
104 #define VX_SET_TLS_DATA(x) __gthread_tls_data = (x)
|
|
105
|
|
106 #define VX_ENTER_TLS_DTOR()
|
|
107 #define VX_LEAVE_TLS_DTOR()
|
|
108
|
|
109 #else
|
|
110
|
|
111 extern void *__gthread_get_tls_data (void);
|
|
112 extern void __gthread_set_tls_data (void *data);
|
|
113
|
|
114 extern void __gthread_enter_tls_dtor_context (void);
|
|
115 extern void __gthread_leave_tls_dtor_context (void);
|
|
116
|
|
117 #define VX_GET_TLS_DATA() __gthread_get_tls_data()
|
|
118 #define VX_SET_TLS_DATA(x) __gthread_set_tls_data(x)
|
|
119
|
|
120 #define VX_ENTER_TLS_DTOR() __gthread_enter_tls_dtor_context ()
|
|
121 #define VX_LEAVE_TLS_DTOR() __gthread_leave_tls_dtor_context ()
|
|
122
|
|
123 #endif
|
|
124
|
|
125 /* This is a global structure which records all of the active keys.
|
|
126
|
|
127 A key is potentially valid (i.e. has been handed out by
|
|
128 __gthread_key_create) iff its generation count in this structure is
|
|
129 even. In that case, the matching entry in the dtors array is a
|
|
130 routine to be called when a thread terminates with a valid,
|
|
131 non-NULL specific value for that key.
|
|
132
|
|
133 A key is actually valid in a thread T iff the generation count
|
|
134 stored in this structure is equal to the generation count stored in
|
|
135 T's specific-value structure. */
|
|
136
|
|
137 typedef void (*tls_dtor) (void *);
|
|
138
|
|
139 struct tls_keys
|
|
140 {
|
|
141 tls_dtor dtor[MAX_KEYS];
|
|
142 unsigned int generation[MAX_KEYS];
|
|
143 };
|
|
144
|
|
145 #define KEY_VALID_P(key) !(tls_keys.generation[key] & 1)
|
|
146
|
|
147 /* Note: if MAX_KEYS is increased, this initializer must be updated
|
|
148 to match. All the generation counts begin at 1, which means no
|
|
149 key is valid. */
|
|
150 static struct tls_keys tls_keys =
|
|
151 {
|
|
152 { NULL, NULL, NULL, NULL },
|
|
153 { 1, 1, 1, 1 }
|
|
154 };
|
|
155
|
|
156 /* This lock protects the tls_keys structure. */
|
|
157 static __gthread_mutex_t tls_lock;
|
|
158
|
|
159 static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT;
|
|
160
|
|
161 /* Internal routines. */
|
|
162
|
|
163 /* The task TCB has just been deleted. Call the destructor
|
|
164 function for each TLS key that has both a destructor and
|
|
165 a non-NULL specific value in this thread.
|
|
166
|
|
167 This routine does not need to take tls_lock; the generation
|
|
168 count protects us from calling a stale destructor. It does
|
|
169 need to read tls_keys.dtor[key] atomically. */
|
|
170
|
|
171 void
|
|
172 tls_delete_hook (void *tcb ATTRIBUTE_UNUSED)
|
|
173 {
|
|
174 struct tls_data *data;
|
|
175 __gthread_key_t key;
|
|
176
|
|
177 data = VX_GET_TLS_DATA();
|
|
178
|
|
179 if (data && data->owner == &self_owner)
|
|
180 {
|
|
181 VX_ENTER_TLS_DTOR();
|
|
182 for (key = 0; key < MAX_KEYS; key++)
|
|
183 {
|
|
184 if (data->generation[key] == tls_keys.generation[key])
|
|
185 {
|
|
186 tls_dtor dtor = tls_keys.dtor[key];
|
|
187
|
|
188 if (dtor)
|
|
189 dtor (data->values[key]);
|
|
190 }
|
|
191 }
|
|
192 free (data);
|
|
193
|
|
194 VX_LEAVE_TLS_DTOR();
|
|
195 VX_SET_TLS_DATA(NULL);
|
|
196 }
|
|
197 }
|
|
198
|
|
199 /* Initialize global data used by the TLS system. */
|
|
200 static void
|
|
201 tls_init (void)
|
|
202 {
|
|
203 __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock);
|
|
204 }
|
|
205
|
|
206 static void tls_destructor (void) __attribute__ ((destructor));
|
|
207 static void
|
|
208 tls_destructor (void)
|
|
209 {
|
|
210 #ifdef __RTP__
|
|
211 /* All threads but this one should have exited by now. */
|
|
212 tls_delete_hook (NULL);
|
|
213 #endif
|
|
214 /* Unregister the hook. */
|
|
215 if (delete_hook_installed)
|
|
216 taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
|
|
217
|
|
218 if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR)
|
|
219 semDelete (tls_lock);
|
|
220 }
|
|
221
|
|
222 /* External interface */
|
|
223
|
|
224 /* Store in KEYP a value which can be passed to __gthread_setspecific/
|
|
225 __gthread_getspecific to store and retrieve a value which is
|
|
226 specific to each calling thread. If DTOR is not NULL, it will be
|
|
227 called when a thread terminates with a non-NULL specific value for
|
|
228 this key, with the value as its sole argument. */
|
|
229
|
|
230 int
|
|
231 __gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor)
|
|
232 {
|
|
233 __gthread_key_t key;
|
|
234
|
|
235 __gthread_once (&tls_init_guard, tls_init);
|
|
236
|
|
237 if (__gthread_mutex_lock (&tls_lock) == ERROR)
|
|
238 return errno;
|
|
239
|
|
240 for (key = 0; key < MAX_KEYS; key++)
|
|
241 if (!KEY_VALID_P (key))
|
|
242 goto found_slot;
|
|
243
|
|
244 /* no room */
|
|
245 __gthread_mutex_unlock (&tls_lock);
|
|
246 return EAGAIN;
|
|
247
|
|
248 found_slot:
|
|
249 tls_keys.generation[key]++; /* making it even */
|
|
250 tls_keys.dtor[key] = dtor;
|
|
251 *keyp = key;
|
|
252 __gthread_mutex_unlock (&tls_lock);
|
|
253 return 0;
|
|
254 }
|
|
255
|
|
256 /* Invalidate KEY; it can no longer be used as an argument to
|
|
257 setspecific/getspecific. Note that this does NOT call destructor
|
|
258 functions for any live values for this key. */
|
|
259 int
|
|
260 __gthread_key_delete (__gthread_key_t key)
|
|
261 {
|
|
262 if (key >= MAX_KEYS)
|
|
263 return EINVAL;
|
|
264
|
|
265 __gthread_once (&tls_init_guard, tls_init);
|
|
266
|
|
267 if (__gthread_mutex_lock (&tls_lock) == ERROR)
|
|
268 return errno;
|
|
269
|
|
270 if (!KEY_VALID_P (key))
|
|
271 {
|
|
272 __gthread_mutex_unlock (&tls_lock);
|
|
273 return EINVAL;
|
|
274 }
|
|
275
|
|
276 tls_keys.generation[key]++; /* making it odd */
|
|
277 tls_keys.dtor[key] = 0;
|
|
278
|
|
279 __gthread_mutex_unlock (&tls_lock);
|
|
280 return 0;
|
|
281 }
|
|
282
|
|
283 /* Retrieve the thread-specific value for KEY. If it has never been
|
|
284 set in this thread, or KEY is invalid, returns NULL.
|
|
285
|
|
286 It does not matter if this function races with key_create or
|
|
287 key_delete; the worst that can happen is you get a value other than
|
|
288 the one that a serialized implementation would have provided. */
|
|
289
|
|
290 void *
|
|
291 __gthread_getspecific (__gthread_key_t key)
|
|
292 {
|
|
293 struct tls_data *data;
|
|
294
|
|
295 if (key >= MAX_KEYS)
|
|
296 return 0;
|
|
297
|
|
298 data = VX_GET_TLS_DATA();
|
|
299
|
|
300 if (!data)
|
|
301 return 0;
|
|
302
|
|
303 if (data->generation[key] != tls_keys.generation[key])
|
|
304 return 0;
|
|
305
|
|
306 return data->values[key];
|
|
307 }
|
|
308
|
|
309 /* Set the thread-specific value for KEY. If KEY is invalid, or
|
|
310 memory allocation fails, returns -1, otherwise 0.
|
|
311
|
|
312 The generation count protects this function against races with
|
|
313 key_create/key_delete; the worst thing that can happen is that a
|
|
314 value is successfully stored into a dead generation (and then
|
|
315 immediately becomes invalid). However, we do have to make sure
|
|
316 to read tls_keys.generation[key] atomically. */
|
|
317
|
|
318 int
|
|
319 __gthread_setspecific (__gthread_key_t key, void *value)
|
|
320 {
|
|
321 struct tls_data *data;
|
|
322 unsigned int generation;
|
|
323
|
|
324 if (key >= MAX_KEYS)
|
|
325 return EINVAL;
|
|
326
|
|
327 data = VX_GET_TLS_DATA();
|
|
328
|
|
329 if (!data)
|
|
330 {
|
|
331 if (!delete_hook_installed)
|
|
332 {
|
|
333 /* Install the delete hook. */
|
|
334 if (__gthread_mutex_lock (&tls_lock) == ERROR)
|
|
335 return ENOMEM;
|
|
336 if (!delete_hook_installed)
|
|
337 {
|
|
338 taskDeleteHookAdd ((FUNCPTR)tls_delete_hook);
|
|
339 delete_hook_installed = 1;
|
|
340 }
|
|
341 __gthread_mutex_unlock (&tls_lock);
|
|
342 }
|
|
343
|
|
344 data = malloc (sizeof (struct tls_data));
|
|
345 if (!data)
|
|
346 return ENOMEM;
|
|
347
|
|
348 memset (data, 0, sizeof (struct tls_data));
|
|
349 data->owner = &self_owner;
|
|
350
|
|
351 VX_SET_TLS_DATA(data);
|
|
352 }
|
|
353
|
|
354 generation = tls_keys.generation[key];
|
|
355
|
|
356 if (generation & 1)
|
|
357 return EINVAL;
|
|
358
|
|
359 data->generation[key] = generation;
|
|
360 data->values[key] = value;
|
|
361
|
|
362 return 0;
|
|
363 }
|
|
364 #endif /* __GTHREADS */
|