1 /* Linuxthreads - a simple clone()-based implementation of Posix */
2 /* threads for Linux. */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
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. */
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. */
15 /* Thread-specific data */
21 #include "internals.h"
24 #include <bits/libc-lock.h>
29 static struct pthread_key_struct pthread_keys
[PTHREAD_KEYS_MAX
] =
32 /* For debugging purposes put the maximum number of keys in a variable. */
33 const int __linuxthreads_pthread_keys_max
= PTHREAD_KEYS_MAX
;
34 const int __linuxthreads_pthread_key_2ndlevel_size
= PTHREAD_KEY_2NDLEVEL_SIZE
;
36 /* Mutex to protect access to pthread_keys */
38 static pthread_mutex_t pthread_keys_mutex
= PTHREAD_MUTEX_INITIALIZER
;
40 /* Create a new key */
42 int __pthread_key_create(pthread_key_t
* key
, destr_function destr
)
46 pthread_mutex_lock(&pthread_keys_mutex
);
47 for (i
= 0; i
< PTHREAD_KEYS_MAX
; i
++) {
48 if (! pthread_keys
[i
].in_use
) {
50 pthread_keys
[i
].in_use
= 1;
51 pthread_keys
[i
].destr
= destr
;
52 pthread_mutex_unlock(&pthread_keys_mutex
);
57 pthread_mutex_unlock(&pthread_keys_mutex
);
60 strong_alias (__pthread_key_create
, pthread_key_create
)
62 /* Reset deleted key's value to NULL in each live thread.
63 * NOTE: this executes in the context of the thread manager! */
65 struct pthread_key_delete_helper_args
{
66 /* Damn, we need lexical closures in C! ;) */
67 unsigned int idx1st
, idx2nd
;
71 static void pthread_key_delete_helper(void *arg
, pthread_descr th
)
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
;
79 self
= args
->self
= thread_self();
81 if (!th
->p_terminated
) {
82 /* pthread_exit() may try to free th->p_specific[idx1st] concurrently. */
83 __pthread_lock(THREAD_GETMEM(th
, p_lock
), self
);
84 if (th
->p_specific
[idx1st
] != NULL
)
85 th
->p_specific
[idx1st
][idx2nd
] = NULL
;
86 __pthread_unlock(THREAD_GETMEM(th
, p_lock
));
91 int pthread_key_delete(pthread_key_t key
)
93 pthread_descr self
= thread_self();
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
);
100 pthread_keys
[key
].in_use
= 0;
101 pthread_keys
[key
].destr
= NULL
;
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.
107 Do nothing if no threads have been created yet. */
109 if (__pthread_manager_request
!= -1)
111 struct pthread_key_delete_helper_args args
;
112 struct pthread_request request
;
114 args
.idx1st
= key
/ PTHREAD_KEY_2NDLEVEL_SIZE
;
115 args
.idx2nd
= key
% PTHREAD_KEY_2NDLEVEL_SIZE
;
118 request
.req_thread
= self
;
119 request
.req_kind
= REQ_FOR_EACH_THREAD
;
120 request
.req_args
.for_each
.arg
= &args
;
121 request
.req_args
.for_each
.fn
= pthread_key_delete_helper
;
123 TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request
,
124 (char *) &request
, sizeof(request
)));
128 pthread_mutex_unlock(&pthread_keys_mutex
);
132 /* Set the value of a key */
134 int __pthread_setspecific(pthread_key_t key
, const void * pointer
)
136 pthread_descr self
= thread_self();
137 unsigned int idx1st
, idx2nd
;
139 if (key
>= PTHREAD_KEYS_MAX
|| !pthread_keys
[key
].in_use
)
141 idx1st
= key
/ PTHREAD_KEY_2NDLEVEL_SIZE
;
142 idx2nd
= key
% PTHREAD_KEY_2NDLEVEL_SIZE
;
143 if (THREAD_GETMEM_NC(self
, p_specific
[idx1st
]) == NULL
) {
144 void *newp
= calloc(PTHREAD_KEY_2NDLEVEL_SIZE
, sizeof (void *));
147 THREAD_SETMEM_NC(self
, p_specific
[idx1st
], newp
);
149 THREAD_GETMEM_NC(self
, p_specific
[idx1st
])[idx2nd
] = (void *) pointer
;
152 strong_alias (__pthread_setspecific
, pthread_setspecific
)
154 /* Get the value of a key */
156 void * __pthread_getspecific(pthread_key_t key
)
158 pthread_descr self
= thread_self();
159 unsigned int idx1st
, idx2nd
;
161 if (key
>= PTHREAD_KEYS_MAX
)
163 idx1st
= key
/ PTHREAD_KEY_2NDLEVEL_SIZE
;
164 idx2nd
= key
% PTHREAD_KEY_2NDLEVEL_SIZE
;
165 if (THREAD_GETMEM_NC(self
, p_specific
[idx1st
]) == NULL
166 || !pthread_keys
[key
].in_use
)
168 return THREAD_GETMEM_NC(self
, p_specific
[idx1st
])[idx2nd
];
170 strong_alias (__pthread_getspecific
, pthread_getspecific
)
172 /* Call the destruction routines on all keys */
174 void __pthread_destroy_specifics()
176 pthread_descr self
= thread_self();
177 int i
, j
, round
, found_nonzero
;
178 destr_function destr
;
181 for (round
= 0, found_nonzero
= 1;
182 found_nonzero
&& round
< PTHREAD_DESTRUCTOR_ITERATIONS
;
185 for (i
= 0; i
< PTHREAD_KEY_1STLEVEL_SIZE
; i
++)
186 if (THREAD_GETMEM_NC(self
, p_specific
[i
]) != NULL
)
187 for (j
= 0; j
< PTHREAD_KEY_2NDLEVEL_SIZE
; j
++) {
188 destr
= pthread_keys
[i
* PTHREAD_KEY_2NDLEVEL_SIZE
+ j
].destr
;
189 data
= THREAD_GETMEM_NC(self
, p_specific
[i
])[j
];
190 if (destr
!= NULL
&& data
!= NULL
) {
191 THREAD_GETMEM_NC(self
, p_specific
[i
])[j
] = NULL
;
197 __pthread_lock(THREAD_GETMEM(self
, p_lock
), self
);
198 for (i
= 0; i
< PTHREAD_KEY_1STLEVEL_SIZE
; i
++) {
199 if (THREAD_GETMEM_NC(self
, p_specific
[i
]) != NULL
) {
200 free(THREAD_GETMEM_NC(self
, p_specific
[i
]));
201 THREAD_SETMEM_NC(self
, p_specific
[i
], NULL
);
204 __pthread_unlock(THREAD_GETMEM(self
, p_lock
));
207 /* Thread-specific data for libc. */
210 libc_internal_tsd_set(enum __libc_tsd_key_t key
, const void * pointer
)
212 pthread_descr self
= thread_self();
214 THREAD_SETMEM_NC(self
, p_libc_specific
[key
], (void *) pointer
);
217 int (*__libc_internal_tsd_set
)(enum __libc_tsd_key_t key
, const void * pointer
)
218 = libc_internal_tsd_set
;
221 libc_internal_tsd_get(enum __libc_tsd_key_t key
)
223 pthread_descr self
= thread_self();
225 return THREAD_GETMEM_NC(self
, p_libc_specific
[key
]);
227 void * (*__libc_internal_tsd_get
)(enum __libc_tsd_key_t key
)
228 = libc_internal_tsd_get
;