blob: 764bf1e959078bee6480c15c6762b96fce01d3f3 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* 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#include <not-cancel.h>
26
27/* Table of keys. */
28
29static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] =
30 { { 0, NULL } };
31
32/* For debugging purposes put the maximum number of keys in a variable. */
33const int __linuxthreads_pthread_keys_max = PTHREAD_KEYS_MAX;
34const int __linuxthreads_pthread_key_2ndlevel_size = PTHREAD_KEY_2NDLEVEL_SIZE;
35
36/* Mutex to protect access to pthread_keys */
37
38static pthread_mutex_t pthread_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
39
40/* Create a new key */
41
42int __pthread_key_create(pthread_key_t * key, destr_function destr)
43{
44 int i;
45
46 pthread_mutex_lock(&pthread_keys_mutex);
47 for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
48 if (! pthread_keys[i].in_use) {
49 /* Mark key in use */
50 pthread_keys[i].in_use = 1;
51 pthread_keys[i].destr = destr;
52 pthread_mutex_unlock(&pthread_keys_mutex);
53 *key = i;
54 return 0;
55 }
56 }
57 pthread_mutex_unlock(&pthread_keys_mutex);
58 return EAGAIN;
59}
60strong_alias (__pthread_key_create, pthread_key_create)
61
62/* Reset deleted key's value to NULL in each live thread.
63 * NOTE: this executes in the context of the thread manager! */
64
65struct pthread_key_delete_helper_args {
66 /* Damn, we need lexical closures in C! ;) */
67 unsigned int idx1st, idx2nd;
68 pthread_descr self;
69};
70
71static void pthread_key_delete_helper(void *arg, pthread_descr th)
72{
73 struct pthread_key_delete_helper_args *args = arg;
74 unsigned int idx1st = args->idx1st;
75 unsigned int idx2nd = args->idx2nd;
76 pthread_descr self = args->self;
77
78 if (self == 0)
79 self = args->self = thread_self();
80
81 if (!th->p_terminated) {
82 /* pthread_exit() may try to free th->p_specific[idx1st] concurrently. */
83 __pthread_lock(th->p_lock, self);
84 if (th->p_specific[idx1st] != NULL)
85 th->p_specific[idx1st][idx2nd] = NULL;
86 __pthread_unlock(th->p_lock);
87 }
88}
89
90/* Delete a key */
91int pthread_key_delete(pthread_key_t key)
92{
93 pthread_descr self = thread_self();
94
95 pthread_mutex_lock(&pthread_keys_mutex);
96 if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) {
97 pthread_mutex_unlock(&pthread_keys_mutex);
98 return EINVAL;
99 }
100 pthread_keys[key].in_use = 0;
101 pthread_keys[key].destr = NULL;
102
103 /* Set the value of the key to NULL in all running threads, so
104 that if the key is reallocated later by pthread_key_create, its
105 associated values will be NULL in all threads.
106
107 If no threads have been created yet, or if we are exiting, clear
108 it just in the current thread. */
109
110 struct pthread_key_delete_helper_args args;
111 args.idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
112 args.idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
113 if (__pthread_manager_request != -1
114 && !(__builtin_expect (__pthread_exit_requested, 0)))
115 {
116 struct pthread_request request;
117
118 args.self = 0;
119
120 request.req_thread = self;
121 request.req_kind = REQ_FOR_EACH_THREAD;
122 request.req_args.for_each.arg = &args;
123 request.req_args.for_each.fn = pthread_key_delete_helper;
124
125 TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
126 (char *) &request, sizeof(request)));
127 suspend(self);
128 }
129 else
130 {
131 if (self->p_specific[args.idx1st] != NULL)
132 self->p_specific[args.idx1st][args.idx2nd] = NULL;
133 }
134
135 pthread_mutex_unlock(&pthread_keys_mutex);
136 return 0;
137}
138
139/* Set the value of a key */
140
141int __pthread_setspecific(pthread_key_t key, const void * pointer)
142{
143 pthread_descr self = thread_self();
144 unsigned int idx1st, idx2nd;
145
146 if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use)
147 return EINVAL;
148 idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
149 idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
150 if (THREAD_GETMEM_NC(self, p_specific[idx1st]) == NULL) {
151 void *newp = calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *));
152 if (newp == NULL)
153 return ENOMEM;
154 THREAD_SETMEM_NC(self, p_specific[idx1st], newp);
155 }
156 THREAD_GETMEM_NC(self, p_specific[idx1st])[idx2nd] = (void *) pointer;
157 return 0;
158}
159strong_alias (__pthread_setspecific, pthread_setspecific)
160
161/* Get the value of a key */
162
163void * __pthread_getspecific(pthread_key_t key)
164{
165 pthread_descr self = thread_self();
166 unsigned int idx1st, idx2nd;
167
168 if (key >= PTHREAD_KEYS_MAX)
169 return NULL;
170 idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
171 idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
172 if (THREAD_GETMEM_NC(self, p_specific[idx1st]) == NULL
173 || !pthread_keys[key].in_use)
174 return NULL;
175 return THREAD_GETMEM_NC(self, p_specific[idx1st])[idx2nd];
176}
177strong_alias (__pthread_getspecific, pthread_getspecific)
178
179/* Call the destruction routines on all keys */
180
181void __pthread_destroy_specifics()
182{
183 pthread_descr self = thread_self();
184 int i, j, round, found_nonzero;
185 destr_function destr;
186 void * data;
187
188 for (round = 0, found_nonzero = 1;
189 found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
190 round++) {
191 found_nonzero = 0;
192 for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
193 if (THREAD_GETMEM_NC(self, p_specific[i]) != NULL)
194 for (j = 0; j < PTHREAD_KEY_2NDLEVEL_SIZE; j++) {
195 destr = pthread_keys[i * PTHREAD_KEY_2NDLEVEL_SIZE + j].destr;
196 data = THREAD_GETMEM_NC(self, p_specific[i])[j];
197 if (destr != NULL && data != NULL) {
198 THREAD_GETMEM_NC(self, p_specific[i])[j] = NULL;
199 destr(data);
200 found_nonzero = 1;
201 }
202 }
203 }
204 __pthread_lock(THREAD_GETMEM(self, p_lock), self);
205 for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
206 if (THREAD_GETMEM_NC(self, p_specific[i]) != NULL) {
207 void *p = THREAD_GETMEM_NC(self, p_specific[i]);
208 THREAD_SETMEM_NC(self, p_specific[i], NULL);
209 free(p);
210 }
211 }
212 __pthread_unlock(THREAD_GETMEM(self, p_lock));
213}
214
215#if !(USE_TLS && HAVE___THREAD)
216
217/* Thread-specific data for libc. */
218
219int
220__pthread_internal_tsd_set (int key, const void * pointer)
221{
222 pthread_descr self = thread_self();
223
224 THREAD_SETMEM_NC(self, p_libc_specific[key], (void *) pointer);
225 return 0;
226}
227
228void *
229__pthread_internal_tsd_get (int key)
230{
231 pthread_descr self = thread_self();
232
233 return THREAD_GETMEM_NC(self, p_libc_specific[key]);
234}
235
236void ** __attribute__ ((__const__))
237__pthread_internal_tsd_address (int key)
238{
239 pthread_descr self = thread_self();
240 return &self->p_libc_specific[key];
241}
242
243#endif