2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / gcc / config / vxlib.c
blob20a257e02c447012968eea3f1cc40c00ad833a2a
1 /* Copyright (C) 2002 Free Software Foundation, Inc.
2 Contributed by Zack Weinberg <zack@codesourcery.com>
4 This file is part of GCC.
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.
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.
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. */
21 /* Threads compatibility routines for libgcc2 for VxWorks.
22 These are out-of-line routines called from gthr-vxworks.h. */
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. */
31 #include "tconfig.h"
32 #include "tsystem.h"
33 #include "gthr.h"
35 #include <vxWorks.h>
36 #include <vxLib.h>
37 #include <taskLib.h>
38 #include <taskHookLib.h>
40 /* Init-once operation.
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.
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. */
54 int
55 __gthread_once (__gthread_once_t *guard, void (*func)(void))
57 if (guard->done)
58 return 0;
60 while (!vxTas ((void *)&guard->busy))
61 taskDelay (1);
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)
67 func ();
68 guard->done = 1;
71 guard->busy = 0;
72 return 0;
75 /* Thread-specific data.
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.
84 There is also a global array which records which keys are valid and
85 which have destructors.
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.)
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. */
98 #define MAX_KEYS 4
100 /* This is the structure pointed to by the pointer returned
101 by __gthread_get_tsd_data. */
102 struct tsd_data
104 void *values[MAX_KEYS];
105 unsigned int generation[MAX_KEYS];
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);
113 extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb);
114 extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb);
116 typedef void (*fet_callback_t) (WIND_TCB *, unsigned int);
117 extern void __gthread_for_all_tasks (fet_callback_t fun, unsigned int number);
119 /* This is a global structure which records all of the active keys.
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.
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. */
131 typedef void (*tsd_dtor) (void *);
133 struct tsd_keys
135 tsd_dtor dtor[MAX_KEYS];
136 unsigned int generation[MAX_KEYS];
139 #define KEY_VALID_P(key) !(tsd_keys.generation[key] & 1)
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 =
146 { 0, 0, 0, 0 },
147 { 1, 1, 1, 1 }
150 /* This lock protects the tsd_keys structure. */
151 static __gthread_mutex_t tsd_lock;
153 static __gthread_once_t tsd_init_guard = __GTHREAD_ONCE_INIT;
155 /* Internal routines. */
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.
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. */
165 static void
166 tsd_delete_hook (WIND_TCB *tcb)
168 struct tsd_data *data = __gthread_get_tsd_data (tcb);
169 __gthread_key_t key;
171 if (data)
173 __gthread_enter_tsd_dtor_context (tcb);
174 for (key = 0; key < MAX_KEYS; key++)
176 if (data->generation[key] == tsd_keys.generation[key])
178 tsd_dtor dtor = tsd_keys.dtor[key];
180 if (dtor)
181 dtor (data->values[key]);
184 free (data);
185 __gthread_set_tsd_data (tcb, 0);
186 __gthread_leave_tsd_dtor_context (tcb);
190 /* Initialize global data used by the TSD system. */
191 static void
192 tsd_init (void)
194 taskDeleteHookAdd ((FUNCPTR)tsd_delete_hook);
195 __GTHREAD_MUTEX_INIT_FUNCTION (&tsd_lock);
198 /* External interface */
200 /* Store in KEYP a value which can be passed to __gthread_setspecific/
201 __gthread_getspecific to store and retrieve 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. */
207 __gthread_key_create (__gthread_key_t *keyp, tsd_dtor dtor)
209 __gthread_key_t key;
211 __gthread_once (&tsd_init_guard, tsd_init);
213 if (__gthread_mutex_lock (&tsd_lock) == ERROR)
214 return errno;
216 for (key = 0; key < MAX_KEYS; key++)
217 if (!KEY_VALID_P (key))
218 goto found_slot;
220 /* no room */
221 __gthread_mutex_unlock (&tsd_lock);
222 return EAGAIN;
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;
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. */
236 __gthread_key_delete (__gthread_key_t key)
238 if (key >= MAX_KEYS)
239 return EINVAL;
241 __gthread_once (&tsd_init_guard, tsd_init);
243 if (__gthread_mutex_lock (&tsd_lock) == ERROR)
244 return errno;
246 if (!KEY_VALID_P (key))
248 __gthread_mutex_unlock (&tsd_lock);
249 return EINVAL;
252 tsd_keys.generation[key]++; /* making it odd */
253 tsd_keys.dtor[key] = 0;
255 __gthread_mutex_unlock (&tsd_lock);
256 return 0;
259 /* Retrieve the thread-specific value for KEY. If it has never been
260 set in this thread, or KEY is invalid, returns NULL.
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. */
266 void *
267 __gthread_getspecific (__gthread_key_t key)
269 struct tsd_data *data;
271 if (key >= MAX_KEYS)
272 return 0;
274 data = __gthread_get_tsd_data (taskTcb (taskIdSelf ()));
276 if (!data)
277 return 0;
279 if (data->generation[key] != tsd_keys.generation[key])
280 return 0;
282 return data->values[key];
285 /* Set the thread-specific value for KEY. If KEY is invalid, or
286 memory allocation fails, returns -1, otherwise 0.
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. */
295 __gthread_setspecific (__gthread_key_t key, void *value)
297 struct tsd_data *data;
298 WIND_TCB *tcb;
299 unsigned int generation;
301 if (key >= MAX_KEYS)
302 return EINVAL;
304 tcb = taskTcb (taskIdSelf ());
305 data = __gthread_get_tsd_data (tcb);
306 if (!data)
308 data = malloc (sizeof (struct tsd_data));
309 if (!data)
310 return ENOMEM;
312 memset (data, 0, sizeof (struct tsd_data));
313 __gthread_set_tsd_data (tcb, data);
316 generation = tsd_keys.generation[key];
318 if (generation & 1)
319 return EINVAL;
321 data->generation[key] = generation;
322 data->values[key] = value;
324 return 0;