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
__gthread_once(__gthread_once_t * guard,void (* func)(void))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
tsd_delete_hook(WIND_TCB * tcb)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
tsd_init(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 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.  */
205 
206 int
__gthread_key_create(__gthread_key_t * keyp,tsd_dtor dtor)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
__gthread_key_delete(__gthread_key_t key)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 *
__gthread_getspecific(__gthread_key_t key)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
__gthread_setspecific(__gthread_key_t key,void * value)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 }
326