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