1/* Copyright (C) 2002-2015 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 3, 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 16Under Section 7 of GPL version 3, you are granted additional 17permissions described in the GCC Runtime Library Exception, version 183.1, as published by the Free Software Foundation. 19 20You should have received a copy of the GNU General Public License and 21a copy of the GCC Runtime Library Exception along with this program; 22see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23<http://www.gnu.org/licenses/>. */ 24 25/* Threads compatibility routines for libgcc2 for VxWorks. 26 These are out-of-line routines called from gthr-vxworks.h. 27 28 This file provides the TLS related support routines, calling specific 29 VxWorks kernel entry points for this purpose. The base VxWorks 5.x kernels 30 don't feature these entry points, and we provide gthr_supp_vxw_5x.c as an 31 option to fill this gap. Asking users to rebuild a kernel is not to be 32 taken lightly, still, so we have isolated these routines from the rest of 33 vxlib to ensure that the kernel dependencies are only dragged when really 34 necessary. */ 35 36#include "tconfig.h" 37#include "tsystem.h" 38#include "gthr.h" 39 40#if defined(__GTHREADS) 41#include <vxWorks.h> 42#ifndef __RTP__ 43#include <vxLib.h> 44#endif 45#include <taskLib.h> 46#ifndef __RTP__ 47#include <taskHookLib.h> 48#else 49# include <errno.h> 50#endif 51 52/* Thread-local storage. 53 54 We reserve a field in the TCB to point to a dynamically allocated 55 array which is used to store TLS values. A TLS key is simply an 56 offset in this array. The exact location of the TCB field is not 57 known to this code nor to vxlib.c -- all access to it indirects 58 through the routines __gthread_get_tls_data and 59 __gthread_set_tls_data, which are provided by the VxWorks kernel. 60 61 There is also a global array which records which keys are valid and 62 which have destructors. 63 64 A task delete hook is installed to execute key destructors. The 65 routines __gthread_enter_tls_dtor_context and 66 __gthread_leave_tls_dtor_context, which are also provided by the 67 kernel, ensure that it is safe to call free() on memory allocated 68 by the task being deleted. (This is a no-op on VxWorks 5, but 69 a major undertaking on AE.) 70 71 The task delete hook is only installed when at least one thread 72 has TLS data. This is a necessary precaution, to allow this module 73 to be unloaded - a module with a hook can not be removed. 74 75 Since this interface is used to allocate only a small number of 76 keys, the table size is small and static, which simplifies the 77 code quite a bit. Revisit this if and when it becomes necessary. */ 78 79#define MAX_KEYS 4 80 81/* This is the structure pointed to by the pointer returned 82 by __gthread_get_tls_data. */ 83struct tls_data 84{ 85 int *owner; 86 void *values[MAX_KEYS]; 87 unsigned int generation[MAX_KEYS]; 88}; 89 90/* To make sure we only delete TLS data associated with this object, 91 include a pointer to a local variable in the TLS data object. */ 92static int self_owner; 93 94/* Flag to check whether the delete hook is installed. Once installed 95 it is only removed when unloading this module. */ 96static volatile int delete_hook_installed; 97 98/* kernel provided routines */ 99extern void *__gthread_get_tls_data (void); 100extern void __gthread_set_tls_data (void *data); 101 102extern void __gthread_enter_tls_dtor_context (void); 103extern void __gthread_leave_tls_dtor_context (void); 104 105#ifndef __RTP__ 106 107extern void *__gthread_get_tsd_data (WIND_TCB *tcb); 108extern void __gthread_set_tsd_data (WIND_TCB *tcb, void *data); 109extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb); 110extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb); 111 112#endif /* __RTP__ */ 113 114/* This is a global structure which records all of the active keys. 115 116 A key is potentially valid (i.e. has been handed out by 117 __gthread_key_create) iff its generation count in this structure is 118 even. In that case, the matching entry in the dtors array is a 119 routine to be called when a thread terminates with a valid, 120 non-NULL specific value for that key. 121 122 A key is actually valid in a thread T iff the generation count 123 stored in this structure is equal to the generation count stored in 124 T's specific-value structure. */ 125 126typedef void (*tls_dtor) (void *); 127 128struct tls_keys 129{ 130 tls_dtor dtor[MAX_KEYS]; 131 unsigned int generation[MAX_KEYS]; 132}; 133 134#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1) 135 136/* Note: if MAX_KEYS is increased, this initializer must be updated 137 to match. All the generation counts begin at 1, which means no 138 key is valid. */ 139static struct tls_keys tls_keys = 140{ 141 { 0, 0, 0, 0 }, 142 { 1, 1, 1, 1 } 143}; 144 145/* This lock protects the tls_keys structure. */ 146static __gthread_mutex_t tls_lock; 147 148static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT; 149 150/* Internal routines. */ 151 152/* The task TCB has just been deleted. Call the destructor 153 function for each TLS key that has both a destructor and 154 a non-NULL specific value in this thread. 155 156 This routine does not need to take tls_lock; the generation 157 count protects us from calling a stale destructor. It does 158 need to read tls_keys.dtor[key] atomically. */ 159 160static void 161tls_delete_hook (void *tcb ATTRIBUTE_UNUSED) 162{ 163 struct tls_data *data; 164 __gthread_key_t key; 165 166#ifdef __RTP__ 167 data = __gthread_get_tls_data (); 168#else 169 /* In kernel mode, we can be called in the context of the thread 170 doing the killing, so must use the TCB to determine the data of 171 the thread being killed. */ 172 data = __gthread_get_tsd_data (tcb); 173#endif 174 175 if (data && data->owner == &self_owner) 176 { 177#ifdef __RTP__ 178 __gthread_enter_tls_dtor_context (); 179#else 180 __gthread_enter_tsd_dtor_context (tcb); 181#endif 182 for (key = 0; key < MAX_KEYS; key++) 183 { 184 if (data->generation[key] == tls_keys.generation[key]) 185 { 186 tls_dtor dtor = tls_keys.dtor[key]; 187 188 if (dtor) 189 dtor (data->values[key]); 190 } 191 } 192 free (data); 193#ifdef __RTP__ 194 __gthread_leave_tls_dtor_context (); 195#else 196 __gthread_leave_tsd_dtor_context (tcb); 197#endif 198 199#ifdef __RTP__ 200 __gthread_set_tls_data (0); 201#else 202 __gthread_set_tsd_data (tcb, 0); 203#endif 204 } 205} 206 207/* Initialize global data used by the TLS system. */ 208static void 209tls_init (void) 210{ 211 __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock); 212} 213 214static void tls_destructor (void) __attribute__ ((destructor)); 215static void 216tls_destructor (void) 217{ 218#ifdef __RTP__ 219 /* All threads but this one should have exited by now. */ 220 tls_delete_hook (NULL); 221#endif 222 /* Unregister the hook. */ 223 if (delete_hook_installed) 224 taskDeleteHookDelete ((FUNCPTR)tls_delete_hook); 225 226 if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR) 227 semDelete (tls_lock); 228} 229 230/* External interface */ 231 232/* Store in KEYP a value which can be passed to __gthread_setspecific/ 233 __gthread_getspecific to store and retrieve a value which is 234 specific to each calling thread. If DTOR is not NULL, it will be 235 called when a thread terminates with a non-NULL specific value for 236 this key, with the value as its sole argument. */ 237 238int 239__gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor) 240{ 241 __gthread_key_t key; 242 243 __gthread_once (&tls_init_guard, tls_init); 244 245 if (__gthread_mutex_lock (&tls_lock) == ERROR) 246 return errno; 247 248 for (key = 0; key < MAX_KEYS; key++) 249 if (!KEY_VALID_P (key)) 250 goto found_slot; 251 252 /* no room */ 253 __gthread_mutex_unlock (&tls_lock); 254 return EAGAIN; 255 256 found_slot: 257 tls_keys.generation[key]++; /* making it even */ 258 tls_keys.dtor[key] = dtor; 259 *keyp = key; 260 __gthread_mutex_unlock (&tls_lock); 261 return 0; 262} 263 264/* Invalidate KEY; it can no longer be used as an argument to 265 setspecific/getspecific. Note that this does NOT call destructor 266 functions for any live values for this key. */ 267int 268__gthread_key_delete (__gthread_key_t key) 269{ 270 if (key >= MAX_KEYS) 271 return EINVAL; 272 273 __gthread_once (&tls_init_guard, tls_init); 274 275 if (__gthread_mutex_lock (&tls_lock) == ERROR) 276 return errno; 277 278 if (!KEY_VALID_P (key)) 279 { 280 __gthread_mutex_unlock (&tls_lock); 281 return EINVAL; 282 } 283 284 tls_keys.generation[key]++; /* making it odd */ 285 tls_keys.dtor[key] = 0; 286 287 __gthread_mutex_unlock (&tls_lock); 288 return 0; 289} 290 291/* Retrieve the thread-specific value for KEY. If it has never been 292 set in this thread, or KEY is invalid, returns NULL. 293 294 It does not matter if this function races with key_create or 295 key_delete; the worst that can happen is you get a value other than 296 the one that a serialized implementation would have provided. */ 297 298void * 299__gthread_getspecific (__gthread_key_t key) 300{ 301 struct tls_data *data; 302 303 if (key >= MAX_KEYS) 304 return 0; 305 306 data = __gthread_get_tls_data (); 307 308 if (!data) 309 return 0; 310 311 if (data->generation[key] != tls_keys.generation[key]) 312 return 0; 313 314 return data->values[key]; 315} 316 317/* Set the thread-specific value for KEY. If KEY is invalid, or 318 memory allocation fails, returns -1, otherwise 0. 319 320 The generation count protects this function against races with 321 key_create/key_delete; the worst thing that can happen is that a 322 value is successfully stored into a dead generation (and then 323 immediately becomes invalid). However, we do have to make sure 324 to read tls_keys.generation[key] atomically. */ 325 326int 327__gthread_setspecific (__gthread_key_t key, void *value) 328{ 329 struct tls_data *data; 330 unsigned int generation; 331 332 if (key >= MAX_KEYS) 333 return EINVAL; 334 335 data = __gthread_get_tls_data (); 336 if (!data) 337 { 338 if (!delete_hook_installed) 339 { 340 /* Install the delete hook. */ 341 if (__gthread_mutex_lock (&tls_lock) == ERROR) 342 return ENOMEM; 343 if (!delete_hook_installed) 344 { 345 taskDeleteHookAdd ((FUNCPTR)tls_delete_hook); 346 delete_hook_installed = 1; 347 } 348 __gthread_mutex_unlock (&tls_lock); 349 } 350 351 data = malloc (sizeof (struct tls_data)); 352 if (!data) 353 return ENOMEM; 354 355 memset (data, 0, sizeof (struct tls_data)); 356 data->owner = &self_owner; 357 __gthread_set_tls_data (data); 358 } 359 360 generation = tls_keys.generation[key]; 361 362 if (generation & 1) 363 return EINVAL; 364 365 data->generation[key] = generation; 366 data->values[key] = value; 367 368 return 0; 369} 370#endif /* __GTHREADS */ 371