]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/libpthread/src/specific.c
update
[l4.git] / l4 / pkg / uclibc / lib / libpthread / src / specific.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program is distributed in the hope that it will be useful,      */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
13 /* GNU Library General Public License for more details.                 */
14
15 /* Thread-specific data */
16
17 #include <errno.h>
18 #include <stddef.h>
19 #include <stdlib.h>
20 #include "pthread.h"
21 #include "internals.h"
22 #include "spinlock.h"
23 #include "restart.h"
24 #include <bits/libc-lock.h>
25
26 #include <stdio.h>
27
28 /* Table of keys. */
29
30 static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] =
31   { { 0, NULL } };
32
33 /* For debugging purposes put the maximum number of keys in a variable.  */
34 const int __linuxthreads_pthread_keys_max = PTHREAD_KEYS_MAX;
35 const int __linuxthreads_pthread_key_2ndlevel_size = PTHREAD_KEY_2NDLEVEL_SIZE;
36
37 /* Mutex to protect access to pthread_keys */
38
39 static pthread_mutex_t pthread_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
40
41 /* Create a new key */
42
43 int
44 attribute_hidden
45 __pthread_key_create(pthread_key_t * key, destr_function destr)
46 {
47   int i;
48
49   pthread_mutex_lock(&pthread_keys_mutex);
50   for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
51     if (! pthread_keys[i].in_use) {
52       /* Mark key in use */
53       pthread_keys[i].in_use = 1;
54       pthread_keys[i].destr = destr;
55       pthread_mutex_unlock(&pthread_keys_mutex);
56       *key = i;
57       return 0;
58     }
59   }
60   pthread_mutex_unlock(&pthread_keys_mutex);
61   return EAGAIN;
62 }
63 strong_alias (__pthread_key_create, pthread_key_create)
64
65 /* Reset deleted key's value to NULL in each live thread.
66  * NOTE: this executes in the context of the thread manager! */
67
68 struct pthread_key_delete_helper_args {
69   /* Damn, we need lexical closures in C! ;) */
70   unsigned int idx1st, idx2nd;
71   pthread_descr self;
72 };
73
74 static void pthread_key_delete_helper(void *arg, pthread_descr th)
75 {
76   struct pthread_key_delete_helper_args *args = arg;
77   unsigned int idx1st = args->idx1st;
78   unsigned int idx2nd = args->idx2nd;
79   pthread_descr self = args->self;
80
81   if (self == 0)
82     self = args->self = thread_self();
83
84   if (!th->p_terminated) {
85     /* pthread_exit() may try to free th->p_specific[idx1st] concurrently. */
86     __pthread_lock(th->p_lock, self);
87     if (th->p_specific[idx1st] != NULL)
88       th->p_specific[idx1st][idx2nd] = NULL;
89     __pthread_unlock(th->p_lock);
90   }
91 }
92
93 /* Delete a key */
94 int pthread_key_delete(pthread_key_t key)
95 {
96   pthread_descr self = thread_self();
97
98   pthread_mutex_lock(&pthread_keys_mutex);
99   if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) {
100     pthread_mutex_unlock(&pthread_keys_mutex);
101     return EINVAL;
102   }
103   pthread_keys[key].in_use = 0;
104   pthread_keys[key].destr = NULL;
105
106   /* Set the value of the key to NULL in all running threads, so
107      that if the key is reallocated later by pthread_key_create, its
108      associated values will be NULL in all threads.
109
110      If no threads have been created yet, or if we are exiting, clear
111      it just in the current thread.  */
112
113   struct pthread_key_delete_helper_args args;
114   args.idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
115   args.idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
116   if (!l4_is_invalid_cap(__pthread_manager_request)
117       && !(__builtin_expect (__pthread_exit_requested, 0)))
118     {
119       struct pthread_request request;
120
121       args.self = 0;
122
123       request.req_thread = self;
124       request.req_kind = REQ_FOR_EACH_THREAD;
125       request.req_args.for_each.arg = &args;
126       request.req_args.for_each.fn = pthread_key_delete_helper;
127 #if 1
128       __pthread_send_manager_rq(&request, 1);
129 #else
130       TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
131                                           (char *) &request, sizeof(request)));
132 #endif
133       suspend(self);
134     }
135   else
136     {
137       if (self->p_specific[args.idx1st] != NULL)
138         self->p_specific[args.idx1st][args.idx2nd] = NULL;
139     }
140
141   pthread_mutex_unlock(&pthread_keys_mutex);
142   return 0;
143 }
144
145 /* Set the value of a key */
146
147 int
148 attribute_hidden
149 __pthread_setspecific(pthread_key_t key, const void * pointer)
150 {
151   pthread_descr self = thread_self();
152   unsigned int idx1st, idx2nd;
153
154   if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use)
155     return EINVAL;
156   idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
157   idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
158   if (THREAD_GETMEM_NC(self, p_specific, idx1st) == NULL) {
159     void *newp = calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *));
160     if (newp == NULL)
161       return ENOMEM;
162     THREAD_SETMEM_NC(self, p_specific ,idx1st, newp);
163   }
164   THREAD_GETMEM_NC(self, p_specific, idx1st)[idx2nd] = (void *) pointer;
165   return 0;
166 }
167 strong_alias (__pthread_setspecific, pthread_setspecific)
168
169 /* Get the value of a key */
170
171 attribute_hidden
172 void *
173 __pthread_getspecific(pthread_key_t key)
174 {
175   pthread_descr self = thread_self();
176   unsigned int idx1st, idx2nd;
177
178   if (key >= PTHREAD_KEYS_MAX)
179     return NULL;
180   idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
181   idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
182   if (THREAD_GETMEM_NC(self, p_specific, idx1st) == NULL
183       || !pthread_keys[key].in_use)
184     return NULL;
185   return THREAD_GETMEM_NC(self, p_specific, idx1st)[idx2nd];
186 }
187 strong_alias (__pthread_getspecific, pthread_getspecific)
188
189 /* Call the destruction routines on all keys */
190
191 void
192 attribute_hidden
193 __pthread_destroy_specifics()
194 {
195   pthread_descr self = thread_self();
196   int i, j, round, found_nonzero;
197   destr_function destr;
198   void * data;
199
200   for (round = 0, found_nonzero = 1;
201        found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
202        round++) {
203     found_nonzero = 0;
204     for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
205       if (THREAD_GETMEM_NC(self, p_specific, i) != NULL)
206         for (j = 0; j < PTHREAD_KEY_2NDLEVEL_SIZE; j++) {
207           destr = pthread_keys[i * PTHREAD_KEY_2NDLEVEL_SIZE + j].destr;
208           data = THREAD_GETMEM_NC(self, p_specific, i)[j];
209           if (destr != NULL && data != NULL) {
210             THREAD_GETMEM_NC(self, p_specific, i)[j] = NULL;
211             destr(data);
212             found_nonzero = 1;
213           }
214         }
215   }
216   __pthread_lock(THREAD_GETMEM(self, p_lock), self);
217   for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
218     if (THREAD_GETMEM_NC(self, p_specific, i) != NULL) {
219       void *p = THREAD_GETMEM_NC(self, p_specific, i);
220       THREAD_SETMEM_NC(self, p_specific, i, NULL);
221       free(p);
222     }
223   }
224   __pthread_unlock(THREAD_GETMEM(self, p_lock));
225 }
226
227 #if !(USE_TLS && HAVE___THREAD)
228
229 /* Thread-specific data for libc. */
230
231 int
232 attribute_hidden
233 __pthread_internal_tsd_set (int key, const void * pointer)
234 {
235   pthread_descr self = thread_self();
236
237   THREAD_SETMEM_NC(self, p_libc_specific, key, (void *) pointer);
238   return 0;
239 }
240
241 void *
242 attribute_hidden
243 __pthread_internal_tsd_get (int key)
244 {
245   pthread_descr self = thread_self();
246
247   return THREAD_GETMEM_NC(self, p_libc_specific, key);
248 }
249
250 void ** __attribute__ ((__const__))
251 attribute_hidden
252 __pthread_internal_tsd_address (int key)
253 {
254   pthread_descr self = thread_self();
255   return &self->p_libc_specific[key];
256 }
257
258 #endif