1/* Copyright (C) 2002, 2003, 2004, 2005 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, 51 Franklin Street, Fifth Floor, Boston, MA
1902110-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
61int
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.  */
121struct 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.  */
130static int self_owner;
131
132/* The number of threads for this module which have active TLS data.
133   This is protected by tls_lock.  */
134static int active_tls_threads;
135
136/* kernel provided routines */
137extern void *__gthread_get_tls_data (void);
138extern void __gthread_set_tls_data (void *data);
139
140extern void __gthread_enter_tls_dtor_context (void);
141extern 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
156typedef void (*tls_dtor) (void *);
157
158struct 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.  */
169static 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.  */
176static __gthread_mutex_t tls_lock;
177
178static __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
190static void
191tls_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.  */
227static void
228tls_init (void)
229{
230  __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock);
231}
232
233static void tls_destructor (void) __attribute__ ((destructor));
234static void
235tls_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
260int
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.  */
289int
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
320void *
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
348int
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 */
387