]>
Commit | Line | Data |
---|---|---|
748086b7 | 1 | /* Copyright (C) 2002, 2003, 2004, 2005, 2009 Free Software Foundation, Inc. |
c0da9742 OH |
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 | |
748086b7 | 8 | Software Foundation; either version 3, or (at your option) any later |
c0da9742 OH |
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 | ||
748086b7 JJ |
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. | |
c0da9742 | 19 | |
748086b7 JJ |
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/>. */ | |
c0da9742 OH |
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 | { | |
caaf5345 | 155 | struct tls_data *data; |
c0da9742 OH |
156 | __gthread_key_t key; |
157 | ||
caaf5345 NS |
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 | ||
c0da9742 OH |
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 | } | |
caaf5345 | 191 | #ifdef __RTP__ |
c0da9742 | 192 | __gthread_set_tls_data (0); |
caaf5345 NS |
193 | #else |
194 | __gthread_set_tsd_data (tcb, 0); | |
195 | #endif | |
c0da9742 OH |
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 */ |