OSDN Git Service

Merge basic-improvements-branch to trunk
[pf3gnuchains/gcc-fork.git] / gcc / config / vxlib.c
1 /* Copyright (C) 2002 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, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.  */
20
21 /* Threads compatibility routines for libgcc2 for VxWorks.
22    These are out-of-line routines called from gthr-vxworks.h.  */
23
24 /* As a special exception, if you link this library with other files,
25    some of which are compiled with GCC, to produce an executable,
26    this library does not by itself cause the resulting executable
27    to be covered by the GNU General Public License.
28    This exception does not however invalidate any other reasons why
29    the executable file might be covered by the GNU General Public License.  */
30
31 #include "tconfig.h"
32 #include "tsystem.h"
33 #include "gthr.h"
34
35 #include <vxWorks.h>
36 #include <vxLib.h>
37 #include <taskLib.h>
38 #include <taskHookLib.h>
39
40 /* Init-once operation.
41
42    This would be a clone of the implementation from gthr-solaris.h,
43    except that we have a bootstrap problem - the whole point of this
44    exercise is to prevent double initialization, but if two threads
45    are racing with each other, once->mutex is liable to be initialized
46    by both.  Then each thread will lock its own mutex, and proceed to
47    call the initialization routine.
48
49    So instead we use a bare atomic primitive (vxTas()) to handle
50    mutual exclusion.  Threads losing the race then busy-wait, calling
51    taskDelay() to yield the processor, until the initialization is
52    completed.  Inefficient, but reliable.  */
53
54 int
55 __gthread_once (__gthread_once_t *guard, void (*func)(void))
56 {
57   if (guard->done)
58     return 0;
59
60   while (!vxTas ((void *)&guard->busy))
61     taskDelay (1);
62
63   /* Only one thread at a time gets here.  Check ->done again, then
64      go ahead and call func() if no one has done it yet.  */
65   if (!guard->done)
66     {
67       func ();
68       guard->done = 1;
69     }
70
71   guard->busy = 0;
72   return 0;
73 }
74
75 /* Thread-specific data.
76
77    We reserve a field in the TCB to point to a dynamically allocated
78    array which is used to store TSD values.  A TSD key is simply an
79    offset in this array.  The exact location of the TCB field is not
80    known to this code nor to vxlib.c -- all access to it indirects
81    through the routines __gthread_get_tsd_data and
82    __gthread_set_tsd_data, which are provided by the VxWorks kernel.
83
84    There is also a global array which records which keys are valid and
85    which have destructors.
86
87    A task delete hook is installed to execute key destructors.  The
88    routines __gthread_enter_tsd_dtor_context and
89    __gthread_leave_tsd_dtor_context, which are also provided by the
90    kernel, ensure that it is safe to call free() on memory allocated
91    by the task being deleted.  (This is a no-op on VxWorks 5, but
92    a major undertaking on AE.)
93
94    Since this interface is used to allocate only a small number of
95    keys, the table size is small and static, which simplifies the
96    code quite a bit.  Revisit this if and when it becomes necessary.  */
97
98 #define MAX_KEYS 4
99
100 /* This is the structure pointed to by the pointer returned
101    by __gthread_get_tsd_data.  */
102 struct tsd_data
103 {
104   void *values[MAX_KEYS];
105   unsigned int generation[MAX_KEYS];
106 };
107
108
109 /* kernel provided routines */
110 extern void *__gthread_get_tsd_data (WIND_TCB *tcb);
111 extern void __gthread_set_tsd_data (WIND_TCB *tcb, void *data);
112
113 extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb);
114 extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb);
115
116 typedef void (*fet_callback_t) (WIND_TCB *, unsigned int);
117 extern void __gthread_for_all_tasks (fet_callback_t fun, unsigned int number);
118
119 /* This is a global structure which records all of the active keys.
120
121    A key is potentially valid (i.e. has been handed out by
122    __gthread_key_create) iff its generation count in this structure is
123    even.  In that case, the matching entry in the dtors array is a
124    routine to be called when a thread terminates with a valid,
125    non-NULL specific value for that key.
126
127    A key is actually valid in a thread T iff the generation count
128    stored in this structure is equal to the generation count stored in
129    T's specific-value structure.  */
130
131 typedef void (*tsd_dtor) (void *);
132
133 struct tsd_keys
134 {
135   tsd_dtor dtor[MAX_KEYS];
136   unsigned int generation[MAX_KEYS];
137 };
138
139 #define KEY_VALID_P(key) !(tsd_keys.generation[key] & 1)
140
141 /* Note: if MAX_KEYS is increased, this initializer must be updated
142    to match.  All the generation counts begin at 1, which means no
143    key is valid.  */
144 static struct tsd_keys tsd_keys =
145 {
146   { 0, 0, 0, 0 },
147   { 1, 1, 1, 1 }
148 };
149
150 /* This lock protects the tsd_keys structure.  */
151 static __gthread_mutex_t tsd_lock;
152
153 static __gthread_once_t tsd_init_guard = __GTHREAD_ONCE_INIT;
154
155 /* Internal routines.  */
156
157 /* The task TCB has just been deleted.  Call the destructor
158    function for each TSD key that has both a destructor and
159    a non-NULL specific value in this thread.
160
161    This routine does not need to take tsd_lock; the generation
162    count protects us from calling a stale destructor.  It does
163    need to read tsd_keys.dtor[key] atomically.  */
164
165 static void
166 tsd_delete_hook (WIND_TCB *tcb)
167 {
168   struct tsd_data *data = __gthread_get_tsd_data (tcb);
169   __gthread_key_t key;
170
171   if (data)
172     {
173       __gthread_enter_tsd_dtor_context (tcb);
174       for (key = 0; key < MAX_KEYS; key++)
175         {
176           if (data->generation[key] == tsd_keys.generation[key])
177             {
178               tsd_dtor dtor = tsd_keys.dtor[key];
179
180               if (dtor)
181                 dtor (data->values[key]);
182             }
183         }
184       free (data);
185       __gthread_set_tsd_data (tcb, 0);
186       __gthread_leave_tsd_dtor_context (tcb);
187     }
188
189
190 /* Initialize global data used by the TSD system.  */
191 static void
192 tsd_init (void)
193 {
194   taskDeleteHookAdd ((FUNCPTR)tsd_delete_hook);
195   __GTHREAD_MUTEX_INIT_FUNCTION (&tsd_lock);
196 }
197
198 /* External interface */
199
200 /* Store in KEYP a value which can be passed to __gthread_setspecific/
201    __gthread_getspecific to store and retrive a value which is
202    specific to each calling thread.  If DTOR is not NULL, it will be
203    called when a thread terminates with a non-NULL specific value for
204    this key, with the value as its sole argument.  */
205
206 int
207 __gthread_key_create (__gthread_key_t *keyp, tsd_dtor dtor)
208 {
209   __gthread_key_t key;
210
211   __gthread_once (&tsd_init_guard, tsd_init);
212
213   if (__gthread_mutex_lock (&tsd_lock) == ERROR)
214     return errno;
215
216   for (key = 0; key < MAX_KEYS; key++)
217     if (!KEY_VALID_P (key))
218       goto found_slot;
219
220   /* no room */
221   __gthread_mutex_unlock (&tsd_lock);
222   return EAGAIN;
223
224  found_slot:
225   tsd_keys.generation[key]++;  /* making it even */
226   tsd_keys.dtor[key] = dtor;
227   *keyp = key;
228   __gthread_mutex_unlock (&tsd_lock);
229   return 0;
230 }
231
232 /* Invalidate KEY; it can no longer be used as an argument to
233    setspecific/getspecific.  Note that this does NOT call destructor
234    functions for any live values for this key.  */
235 int
236 __gthread_key_delete (__gthread_key_t key)
237 {
238   if (key >= MAX_KEYS)
239     return EINVAL;
240
241   __gthread_once (&tsd_init_guard, tsd_init);
242
243   if (__gthread_mutex_lock (&tsd_lock) == ERROR)
244     return errno;
245
246   if (!KEY_VALID_P (key))
247     {
248       __gthread_mutex_unlock (&tsd_lock);
249       return EINVAL;
250     }
251
252   tsd_keys.generation[key]++;  /* making it odd */
253   tsd_keys.dtor[key] = 0;
254
255   __gthread_mutex_unlock (&tsd_lock);
256   return 0;
257 }
258
259 /* Retrieve the thread-specific value for KEY.  If it has never been
260    set in this thread, or KEY is invalid, returns NULL.
261
262    It does not matter if this function races with key_create or
263    key_delete; the worst that can happen is you get a value other than
264    the one that a serialized implementation would have provided.  */
265
266 void *
267 __gthread_getspecific (__gthread_key_t key)
268 {
269   struct tsd_data *data;
270
271   if (key >= MAX_KEYS)
272     return 0;
273
274   data = __gthread_get_tsd_data (taskTcb (taskIdSelf ()));
275
276   if (!data)
277     return 0;
278
279   if (data->generation[key] != tsd_keys.generation[key])
280     return 0;
281
282   return data->values[key];
283 }
284
285 /* Set the thread-specific value for KEY.  If KEY is invalid, or
286    memory allocation fails, returns -1, otherwise 0.
287
288    The generation count protects this function against races with
289    key_create/key_delete; the worst thing that can happen is that a
290    value is successfully stored into a dead generation (and then
291    immediately becomes invalid).  However, we do have to make sure
292    to read tsd_keys.generation[key] atomically.  */
293
294 int
295 __gthread_setspecific (__gthread_key_t key, void *value)
296 {
297   struct tsd_data *data;
298   WIND_TCB *tcb;
299   unsigned int generation;
300
301   if (key >= MAX_KEYS)
302     return EINVAL;
303
304   tcb = taskTcb (taskIdSelf ());
305   data = __gthread_get_tsd_data (tcb);
306   if (!data)
307     {
308       data = malloc (sizeof (struct tsd_data));
309       if (!data)
310         return ENOMEM;
311
312       memset (data, 0, sizeof (struct tsd_data));
313       __gthread_set_tsd_data (tcb, data);
314     }
315
316   generation = tsd_keys.generation[key];
317
318   if (generation & 1)
319     return EINVAL;
320
321   data->generation[key] = generation;
322   data->values[key] = value;
323
324   return 0;
325 }