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