]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/gcc/config/vxlib.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / gcc / config / vxlib.c
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
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
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
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
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
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
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 *
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
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 */