1169689Skan/* Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
2132718Skan   Contributed by Zack Weinberg <zack@codesourcery.com>
3132718Skan
4132718SkanThis file is part of GCC.
5132718Skan
6132718SkanGCC is free software; you can redistribute it and/or modify it under
7132718Skanthe terms of the GNU General Public License as published by the Free
8132718SkanSoftware Foundation; either version 2, or (at your option) any later
9132718Skanversion.
10132718Skan
11132718SkanGCC is distributed in the hope that it will be useful, but WITHOUT ANY
12132718SkanWARRANTY; without even the implied warranty of MERCHANTABILITY or
13132718SkanFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14132718Skanfor more details.
15132718Skan
16132718SkanYou should have received a copy of the GNU General Public License
17132718Skanalong with GCC; see the file COPYING.  If not, write to the Free
18169689SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
19169689Skan02110-1301, USA.  */
20132718Skan
21132718Skan/* As a special exception, if you link this library with other files,
22132718Skan   some of which are compiled with GCC, to produce an executable,
23132718Skan   this library does not by itself cause the resulting executable
24132718Skan   to be covered by the GNU General Public License.
25132718Skan   This exception does not however invalidate any other reasons why
26132718Skan   the executable file might be covered by the GNU General Public License.  */
27132718Skan
28169689Skan/* Threads compatibility routines for libgcc2 for VxWorks.
29169689Skan   These are out-of-line routines called from gthr-vxworks.h.  */
30169689Skan
31132718Skan#include "tconfig.h"
32132718Skan#include "tsystem.h"
33132718Skan#include "gthr.h"
34132718Skan
35169689Skan#if defined(__GTHREADS)
36132718Skan#include <vxWorks.h>
37169689Skan#ifndef __RTP__
38132718Skan#include <vxLib.h>
39169689Skan#endif
40132718Skan#include <taskLib.h>
41169689Skan#ifndef __RTP__
42132718Skan#include <taskHookLib.h>
43169689Skan#else
44169689Skan# include <errno.h>
45169689Skan#endif
46132718Skan
47132718Skan/* Init-once operation.
48132718Skan
49132718Skan   This would be a clone of the implementation from gthr-solaris.h,
50132718Skan   except that we have a bootstrap problem - the whole point of this
51132718Skan   exercise is to prevent double initialization, but if two threads
52132718Skan   are racing with each other, once->mutex is liable to be initialized
53132718Skan   by both.  Then each thread will lock its own mutex, and proceed to
54132718Skan   call the initialization routine.
55132718Skan
56132718Skan   So instead we use a bare atomic primitive (vxTas()) to handle
57132718Skan   mutual exclusion.  Threads losing the race then busy-wait, calling
58132718Skan   taskDelay() to yield the processor, until the initialization is
59132718Skan   completed.  Inefficient, but reliable.  */
60132718Skan
61132718Skanint
62132718Skan__gthread_once (__gthread_once_t *guard, void (*func)(void))
63132718Skan{
64132718Skan  if (guard->done)
65132718Skan    return 0;
66132718Skan
67169689Skan#ifdef __RTP__
68169689Skan  __gthread_lock_library ();
69169689Skan#else
70132718Skan  while (!vxTas ((void *)&guard->busy))
71132718Skan    taskDelay (1);
72169689Skan#endif
73132718Skan
74132718Skan  /* Only one thread at a time gets here.  Check ->done again, then
75132718Skan     go ahead and call func() if no one has done it yet.  */
76132718Skan  if (!guard->done)
77132718Skan    {
78132718Skan      func ();
79132718Skan      guard->done = 1;
80132718Skan    }
81132718Skan
82169689Skan#ifdef __RTP__
83169689Skan  __gthread_unlock_library ();
84169689Skan#else
85132718Skan  guard->busy = 0;
86169689Skan#endif
87132718Skan  return 0;
88132718Skan}
89132718Skan
90169689Skan/* Thread-local storage.
91132718Skan
92132718Skan   We reserve a field in the TCB to point to a dynamically allocated
93169689Skan   array which is used to store TLS values.  A TLS key is simply an
94132718Skan   offset in this array.  The exact location of the TCB field is not
95132718Skan   known to this code nor to vxlib.c -- all access to it indirects
96169689Skan   through the routines __gthread_get_tls_data and
97169689Skan   __gthread_set_tls_data, which are provided by the VxWorks kernel.
98132718Skan
99132718Skan   There is also a global array which records which keys are valid and
100132718Skan   which have destructors.
101132718Skan
102132718Skan   A task delete hook is installed to execute key destructors.  The
103169689Skan   routines __gthread_enter_tls_dtor_context and
104169689Skan   __gthread_leave_tls_dtor_context, which are also provided by the
105132718Skan   kernel, ensure that it is safe to call free() on memory allocated
106132718Skan   by the task being deleted.  (This is a no-op on VxWorks 5, but
107132718Skan   a major undertaking on AE.)
108132718Skan
109169689Skan   The task delete hook is only installed when at least one thread
110169689Skan   has TLS data.  This is a necessary precaution, to allow this module
111169689Skan   to be unloaded - a module with a hook can not be removed.
112169689Skan
113132718Skan   Since this interface is used to allocate only a small number of
114132718Skan   keys, the table size is small and static, which simplifies the
115132718Skan   code quite a bit.  Revisit this if and when it becomes necessary.  */
116132718Skan
117132718Skan#define MAX_KEYS 4
118132718Skan
119132718Skan/* This is the structure pointed to by the pointer returned
120169689Skan   by __gthread_get_tls_data.  */
121169689Skanstruct tls_data
122132718Skan{
123169689Skan  int *owner;
124132718Skan  void *values[MAX_KEYS];
125132718Skan  unsigned int generation[MAX_KEYS];
126132718Skan};
127132718Skan
128169689Skan/* To make sure we only delete TLS data associated with this object,
129169689Skan   include a pointer to a local variable in the TLS data object.  */
130169689Skanstatic int self_owner;
131132718Skan
132169689Skan/* The number of threads for this module which have active TLS data.
133169689Skan   This is protected by tls_lock.  */
134169689Skanstatic int active_tls_threads;
135169689Skan
136132718Skan/* kernel provided routines */
137169689Skanextern void *__gthread_get_tls_data (void);
138169689Skanextern void __gthread_set_tls_data (void *data);
139132718Skan
140169689Skanextern void __gthread_enter_tls_dtor_context (void);
141169689Skanextern void __gthread_leave_tls_dtor_context (void);
142132718Skan
143132718Skan
144132718Skan/* This is a global structure which records all of the active keys.
145132718Skan
146132718Skan   A key is potentially valid (i.e. has been handed out by
147132718Skan   __gthread_key_create) iff its generation count in this structure is
148132718Skan   even.  In that case, the matching entry in the dtors array is a
149132718Skan   routine to be called when a thread terminates with a valid,
150132718Skan   non-NULL specific value for that key.
151132718Skan
152132718Skan   A key is actually valid in a thread T iff the generation count
153132718Skan   stored in this structure is equal to the generation count stored in
154132718Skan   T's specific-value structure.  */
155132718Skan
156169689Skantypedef void (*tls_dtor) (void *);
157132718Skan
158169689Skanstruct tls_keys
159132718Skan{
160169689Skan  tls_dtor dtor[MAX_KEYS];
161132718Skan  unsigned int generation[MAX_KEYS];
162132718Skan};
163132718Skan
164169689Skan#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1)
165132718Skan
166132718Skan/* Note: if MAX_KEYS is increased, this initializer must be updated
167132718Skan   to match.  All the generation counts begin at 1, which means no
168132718Skan   key is valid.  */
169169689Skanstatic struct tls_keys tls_keys =
170132718Skan{
171132718Skan  { 0, 0, 0, 0 },
172132718Skan  { 1, 1, 1, 1 }
173132718Skan};
174132718Skan
175169689Skan/* This lock protects the tls_keys structure.  */
176169689Skanstatic __gthread_mutex_t tls_lock;
177132718Skan
178169689Skanstatic __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT;
179132718Skan
180132718Skan/* Internal routines.  */
181132718Skan
182132718Skan/* The task TCB has just been deleted.  Call the destructor
183169689Skan   function for each TLS key that has both a destructor and
184132718Skan   a non-NULL specific value in this thread.
185132718Skan
186169689Skan   This routine does not need to take tls_lock; the generation
187132718Skan   count protects us from calling a stale destructor.  It does
188169689Skan   need to read tls_keys.dtor[key] atomically.  */
189132718Skan
190132718Skanstatic void
191169689Skantls_delete_hook (void *tcb ATTRIBUTE_UNUSED)
192132718Skan{
193169689Skan  struct tls_data *data = __gthread_get_tls_data ();
194132718Skan  __gthread_key_t key;
195132718Skan
196169689Skan  if (data && data->owner == &self_owner)
197132718Skan    {
198169689Skan      __gthread_enter_tls_dtor_context ();
199132718Skan      for (key = 0; key < MAX_KEYS; key++)
200132718Skan	{
201169689Skan	  if (data->generation[key] == tls_keys.generation[key])
202132718Skan	    {
203169689Skan	      tls_dtor dtor = tls_keys.dtor[key];
204132718Skan
205132718Skan	      if (dtor)
206132718Skan		dtor (data->values[key]);
207132718Skan	    }
208132718Skan	}
209132718Skan      free (data);
210169689Skan
211169689Skan      /* We can't handle an error here, so just leave the thread
212169689Skan	 marked as loaded if one occurs.  */
213169689Skan      if (__gthread_mutex_lock (&tls_lock) != ERROR)
214169689Skan	{
215169689Skan	  active_tls_threads--;
216169689Skan	  if (active_tls_threads == 0)
217169689Skan	    taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
218169689Skan	  __gthread_mutex_unlock (&tls_lock);
219169689Skan	}
220169689Skan
221169689Skan      __gthread_set_tls_data (0);
222169689Skan      __gthread_leave_tls_dtor_context ();
223132718Skan    }
224132718Skan}
225132718Skan
226169689Skan/* Initialize global data used by the TLS system.  */
227132718Skanstatic void
228169689Skantls_init (void)
229132718Skan{
230169689Skan  __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock);
231132718Skan}
232132718Skan
233169689Skanstatic void tls_destructor (void) __attribute__ ((destructor));
234169689Skanstatic void
235169689Skantls_destructor (void)
236169689Skan{
237169689Skan#ifdef __RTP__
238169689Skan  /* All threads but this one should have exited by now.  */
239169689Skan  tls_delete_hook (NULL);
240169689Skan#else
241169689Skan  /* Unregister the hook forcibly.  The counter of active threads may
242169689Skan     be incorrect, because constructors (like the C++ library's) and
243169689Skan     destructors (like this one) run in the context of the shell rather
244169689Skan     than in a task spawned from this module.  */
245169689Skan  taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
246169689Skan#endif
247169689Skan
248169689Skan  if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR)
249169689Skan    semDelete (tls_lock);
250169689Skan}
251169689Skan
252132718Skan/* External interface */
253132718Skan
254132718Skan/* Store in KEYP a value which can be passed to __gthread_setspecific/
255132718Skan   __gthread_getspecific to store and retrieve a value which is
256132718Skan   specific to each calling thread.  If DTOR is not NULL, it will be
257132718Skan   called when a thread terminates with a non-NULL specific value for
258132718Skan   this key, with the value as its sole argument.  */
259132718Skan
260132718Skanint
261169689Skan__gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor)
262132718Skan{
263132718Skan  __gthread_key_t key;
264132718Skan
265169689Skan  __gthread_once (&tls_init_guard, tls_init);
266132718Skan
267169689Skan  if (__gthread_mutex_lock (&tls_lock) == ERROR)
268132718Skan    return errno;
269132718Skan
270132718Skan  for (key = 0; key < MAX_KEYS; key++)
271132718Skan    if (!KEY_VALID_P (key))
272132718Skan      goto found_slot;
273132718Skan
274132718Skan  /* no room */
275169689Skan  __gthread_mutex_unlock (&tls_lock);
276132718Skan  return EAGAIN;
277132718Skan
278132718Skan found_slot:
279169689Skan  tls_keys.generation[key]++;  /* making it even */
280169689Skan  tls_keys.dtor[key] = dtor;
281132718Skan  *keyp = key;
282169689Skan  __gthread_mutex_unlock (&tls_lock);
283132718Skan  return 0;
284132718Skan}
285132718Skan
286132718Skan/* Invalidate KEY; it can no longer be used as an argument to
287132718Skan   setspecific/getspecific.  Note that this does NOT call destructor
288132718Skan   functions for any live values for this key.  */
289132718Skanint
290132718Skan__gthread_key_delete (__gthread_key_t key)
291132718Skan{
292132718Skan  if (key >= MAX_KEYS)
293132718Skan    return EINVAL;
294132718Skan
295169689Skan  __gthread_once (&tls_init_guard, tls_init);
296132718Skan
297169689Skan  if (__gthread_mutex_lock (&tls_lock) == ERROR)
298132718Skan    return errno;
299132718Skan
300132718Skan  if (!KEY_VALID_P (key))
301132718Skan    {
302169689Skan      __gthread_mutex_unlock (&tls_lock);
303132718Skan      return EINVAL;
304132718Skan    }
305132718Skan
306169689Skan  tls_keys.generation[key]++;  /* making it odd */
307169689Skan  tls_keys.dtor[key] = 0;
308132718Skan
309169689Skan  __gthread_mutex_unlock (&tls_lock);
310132718Skan  return 0;
311132718Skan}
312132718Skan
313132718Skan/* Retrieve the thread-specific value for KEY.  If it has never been
314132718Skan   set in this thread, or KEY is invalid, returns NULL.
315132718Skan
316132718Skan   It does not matter if this function races with key_create or
317132718Skan   key_delete; the worst that can happen is you get a value other than
318132718Skan   the one that a serialized implementation would have provided.  */
319132718Skan
320132718Skanvoid *
321132718Skan__gthread_getspecific (__gthread_key_t key)
322132718Skan{
323169689Skan  struct tls_data *data;
324132718Skan
325132718Skan  if (key >= MAX_KEYS)
326132718Skan    return 0;
327132718Skan
328169689Skan  data = __gthread_get_tls_data ();
329132718Skan
330132718Skan  if (!data)
331132718Skan    return 0;
332132718Skan
333169689Skan  if (data->generation[key] != tls_keys.generation[key])
334132718Skan    return 0;
335132718Skan
336132718Skan  return data->values[key];
337132718Skan}
338132718Skan
339132718Skan/* Set the thread-specific value for KEY.  If KEY is invalid, or
340132718Skan   memory allocation fails, returns -1, otherwise 0.
341132718Skan
342132718Skan   The generation count protects this function against races with
343132718Skan   key_create/key_delete; the worst thing that can happen is that a
344132718Skan   value is successfully stored into a dead generation (and then
345132718Skan   immediately becomes invalid).  However, we do have to make sure
346169689Skan   to read tls_keys.generation[key] atomically.  */
347132718Skan
348132718Skanint
349132718Skan__gthread_setspecific (__gthread_key_t key, void *value)
350132718Skan{
351169689Skan  struct tls_data *data;
352132718Skan  unsigned int generation;
353132718Skan
354132718Skan  if (key >= MAX_KEYS)
355132718Skan    return EINVAL;
356132718Skan
357169689Skan  data = __gthread_get_tls_data ();
358132718Skan  if (!data)
359132718Skan    {
360169689Skan      if (__gthread_mutex_lock (&tls_lock) == ERROR)
361169689Skan	return ENOMEM;
362169689Skan      if (active_tls_threads == 0)
363169689Skan	taskDeleteHookAdd ((FUNCPTR)tls_delete_hook);
364169689Skan      active_tls_threads++;
365169689Skan      __gthread_mutex_unlock (&tls_lock);
366169689Skan
367169689Skan      data = malloc (sizeof (struct tls_data));
368132718Skan      if (!data)
369132718Skan	return ENOMEM;
370132718Skan
371169689Skan      memset (data, 0, sizeof (struct tls_data));
372169689Skan      data->owner = &self_owner;
373169689Skan      __gthread_set_tls_data (data);
374132718Skan    }
375132718Skan
376169689Skan  generation = tls_keys.generation[key];
377132718Skan
378132718Skan  if (generation & 1)
379132718Skan    return EINVAL;
380132718Skan
381132718Skan  data->generation[key] = generation;
382132718Skan  data->values[key] = value;
383132718Skan
384132718Skan  return 0;
385132718Skan}
386169689Skan#endif /* __GTHREADS */
387