vxlib.c revision 132718
1/* Copyright (C) 2002 Free Software Foundation, Inc.
2   Contributed by Zack Weinberg <zack@codesourcery.com>
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 2, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License
17along with GCC; see the file COPYING.  If not, write to the Free
18Software Foundation, 59 Temple Place - Suite 330, Boston, MA
1902111-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
54int
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.  */
102struct tsd_data
103{
104  void *values[MAX_KEYS];
105  unsigned int generation[MAX_KEYS];
106};
107
108
109/* kernel provided routines */
110extern void *__gthread_get_tsd_data (WIND_TCB *tcb);
111extern void __gthread_set_tsd_data (WIND_TCB *tcb, void *data);
112
113extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb);
114extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb);
115
116typedef void (*fet_callback_t) (WIND_TCB *, unsigned int);
117extern 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
131typedef void (*tsd_dtor) (void *);
132
133struct 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.  */
144static 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.  */
151static __gthread_mutex_t tsd_lock;
152
153static __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
165static void
166tsd_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.  */
191static void
192tsd_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
206int
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.  */
235int
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
266void *
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
294int
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