0
|
1 /* Copyright (C) 2002, 2003, 2004, 2005, 2009 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. The base VxWorks 5.x kernels
|
|
30 don't feature these entry points, and we provide gthr_supp_vxw_5x.c as an
|
|
31 option to fill this gap. Asking users to rebuild a kernel is not to be
|
|
32 taken lightly, still, so we have isolated these routines from the rest of
|
|
33 vxlib to ensure that the kernel dependencies are only dragged when really
|
|
34 necessary. */
|
|
35
|
|
36 #include "tconfig.h"
|
|
37 #include "tsystem.h"
|
|
38 #include "gthr.h"
|
|
39
|
|
40 #if defined(__GTHREADS)
|
|
41 #include <vxWorks.h>
|
|
42 #ifndef __RTP__
|
|
43 #include <vxLib.h>
|
|
44 #endif
|
|
45 #include <taskLib.h>
|
|
46 #ifndef __RTP__
|
|
47 #include <taskHookLib.h>
|
|
48 #else
|
|
49 # include <errno.h>
|
|
50 #endif
|
|
51
|
|
52 /* Thread-local storage.
|
|
53
|
|
54 We reserve a field in the TCB to point to a dynamically allocated
|
|
55 array which is used to store TLS values. A TLS key is simply an
|
|
56 offset in this array. The exact location of the TCB field is not
|
|
57 known to this code nor to vxlib.c -- all access to it indirects
|
|
58 through the routines __gthread_get_tls_data and
|
|
59 __gthread_set_tls_data, which are provided by the VxWorks kernel.
|
|
60
|
|
61 There is also a global array which records which keys are valid and
|
|
62 which have destructors.
|
|
63
|
|
64 A task delete hook is installed to execute key destructors. The
|
|
65 routines __gthread_enter_tls_dtor_context and
|
|
66 __gthread_leave_tls_dtor_context, which are also provided by the
|
|
67 kernel, ensure that it is safe to call free() on memory allocated
|
|
68 by the task being deleted. (This is a no-op on VxWorks 5, but
|
|
69 a major undertaking on AE.)
|
|
70
|
|
71 The task delete hook is only installed when at least one thread
|
|
72 has TLS data. This is a necessary precaution, to allow this module
|
|
73 to be unloaded - a module with a hook can not be removed.
|
|
74
|
|
75 Since this interface is used to allocate only a small number of
|
|
76 keys, the table size is small and static, which simplifies the
|
|
77 code quite a bit. Revisit this if and when it becomes necessary. */
|
|
78
|
|
79 #define MAX_KEYS 4
|
|
80
|
|
81 /* This is the structure pointed to by the pointer returned
|
|
82 by __gthread_get_tls_data. */
|
|
83 struct tls_data
|
|
84 {
|
|
85 int *owner;
|
|
86 void *values[MAX_KEYS];
|
|
87 unsigned int generation[MAX_KEYS];
|
|
88 };
|
|
89
|
|
90 /* To make sure we only delete TLS data associated with this object,
|
|
91 include a pointer to a local variable in the TLS data object. */
|
|
92 static int self_owner;
|
|
93
|
|
94 /* The number of threads for this module which have active TLS data.
|
|
95 This is protected by tls_lock. */
|
|
96 static int active_tls_threads;
|
|
97
|
|
98 /* kernel provided routines */
|
|
99 extern void *__gthread_get_tls_data (void);
|
|
100 extern void __gthread_set_tls_data (void *data);
|
|
101
|
|
102 extern void __gthread_enter_tls_dtor_context (void);
|
|
103 extern void __gthread_leave_tls_dtor_context (void);
|
|
104
|
|
105
|
|
106 /* This is a global structure which records all of the active keys.
|
|
107
|
|
108 A key is potentially valid (i.e. has been handed out by
|
|
109 __gthread_key_create) iff its generation count in this structure is
|
|
110 even. In that case, the matching entry in the dtors array is a
|
|
111 routine to be called when a thread terminates with a valid,
|
|
112 non-NULL specific value for that key.
|
|
113
|
|
114 A key is actually valid in a thread T iff the generation count
|
|
115 stored in this structure is equal to the generation count stored in
|
|
116 T's specific-value structure. */
|
|
117
|
|
118 typedef void (*tls_dtor) (void *);
|
|
119
|
|
120 struct tls_keys
|
|
121 {
|
|
122 tls_dtor dtor[MAX_KEYS];
|
|
123 unsigned int generation[MAX_KEYS];
|
|
124 };
|
|
125
|
|
126 #define KEY_VALID_P(key) !(tls_keys.generation[key] & 1)
|
|
127
|
|
128 /* Note: if MAX_KEYS is increased, this initializer must be updated
|
|
129 to match. All the generation counts begin at 1, which means no
|
|
130 key is valid. */
|
|
131 static struct tls_keys tls_keys =
|
|
132 {
|
|
133 { 0, 0, 0, 0 },
|
|
134 { 1, 1, 1, 1 }
|
|
135 };
|
|
136
|
|
137 /* This lock protects the tls_keys structure. */
|
|
138 static __gthread_mutex_t tls_lock;
|
|
139
|
|
140 static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT;
|
|
141
|
|
142 /* Internal routines. */
|
|
143
|
|
144 /* The task TCB has just been deleted. Call the destructor
|
|
145 function for each TLS key that has both a destructor and
|
|
146 a non-NULL specific value in this thread.
|
|
147
|
|
148 This routine does not need to take tls_lock; the generation
|
|
149 count protects us from calling a stale destructor. It does
|
|
150 need to read tls_keys.dtor[key] atomically. */
|
|
151
|
|
152 static void
|
|
153 tls_delete_hook (void *tcb ATTRIBUTE_UNUSED)
|
|
154 {
|
|
155 struct tls_data *data;
|
|
156 __gthread_key_t key;
|
|
157
|
|
158 #ifdef __RTP__
|
|
159 data = __gthread_get_tls_data ();
|
|
160 #else
|
|
161 /* In kernel mode, we can be called in the context of the thread
|
|
162 doing the killing, so must use the TCB to determine the data of
|
|
163 the thread being killed. */
|
|
164 data = __gthread_get_tsd_data (tcb);
|
|
165 #endif
|
|
166
|
|
167 if (data && data->owner == &self_owner)
|
|
168 {
|
|
169 __gthread_enter_tls_dtor_context ();
|
|
170 for (key = 0; key < MAX_KEYS; key++)
|
|
171 {
|
|
172 if (data->generation[key] == tls_keys.generation[key])
|
|
173 {
|
|
174 tls_dtor dtor = tls_keys.dtor[key];
|
|
175
|
|
176 if (dtor)
|
|
177 dtor (data->values[key]);
|
|
178 }
|
|
179 }
|
|
180 free (data);
|
|
181
|
|
182 /* We can't handle an error here, so just leave the thread
|
|
183 marked as loaded if one occurs. */
|
|
184 if (__gthread_mutex_lock (&tls_lock) != ERROR)
|
|
185 {
|
|
186 active_tls_threads--;
|
|
187 if (active_tls_threads == 0)
|
|
188 taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
|
|
189 __gthread_mutex_unlock (&tls_lock);
|
|
190 }
|
|
191 #ifdef __RTP__
|
|
192 __gthread_set_tls_data (0);
|
|
193 #else
|
|
194 __gthread_set_tsd_data (tcb, 0);
|
|
195 #endif
|
|
196 __gthread_leave_tls_dtor_context ();
|
|
197 }
|
|
198 }
|
|
199
|
|
200 /* Initialize global data used by the TLS system. */
|
|
201 static void
|
|
202 tls_init (void)
|
|
203 {
|
|
204 __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock);
|
|
205 }
|
|
206
|
|
207 static void tls_destructor (void) __attribute__ ((destructor));
|
|
208 static void
|
|
209 tls_destructor (void)
|
|
210 {
|
|
211 #ifdef __RTP__
|
|
212 /* All threads but this one should have exited by now. */
|
|
213 tls_delete_hook (NULL);
|
|
214 #else
|
|
215 /* Unregister the hook forcibly. The counter of active threads may
|
|
216 be incorrect, because constructors (like the C++ library's) and
|
|
217 destructors (like this one) run in the context of the shell rather
|
|
218 than in a task spawned from this module. */
|
|
219 taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
|
|
220 #endif
|
|
221
|
|
222 if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR)
|
|
223 semDelete (tls_lock);
|
|
224 }
|
|
225
|
|
226 /* External interface */
|
|
227
|
|
228 /* Store in KEYP a value which can be passed to __gthread_setspecific/
|
|
229 __gthread_getspecific to store and retrieve a value which is
|
|
230 specific to each calling thread. If DTOR is not NULL, it will be
|
|
231 called when a thread terminates with a non-NULL specific value for
|
|
232 this key, with the value as its sole argument. */
|
|
233
|
|
234 int
|
|
235 __gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor)
|
|
236 {
|
|
237 __gthread_key_t key;
|
|
238
|
|
239 __gthread_once (&tls_init_guard, tls_init);
|
|
240
|
|
241 if (__gthread_mutex_lock (&tls_lock) == ERROR)
|
|
242 return errno;
|
|
243
|
|
244 for (key = 0; key < MAX_KEYS; key++)
|
|
245 if (!KEY_VALID_P (key))
|
|
246 goto found_slot;
|
|
247
|
|
248 /* no room */
|
|
249 __gthread_mutex_unlock (&tls_lock);
|
|
250 return EAGAIN;
|
|
251
|
|
252 found_slot:
|
|
253 tls_keys.generation[key]++; /* making it even */
|
|
254 tls_keys.dtor[key] = dtor;
|
|
255 *keyp = key;
|
|
256 __gthread_mutex_unlock (&tls_lock);
|
|
257 return 0;
|
|
258 }
|
|
259
|
|
260 /* Invalidate KEY; it can no longer be used as an argument to
|
|
261 setspecific/getspecific. Note that this does NOT call destructor
|
|
262 functions for any live values for this key. */
|
|
263 int
|
|
264 __gthread_key_delete (__gthread_key_t key)
|
|
265 {
|
|
266 if (key >= MAX_KEYS)
|
|
267 return EINVAL;
|
|
268
|
|
269 __gthread_once (&tls_init_guard, tls_init);
|
|
270
|
|
271 if (__gthread_mutex_lock (&tls_lock) == ERROR)
|
|
272 return errno;
|
|
273
|
|
274 if (!KEY_VALID_P (key))
|
|
275 {
|
|
276 __gthread_mutex_unlock (&tls_lock);
|
|
277 return EINVAL;
|
|
278 }
|
|
279
|
|
280 tls_keys.generation[key]++; /* making it odd */
|
|
281 tls_keys.dtor[key] = 0;
|
|
282
|
|
283 __gthread_mutex_unlock (&tls_lock);
|
|
284 return 0;
|
|
285 }
|
|
286
|
|
287 /* Retrieve the thread-specific value for KEY. If it has never been
|
|
288 set in this thread, or KEY is invalid, returns NULL.
|
|
289
|
|
290 It does not matter if this function races with key_create or
|
|
291 key_delete; the worst that can happen is you get a value other than
|
|
292 the one that a serialized implementation would have provided. */
|
|
293
|
|
294 void *
|
|
295 __gthread_getspecific (__gthread_key_t key)
|
|
296 {
|
|
297 struct tls_data *data;
|
|
298
|
|
299 if (key >= MAX_KEYS)
|
|
300 return 0;
|
|
301
|
|
302 data = __gthread_get_tls_data ();
|
|
303
|
|
304 if (!data)
|
|
305 return 0;
|
|
306
|
|
307 if (data->generation[key] != tls_keys.generation[key])
|
|
308 return 0;
|
|
309
|
|
310 return data->values[key];
|
|
311 }
|
|
312
|
|
313 /* Set the thread-specific value for KEY. If KEY is invalid, or
|
|
314 memory allocation fails, returns -1, otherwise 0.
|
|
315
|
|
316 The generation count protects this function against races with
|
|
317 key_create/key_delete; the worst thing that can happen is that a
|
|
318 value is successfully stored into a dead generation (and then
|
|
319 immediately becomes invalid). However, we do have to make sure
|
|
320 to read tls_keys.generation[key] atomically. */
|
|
321
|
|
322 int
|
|
323 __gthread_setspecific (__gthread_key_t key, void *value)
|
|
324 {
|
|
325 struct tls_data *data;
|
|
326 unsigned int generation;
|
|
327
|
|
328 if (key >= MAX_KEYS)
|
|
329 return EINVAL;
|
|
330
|
|
331 data = __gthread_get_tls_data ();
|
|
332 if (!data)
|
|
333 {
|
|
334 if (__gthread_mutex_lock (&tls_lock) == ERROR)
|
|
335 return ENOMEM;
|
|
336 if (active_tls_threads == 0)
|
|
337 taskDeleteHookAdd ((FUNCPTR)tls_delete_hook);
|
|
338 active_tls_threads++;
|
|
339 __gthread_mutex_unlock (&tls_lock);
|
|
340
|
|
341 data = malloc (sizeof (struct tls_data));
|
|
342 if (!data)
|
|
343 return ENOMEM;
|
|
344
|
|
345 memset (data, 0, sizeof (struct tls_data));
|
|
346 data->owner = &self_owner;
|
|
347 __gthread_set_tls_data (data);
|
|
348 }
|
|
349
|
|
350 generation = tls_keys.generation[key];
|
|
351
|
|
352 if (generation & 1)
|
|
353 return EINVAL;
|
|
354
|
|
355 data->generation[key] = generation;
|
|
356 data->values[key] = value;
|
|
357
|
|
358 return 0;
|
|
359 }
|
|
360 #endif /* __GTHREADS */
|