00001 /* Copyright (C) 2002 Free Software Foundation, Inc. 00002 Contributed by Zack Weinberg <zack@codesourcery.com> 00003 00004 This file is part of GCC. 00005 00006 GCC is free software; you can redistribute it and/or modify it under 00007 the terms of the GNU General Public License as published by the Free 00008 Software Foundation; either version 2, or (at your option) any later 00009 version. 00010 00011 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 00012 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00013 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 00014 for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with GCC; see the file COPYING. If not, write to the Free 00018 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 00019 02111-1307, USA. */ 00020 00021 /* Threads compatibility routines for libgcc2 for VxWorks. 00022 These are out-of-line routines called from gthr-vxworks.h. */ 00023 00024 /* As a special exception, if you link this library with other files, 00025 some of which are compiled with GCC, to produce an executable, 00026 this library does not by itself cause the resulting executable 00027 to be covered by the GNU General Public License. 00028 This exception does not however invalidate any other reasons why 00029 the executable file might be covered by the GNU General Public License. */ 00030 00031 #include "tconfig.h" 00032 #include "tsystem.h" 00033 #include "gthr.h" 00034 00035 #include <vxWorks.h> 00036 #include <vxLib.h> 00037 #include <taskLib.h> 00038 #include <taskHookLib.h> 00039 00040 /* Init-once operation. 00041 00042 This would be a clone of the implementation from gthr-solaris.h, 00043 except that we have a bootstrap problem - the whole point of this 00044 exercise is to prevent double initialization, but if two threads 00045 are racing with each other, once->mutex is liable to be initialized 00046 by both. Then each thread will lock its own mutex, and proceed to 00047 call the initialization routine. 00048 00049 So instead we use a bare atomic primitive (vxTas()) to handle 00050 mutual exclusion. Threads losing the race then busy-wait, calling 00051 taskDelay() to yield the processor, until the initialization is 00052 completed. Inefficient, but reliable. */ 00053 00054 int 00055 __gthread_once (__gthread_once_t *guard, void (*func)(void)) 00056 { 00057 if (guard->done) 00058 return 0; 00059 00060 while (!vxTas ((void *)&guard->busy)) 00061 taskDelay (1); 00062 00063 /* Only one thread at a time gets here. Check ->done again, then 00064 go ahead and call func() if no one has done it yet. */ 00065 if (!guard->done) 00066 { 00067 func (); 00068 guard->done = 1; 00069 } 00070 00071 guard->busy = 0; 00072 return 0; 00073 } 00074 00075 /* Thread-specific data. 00076 00077 We reserve a field in the TCB to point to a dynamically allocated 00078 array which is used to store TSD values. A TSD key is simply an 00079 offset in this array. The exact location of the TCB field is not 00080 known to this code nor to vxlib.c -- all access to it indirects 00081 through the routines __gthread_get_tsd_data and 00082 __gthread_set_tsd_data, which are provided by the VxWorks kernel. 00083 00084 There is also a global array which records which keys are valid and 00085 which have destructors. 00086 00087 A task delete hook is installed to execute key destructors. The 00088 routines __gthread_enter_tsd_dtor_context and 00089 __gthread_leave_tsd_dtor_context, which are also provided by the 00090 kernel, ensure that it is safe to call free() on memory allocated 00091 by the task being deleted. (This is a no-op on VxWorks 5, but 00092 a major undertaking on AE.) 00093 00094 Since this interface is used to allocate only a small number of 00095 keys, the table size is small and static, which simplifies the 00096 code quite a bit. Revisit this if and when it becomes necessary. */ 00097 00098 #define MAX_KEYS 4 00099 00100 /* This is the structure pointed to by the pointer returned 00101 by __gthread_get_tsd_data. */ 00102 struct tsd_data 00103 { 00104 void *values[MAX_KEYS]; 00105 unsigned int generation[MAX_KEYS]; 00106 }; 00107 00108 00109 /* kernel provided routines */ 00110 extern void *__gthread_get_tsd_data (WIND_TCB *tcb); 00111 extern void __gthread_set_tsd_data (WIND_TCB *tcb, void *data); 00112 00113 extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb); 00114 extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb); 00115 00116 typedef void (*fet_callback_t) (WIND_TCB *, unsigned int); 00117 extern void __gthread_for_all_tasks (fet_callback_t fun, unsigned int number); 00118 00119 /* This is a global structure which records all of the active keys. 00120 00121 A key is potentially valid (i.e. has been handed out by 00122 __gthread_key_create) iff its generation count in this structure is 00123 even. In that case, the matching entry in the dtors array is a 00124 routine to be called when a thread terminates with a valid, 00125 non-NULL specific value for that key. 00126 00127 A key is actually valid in a thread T iff the generation count 00128 stored in this structure is equal to the generation count stored in 00129 T's specific-value structure. */ 00130 00131 typedef void (*tsd_dtor) (void *); 00132 00133 struct tsd_keys 00134 { 00135 tsd_dtor dtor[MAX_KEYS]; 00136 unsigned int generation[MAX_KEYS]; 00137 }; 00138 00139 #define KEY_VALID_P(key) !(tsd_keys.generation[key] & 1) 00140 00141 /* Note: if MAX_KEYS is increased, this initializer must be updated 00142 to match. All the generation counts begin at 1, which means no 00143 key is valid. */ 00144 static struct tsd_keys tsd_keys = 00145 { 00146 { 0, 0, 0, 0 }, 00147 { 1, 1, 1, 1 } 00148 }; 00149 00150 /* This lock protects the tsd_keys structure. */ 00151 static __gthread_mutex_t tsd_lock; 00152 00153 static __gthread_once_t tsd_init_guard = __GTHREAD_ONCE_INIT; 00154 00155 /* Internal routines. */ 00156 00157 /* The task TCB has just been deleted. Call the destructor 00158 function for each TSD key that has both a destructor and 00159 a non-NULL specific value in this thread. 00160 00161 This routine does not need to take tsd_lock; the generation 00162 count protects us from calling a stale destructor. It does 00163 need to read tsd_keys.dtor[key] atomically. */ 00164 00165 static void 00166 tsd_delete_hook (WIND_TCB *tcb) 00167 { 00168 struct tsd_data *data = __gthread_get_tsd_data (tcb); 00169 __gthread_key_t key; 00170 00171 if (data) 00172 { 00173 __gthread_enter_tsd_dtor_context (tcb); 00174 for (key = 0; key < MAX_KEYS; key++) 00175 { 00176 if (data->generation[key] == tsd_keys.generation[key]) 00177 { 00178 tsd_dtor dtor = tsd_keys.dtor[key]; 00179 00180 if (dtor) 00181 dtor (data->values[key]); 00182 } 00183 } 00184 free (data); 00185 __gthread_set_tsd_data (tcb, 0); 00186 __gthread_leave_tsd_dtor_context (tcb); 00187 } 00188 } 00189 00190 /* Initialize global data used by the TSD system. */ 00191 static void 00192 tsd_init (void) 00193 { 00194 taskDeleteHookAdd ((FUNCPTR)tsd_delete_hook); 00195 __GTHREAD_MUTEX_INIT_FUNCTION (&tsd_lock); 00196 } 00197 00198 /* External interface */ 00199 00200 /* Store in KEYP a value which can be passed to __gthread_setspecific/ 00201 __gthread_getspecific to store and retrieve a value which is 00202 specific to each calling thread. If DTOR is not NULL, it will be 00203 called when a thread terminates with a non-NULL specific value for 00204 this key, with the value as its sole argument. */ 00205 00206 int 00207 __gthread_key_create (__gthread_key_t *keyp, tsd_dtor dtor) 00208 { 00209 __gthread_key_t key; 00210 00211 __gthread_once (&tsd_init_guard, tsd_init); 00212 00213 if (__gthread_mutex_lock (&tsd_lock) == ERROR) 00214 return errno; 00215 00216 for (key = 0; key < MAX_KEYS; key++) 00217 if (!KEY_VALID_P (key)) 00218 goto found_slot; 00219 00220 /* no room */ 00221 __gthread_mutex_unlock (&tsd_lock); 00222 return EAGAIN; 00223 00224 found_slot: 00225 tsd_keys.generation[key]++; /* making it even */ 00226 tsd_keys.dtor[key] = dtor; 00227 *keyp = key; 00228 __gthread_mutex_unlock (&tsd_lock); 00229 return 0; 00230 } 00231 00232 /* Invalidate KEY; it can no longer be used as an argument to 00233 setspecific/getspecific. Note that this does NOT call destructor 00234 functions for any live values for this key. */ 00235 int 00236 __gthread_key_delete (__gthread_key_t key) 00237 { 00238 if (key >= MAX_KEYS) 00239 return EINVAL; 00240 00241 __gthread_once (&tsd_init_guard, tsd_init); 00242 00243 if (__gthread_mutex_lock (&tsd_lock) == ERROR) 00244 return errno; 00245 00246 if (!KEY_VALID_P (key)) 00247 { 00248 __gthread_mutex_unlock (&tsd_lock); 00249 return EINVAL; 00250 } 00251 00252 tsd_keys.generation[key]++; /* making it odd */ 00253 tsd_keys.dtor[key] = 0; 00254 00255 __gthread_mutex_unlock (&tsd_lock); 00256 return 0; 00257 } 00258 00259 /* Retrieve the thread-specific value for KEY. If it has never been 00260 set in this thread, or KEY is invalid, returns NULL. 00261 00262 It does not matter if this function races with key_create or 00263 key_delete; the worst that can happen is you get a value other than 00264 the one that a serialized implementation would have provided. */ 00265 00266 void * 00267 __gthread_getspecific (__gthread_key_t key) 00268 { 00269 struct tsd_data *data; 00270 00271 if (key >= MAX_KEYS) 00272 return 0; 00273 00274 data = __gthread_get_tsd_data (taskTcb (taskIdSelf ())); 00275 00276 if (!data) 00277 return 0; 00278 00279 if (data->generation[key] != tsd_keys.generation[key]) 00280 return 0; 00281 00282 return data->values[key]; 00283 } 00284 00285 /* Set the thread-specific value for KEY. If KEY is invalid, or 00286 memory allocation fails, returns -1, otherwise 0. 00287 00288 The generation count protects this function against races with 00289 key_create/key_delete; the worst thing that can happen is that a 00290 value is successfully stored into a dead generation (and then 00291 immediately becomes invalid). However, we do have to make sure 00292 to read tsd_keys.generation[key] atomically. */ 00293 00294 int 00295 __gthread_setspecific (__gthread_key_t key, void *value) 00296 { 00297 struct tsd_data *data; 00298 WIND_TCB *tcb; 00299 unsigned int generation; 00300 00301 if (key >= MAX_KEYS) 00302 return EINVAL; 00303 00304 tcb = taskTcb (taskIdSelf ()); 00305 data = __gthread_get_tsd_data (tcb); 00306 if (!data) 00307 { 00308 data = malloc (sizeof (struct tsd_data)); 00309 if (!data) 00310 return ENOMEM; 00311 00312 memset (data, 0, sizeof (struct tsd_data)); 00313 __gthread_set_tsd_data (tcb, data); 00314 } 00315 00316 generation = tsd_keys.generation[key]; 00317 00318 if (generation & 1) 00319 return EINVAL; 00320 00321 data->generation[key] = generation; 00322 data->values[key] = value; 00323 00324 return 0; 00325 }
1.5.6