bpo-30832: Remove own implementation for thread-local storage (#2537) · python/cpython@aa0aa04 (original) (raw)
`@@ -81,14 +81,14 @@ PyThread_init_thread(void)
`
81
81
` or the size specified by the THREAD_STACK_SIZE macro. */
`
82
82
`static size_t _pythread_stacksize = 0;
`
83
83
``
84
``
`-
#ifdef _POSIX_THREADS
`
85
``
`-
#define PYTHREAD_NAME "pthread"
`
86
``
`-
#include "thread_pthread.h"
`
87
``
`-
#endif
`
88
``
-
89
``
`-
#ifdef NT_THREADS
`
90
``
`-
#define PYTHREAD_NAME "nt"
`
91
``
`-
#include "thread_nt.h"
`
``
84
`+
#if defined(_POSIX_THREADS)
`
``
85
`+
define PYTHREAD_NAME "pthread"
`
``
86
`+
include "thread_pthread.h"
`
``
87
`+
#elif defined(NT_THREADS)
`
``
88
`+
define PYTHREAD_NAME "nt"
`
``
89
`+
include "thread_nt.h"
`
``
90
`+
#else
`
``
91
`+
error "Require native thread feature. See https://bugs.python.org/issue30832"
`
92
92
`#endif
`
93
93
``
94
94
``
`@@ -114,13 +114,7 @@ PyThread_set_stacksize(size_t size)
`
114
114
`#endif
`
115
115
`}
`
116
116
``
117
``
`-
#ifndef Py_HAVE_NATIVE_TLS
`
118
``
`-
/* If the platform has not supplied a platform specific
`
119
``
`-
TLS implementation, provide our own.
`
120
117
``
121
``
`-
This code stolen from "thread_sgi.h", where it was the only
`
122
``
`-
implementation of an existing Python TLS API.
`
123
``
`-
*/
`
124
118
`/* ------------------------------------------------------------------------
`
125
119
`Per-thread data ("key") support.
`
126
120
``
`@@ -157,205 +151,6 @@ any of the other functions are called. There's also a hidden assumption
`
157
151
`that calls to PyThread_create_key() are serialized externally.
`
158
152
`------------------------------------------------------------------------ */
`
159
153
``
160
``
`-
/* A singly-linked list of struct key objects remembers all the key->value
`
161
``
`-
- associations. File static keyhead heads the list. keymutex is used
`
162
``
`-
- to enforce exclusion internally.
`
163
``
`-
*/
`
164
``
`-
struct key {
`
165
``
`-
/* Next record in the list, or NULL if this is the last record. */
`
166
``
`-
struct key *next;
`
167
``
-
168
``
`-
/* The thread id, according to PyThread_get_thread_ident(). */
`
169
``
`-
unsigned long id;
`
170
``
-
171
``
`-
/* The key and its associated value. */
`
172
``
`-
int key;
`
173
``
`-
void *value;
`
174
``
`-
};
`
175
``
-
176
``
`-
static struct key *keyhead = NULL;
`
177
``
`-
static PyThread_type_lock keymutex = NULL;
`
178
``
`-
static int nkeys = 0; /* PyThread_create_key() hands out nkeys+1 next */
`
179
``
-
180
``
`-
/* Internal helper.
`
181
``
`-
- If the current thread has a mapping for key, the appropriate struct key*
`
182
``
`-
- is returned. NB: value is ignored in this case!
`
183
``
`-
- If there is no mapping for key in the current thread, then:
`
184
``
`-
- If value is NULL, NULL is returned.
`
185
``
`-
- Else a mapping of key to value is created for the current thread,
`
186
``
`-
- and a pointer to a new struct key* is returned; except that if
`
187
``
`-
- malloc() can't find room for a new struct key*, NULL is returned.
`
188
``
`-
- So when value==NULL, this acts like a pure lookup routine, and when
`
189
``
`-
- value!=NULL, this acts like dict.setdefault(), returning an existing
`
190
``
`-
- mapping if one exists, else creating a new mapping.
`
191
``
`-
`
192
``
`-
- Caution: this used to be too clever, trying to hold keymutex only
`
193
``
`-
- around the "p->next = keyhead; keyhead = p" pair. That allowed
`
194
``
`-
- another thread to mutate the list, via key deletion, concurrent with
`
195
``
`-
- find_key() crawling over the list. Hilarity ensued. For example, when
`
196
``
`-
- the for-loop here does "p = p->next", p could end up pointing at a
`
197
``
`-
- record that PyThread_delete_key_value() was concurrently free()'ing.
`
198
``
`-
- That could lead to anything, from failing to find a key that exists, to
`
199
``
`-
- segfaults. Now we lock the whole routine.
`
200
``
`-
*/
`
201
``
`-
static struct key *
`
202
``
`-
find_key(int set_value, int key, void *value)
`
203
``
`-
{
`
204
``
`-
struct key *p, *prev_p;
`
205
``
`-
unsigned long id = PyThread_get_thread_ident();
`
206
``
-
207
``
`-
if (!keymutex)
`
208
``
`-
return NULL;
`
209
``
`-
PyThread_acquire_lock(keymutex, 1);
`
210
``
`-
prev_p = NULL;
`
211
``
`-
for (p = keyhead; p != NULL; p = p->next) {
`
212
``
`-
if (p->id == id && p->key == key) {
`
213
``
`-
if (set_value)
`
214
``
`-
p->value = value;
`
215
``
`-
goto Done;
`
216
``
`-
}
`
217
``
`-
/* Sanity check. These states should never happen but if
`
218
``
`-
- they do we must abort. Otherwise we'll end up spinning
`
219
``
`-
- in a tight loop with the lock held. A similar check is done
`
220
``
`-
- in pystate.c tstate_delete_common(). */
`
221
``
`-
if (p == prev_p)
`
222
``
`-
Py_FatalError("tls find_key: small circular list(!)");
`
223
``
`-
prev_p = p;
`
224
``
`-
if (p->next == keyhead)
`
225
``
`-
Py_FatalError("tls find_key: circular list(!)");
`
226
``
`-
}
`
227
``
`-
if (!set_value && value == NULL) {
`
228
``
`-
assert(p == NULL);
`
229
``
`-
goto Done;
`
230
``
`-
}
`
231
``
`-
p = (struct key *)PyMem_RawMalloc(sizeof(struct key));
`
232
``
`-
if (p != NULL) {
`
233
``
`-
p->id = id;
`
234
``
`-
p->key = key;
`
235
``
`-
p->value = value;
`
236
``
`-
p->next = keyhead;
`
237
``
`-
keyhead = p;
`
238
``
`-
}
`
239
``
`-
Done:
`
240
``
`-
PyThread_release_lock(keymutex);
`
241
``
`-
return p;
`
242
``
`-
}
`
243
``
-
244
``
`-
/* Return a new key. This must be called before any other functions in
`
245
``
`-
- this family, and callers must arrange to serialize calls to this
`
246
``
`-
- function. No violations are detected.
`
247
``
`-
*/
`
248
``
`-
int
`
249
``
`-
PyThread_create_key(void)
`
250
``
`-
{
`
251
``
`-
/* All parts of this function are wrong if it's called by multiple
`
252
``
`-
- threads simultaneously.
`
253
``
`-
*/
`
254
``
`-
if (keymutex == NULL)
`
255
``
`-
keymutex = PyThread_allocate_lock();
`
256
``
`-
return ++nkeys;
`
257
``
`-
}
`
258
``
-
259
``
`-
/* Forget the associations for key across all threads. */
`
260
``
`-
void
`
261
``
`-
PyThread_delete_key(int key)
`
262
``
`-
{
`
263
``
`-
struct key *p, **q;
`
264
``
-
265
``
`-
PyThread_acquire_lock(keymutex, 1);
`
266
``
`-
q = &keyhead;
`
267
``
`-
while ((p = *q) != NULL) {
`
268
``
`-
if (p->key == key) {
`
269
``
`-
*q = p->next;
`
270
``
`-
PyMem_RawFree((void *)p);
`
271
``
`-
/* NB This does not free p->value! */
`
272
``
`-
}
`
273
``
`-
else
`
274
``
`-
q = &p->next;
`
275
``
`-
}
`
276
``
`-
PyThread_release_lock(keymutex);
`
277
``
`-
}
`
278
``
-
279
``
`-
int
`
280
``
`-
PyThread_set_key_value(int key, void *value)
`
281
``
`-
{
`
282
``
`-
struct key *p;
`
283
``
-
284
``
`-
p = find_key(1, key, value);
`
285
``
`-
if (p == NULL)
`
286
``
`-
return -1;
`
287
``
`-
else
`
288
``
`-
return 0;
`
289
``
`-
}
`
290
``
-
291
``
`-
/* Retrieve the value associated with key in the current thread, or NULL
`
292
``
`-
- if the current thread doesn't have an association for key.
`
293
``
`-
*/
`
294
``
`-
void *
`
295
``
`-
PyThread_get_key_value(int key)
`
296
``
`-
{
`
297
``
`-
struct key *p = find_key(0, key, NULL);
`
298
``
-
299
``
`-
if (p == NULL)
`
300
``
`-
return NULL;
`
301
``
`-
else
`
302
``
`-
return p->value;
`
303
``
`-
}
`
304
``
-
305
``
`-
/* Forget the current thread's association for key, if any. */
`
306
``
`-
void
`
307
``
`-
PyThread_delete_key_value(int key)
`
308
``
`-
{
`
309
``
`-
unsigned long id = PyThread_get_thread_ident();
`
310
``
`-
struct key *p, **q;
`
311
``
-
312
``
`-
PyThread_acquire_lock(keymutex, 1);
`
313
``
`-
q = &keyhead;
`
314
``
`-
while ((p = *q) != NULL) {
`
315
``
`-
if (p->key == key && p->id == id) {
`
316
``
`-
*q = p->next;
`
317
``
`-
PyMem_RawFree((void *)p);
`
318
``
`-
/* NB This does not free p->value! */
`
319
``
`-
break;
`
320
``
`-
}
`
321
``
`-
else
`
322
``
`-
q = &p->next;
`
323
``
`-
}
`
324
``
`-
PyThread_release_lock(keymutex);
`
325
``
`-
}
`
326
``
-
327
``
`-
/* Forget everything not associated with the current thread id.
`
328
``
`-
- This function is called from PyOS_AfterFork_Child(). It is necessary
`
329
``
`-
- because other thread ids which were in use at the time of the fork
`
330
``
`-
- may be reused for new threads created in the forked process.
`
331
``
`-
*/
`
332
``
`-
void
`
333
``
`-
PyThread_ReInitTLS(void)
`
334
``
`-
{
`
335
``
`-
unsigned long id = PyThread_get_thread_ident();
`
336
``
`-
struct key *p, **q;
`
337
``
-
338
``
`-
if (!keymutex)
`
339
``
`-
return;
`
340
``
-
341
``
`-
/* As with interpreter_lock in PyEval_ReInitThreads()
`
342
``
`-
we just create a new lock without freeing the old one */
`
343
``
`-
keymutex = PyThread_allocate_lock();
`
344
``
-
345
``
`-
/* Delete all keys which do not match the current thread id */
`
346
``
`-
q = &keyhead;
`
347
``
`-
while ((p = *q) != NULL) {
`
348
``
`-
if (p->id != id) {
`
349
``
`-
*q = p->next;
`
350
``
`-
PyMem_RawFree((void *)p);
`
351
``
`-
/* NB This does not free p->value! */
`
352
``
`-
}
`
353
``
`-
else
`
354
``
`-
q = &p->next;
`
355
``
`-
}
`
356
``
`-
}
`
357
``
-
358
``
`-
#endif /* Py_HAVE_NATIVE_TLS */
`
359
154
``
360
155
`PyDoc_STRVAR(threadinfo__doc__,
`
361
156
`"sys.thread_info\n\
`